Repository navigation
feat(iit,#8236): ICT-43 Phase 0 — inventaire SAE multi-échelle mesuré au Hub #33047
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |