Skip to content

feat(iit,#8236): ICT-43 Phase 0 — inventaire SAE multi-échelle mesuré au Hub #33047

feat(iit,#8236): ICT-43 Phase 0 — inventaire SAE multi-échelle mesuré au Hub

feat(iit,#8236): ICT-43 Phase 0 — inventaire SAE multi-échelle mesuré au Hub #33047

Workflow file for this run

name: Secret Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write
# Pin the gitleaks BINARY version, and pin it ON OUR SIDE — not on a third party's.
# Two surfaces must agree:
# 1. pre-commit hook -> .pre-commit-config.yaml `rev: vX.Y.Z`
# 2. CI workflow (this) -> `$GITLEAKS_VERSION` env + explicit `docker pull`/`docker run`
# A version drift between the two surfaces is exactly the defect that #10139
# surfaced: hook (8.21.2) and CI (8.24.3) yielded different verdicts on the same
# tree. Measured on this repo, same files and same config, three binaries:
# 8.21.2 -> 0 findings | 8.24.3 -> 2 | 8.30.1 -> 2
# See #10139.
#
# IMPORTANT: GitHub Actions does NOT expand env vars in `uses: docker://...:${X}`
# lines at any scope (not workflow-level, not job-level). The docker image MUST
# be pulled via a `run:` step where shell interpolation DOES expand env vars.
# That is why this workflow uses an explicit `docker pull` script and not a
# `uses: docker://...:${X}` line — verified: 2 CI cycles failed with
# `invalid reference format` before the swap to `run:`. See c.10139-L1.
#
# Why not the `gitleaks/gitleaks-action@v2` wrapper: the wrapper tag `@v2` is
# the GitHub Action version, not the underlying gitleaks binary version. The
# binary is selected by `GITLEAKS_VERSION` from the action's environment, with
# a hard-coded fallback (`"8.24.3"`, src/index.js) — an implicit pin owned by a
# third party and revisable by any `@v2` release without a commit here. This
# workflow removes the third-party pin entirely by invoking the docker image
# directly. Drift detector below asserts parity between hook and CI at runtime.
concurrency:
group: secret-scan-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
# Jambe auto-hebergee (#14283) : le trafic same-repo -- 32 runs le
# 2026-09-02, premier consommateur GitHub-hosted du depot -- bascule sur le
# pool Linux. Le pool n'a PAS de docker (ni binaire dans l'image, ni socket
# montee : scripts/ci/docker/linux-runner/Dockerfile), donc le `docker run`
# de l'ancienne forme y echouerait. La forme retenue est celle que le job
# positive-controls ci-dessous fait deja tourner sur CE MEME pool depuis la
# tranche 3c : binaire recupere depuis la release GitHub, epingle par
# ${GITLEAKS_VERSION}. Le pin ne change pas de proprietaire -- meme version,
# meme release, meme assertion de parite avec .pre-commit-config.yaml.
# Garde anti-fork au niveau job + runs-on STATIQUE (une expression leverait
# DYNAMIC_RUNS_ON dans check_self_hosted_runner_policy.py).
# Retour arriere = remettre `runs-on: ubuntu-latest` + la forme docker.
gitleaks:
name: Gitleaks secret scanner
runs-on: [self-hosted, coursia-ephemeral, coursia-linux]
if: github.event.pull_request.head.repo.full_name == null || github.event.pull_request.head.repo.full_name == github.repository
env:
GITLEAKS_VERSION: 8.24.3
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch gitleaks binary (pinned)
run: |
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" -o /tmp/gitleaks.tgz
mkdir -p /tmp/gitleaks-bin
tar -xzf /tmp/gitleaks.tgz -C /tmp/gitleaks-bin
echo "GITLEAKS_BIN=/tmp/gitleaks-bin/gitleaks" >> "$GITHUB_ENV"
- name: Log gitleaks binary version (CI must equal pre-commit rev)
# Ce pas est AUSSI le controle positif de la jambe auto-hebergee : un
# binaire absent ou non executable fait echouer `$GITLEAKS_BIN version`
# ICI, avant le scan. Sans lui, un outil manquant rendrait `0 finding`
# -- indiscernable d'un corpus propre (cf #14294).
run: |
actual="$("$GITLEAKS_BIN" version)"
echo "::notice title=Gitleaks binary version::$actual"
case "$actual" in *"$GITLEAKS_VERSION"*) ;; *) echo "::error title=Gitleaks version drift::binaire '$actual' != pin ${GITLEAKS_VERSION}"; exit 1;; esac
expected="$(grep -oE 'rev: v[0-9]+\.[0-9]+\.[0-9]+' .pre-commit-config.yaml | head -n1 | cut -d' ' -f2 | tr -d 'v')"
if [ "${GITLEAKS_VERSION}" != "${expected}" ]; then
echo "::error title=Gitleaks version drift::CI pins ${GITLEAKS_VERSION} but .pre-commit-config.yaml pins v${expected}. Update both to the same value."
exit 1
fi
- name: Gitleaks detect
run: |
"$GITLEAKS_BIN" detect \
--source . \
--config .gitleaks.toml \
--verbose \
--no-git \
--redact \
--exit-code 1
# Jambe fork : les PRs des ~95 forks etudiants ne touchent JAMAIS un runner
# auto-heberge. Elles gardent donc leur scan sur GitHub-hosted, sous la forme
# docker d'origine (l'image y est disponible). Sans cette jambe, la garde
# anti-fork ci-dessus ferait simplement DISPARAITRE le scan de secrets la ou
# il sert le plus. Nom de check distinct : `Secret Scan` n'est pas un check
# requis -- seul `PR gate` l'est, mesure le 2026-09-02 par
# `gh pr checks <N> --required` -- donc ce dedoublement ne deverrouille ni ne
# bloque rien.
gitleaks-fork:
name: Gitleaks secret scanner (fork)
runs-on: ubuntu-latest
if: github.event.pull_request.head.repo.full_name != null && github.event.pull_request.head.repo.full_name != github.repository
env:
GITLEAKS_VERSION: 8.24.3
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Log gitleaks binary version (CI must equal pre-commit rev)
run: |
echo "::notice title=Gitleaks binary version::$(docker run --rm zricethezav/gitleaks:v${GITLEAKS_VERSION} version)"
expected="$(grep -oE 'rev: v[0-9]+\.[0-9]+\.[0-9]+' .pre-commit-config.yaml | head -n1 | cut -d' ' -f2 | tr -d 'v')"
if [ "${GITLEAKS_VERSION}" != "${expected}" ]; then
echo "::error title=Gitleaks version drift::CI pins ${GITLEAKS_VERSION} but .pre-commit-config.yaml pins v${expected}. Update both to the same value."
exit 1
fi
- name: Gitleaks (pinned docker image, pulled via script)
run: |
docker pull "zricethezav/gitleaks:v${GITLEAKS_VERSION}"
docker run --rm \
-v "${{ github.workspace }}:/src" \
-w /src \
"zricethezav/gitleaks:v${GITLEAKS_VERSION}" \
detect \
--source . \
--config .gitleaks.toml \
--verbose \
--no-git \
--redact \
--exit-code 1
# Positive controls for the allowlist regexes (.gitleaks.toml lines ~108-128).
# The scanner job above proves the corpus is clean; this job proves the
# allowlist did not disarm the detectors (#9888 failure mode). The test
# modules resolve GITLEAKS_BIN in this order: env > PATH > local scratchpad --
# setting GITLEAKS_BIN here turns off the `requires_gitleaks` skip so the
# tests actually run in CI (without this job they skip forever and the `0` is
# true but unguarded, cf c.10143 comment 5255089235). See #10143.
positive-controls:
name: Gitleaks positive controls (#10143)
# Routage #14283 tranche 3c (ai-01 2026-09-02) : jambe Linux auto-hebergee.
# Garde anti-fork au niveau job + runs-on STATIQUE (une expression leverait
# DYNAMIC_RUNS_ON dans check_self_hosted_runner_policy.py).
# Retour arriere = remettre `runs-on: ubuntu-latest` + retrait de l'allowlist.
runs-on: [self-hosted, coursia-ephemeral, coursia-linux]
if: github.event.pull_request.head.repo.full_name == null || github.event.pull_request.head.repo.full_name == github.repository
env:
GITLEAKS_VERSION: 8.24.3
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.13'
- name: Install pytest
run: pip install pytest
- name: Fetch gitleaks binary (pinned to the same version as the scanner job)
run: |
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" -o /tmp/gitleaks.tgz
mkdir -p /tmp/gitleaks-bin
tar -xzf /tmp/gitleaks.tgz -C /tmp/gitleaks-bin
echo "GITLEAKS_BIN=/tmp/gitleaks-bin/gitleaks" >> "$GITHUB_ENV"
- name: Assert gitleaks version matches the CI pin
# Same drift guard as the scanner job: the test binary MUST equal the
# scanner binary, or a suppression can pass here and fail there.
run: |
actual="$("$GITLEAKS_BIN" version)"
echo "::notice title=Test gitleaks binary version::$actual"
case "$actual" in *"$GITLEAKS_VERSION"*) ;; *) echo "::error title=Gitleaks version drift::test binary '$actual' != pin ${GITLEAKS_VERSION}"; exit 1;; esac
- name: Run gitleaks positive-control tests
# Only the two gitleaks test modules (the other scripts/secrets/tests/
# suites require .secrets/master.env, which is gitignored and absent
# from a fresh checkout -- running them here would be env-dependent noise).
run: pytest scripts/secrets/tests/test_gitleaks_qwen_rule.py scripts/secrets/tests/test_gitleaks_10143_classes.py -v