Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 56 additions & 13 deletions .github/workflows/gitleaks-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ on:
workflow_call:

env:
# Pinned gitleaks release. Bump both together: GITLEAKS_SHA256 is the sha256
# of gitleaks_<version>_linux_x64.tar.gz, checked against the release's
# checksums file when bumping.
GITLEAKS_VERSION: "8.30.1"
GITLEAKS_SHA256: "551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb"
# Same rule as the centreon-ubuntu-26.04 runner images (centreon-images,
# runners/base-ubuntu-26.04/Dockerfile): "latest" = newest stable release
# published at least 4 days before the run. Set a version (e.g. "8.30.1")
# to pin it.
GITLEAKS_VERSION: "latest"
# Installed when "latest" cannot be resolved, so the scan still runs.
GITLEAKS_FALLBACK_VERSION: "8.30.1"

jobs:
secret-scan:
Expand Down Expand Up @@ -43,20 +45,61 @@ jobs:

- name: Install Gitleaks
run: |
ASSET="gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
BASE_URL="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}"
set -o pipefail

case "${RUNNER_ARCH}" in
X64) ARCH=x64 ;;
ARM64) ARCH=arm64 ;;
*) echo "::error::Unsupported runner architecture '${RUNNER_ARCH}'."; exit 1 ;;
esac

VERSION="${GITLEAKS_VERSION}"
if [ "${VERSION}" = "latest" ]; then
# Read the public releases feed: no token, no API rate limit. Its
# <updated> date is at or after the publication date, so the
# 4-day delay is never shortened. Drafts are not in the feed, and
# only vX.Y.Z tags are kept, which leaves pre-releases out.
cutoff="$(date -u -d '-4 days' +%Y-%m-%dT%H:%M:%SZ)"
VERSION="$(curl -fsSL --retry 3 "https://github.com/gitleaks/gitleaks/releases.atom" \
| python3 -I -c '
import re, sys, xml.etree.ElementTree as ET
ns = {"a": "http://www.w3.org/2005/Atom"}
for entry in ET.parse(sys.stdin).getroot().findall("a:entry", ns):
tag = entry.findtext("a:id", "", ns).rsplit("/", 1)[-1]
if re.fullmatch(r"v[0-9]+\.[0-9]+\.[0-9]+", tag) and entry.findtext("a:updated", "", ns) <= sys.argv[1]:
print(tag[1:])
' "${cutoff}" \
| sort -V | tail -1)" || VERSION=""
if [ -z "${VERSION}" ]; then
# Do not block the run: fall back to a known release, still
# verified against its checksums file below.
VERSION="${GITLEAKS_FALLBACK_VERSION}"
echo "::warning::gitleaks-version-fallback: could not resolve the latest gitleaks release from its releases feed; using ${VERSION}."
fi
fi

# Some runners (e.g. centreon-ubuntu-26.04-small) ship gitleaks, built
# with the same rule: keep it when it is the expected version,
# otherwise (missing, or any other version) install that release.
CURRENT="$(gitleaks version 2>/dev/null || true)"
if [ "${CURRENT#v}" = "${VERSION}" ]; then
echo "gitleaks ${VERSION} is already installed: $(command -v gitleaks)."
exit 0
fi
echo "Found gitleaks version '${CURRENT:-none}', expected ${VERSION}: installing it."

TARBALL="gitleaks_${VERSION}_linux_${ARCH}.tar.gz"
BASE_URL="https://github.com/gitleaks/gitleaks/releases/download/v${VERSION}"

workdir="$(mktemp -d)"
cd "$workdir"

curl -sSfL -o "${ASSET}" "${BASE_URL}/${ASSET}"

# Verify against the pinned digest, not a checksums file served
# from the same release as the binary.
echo "${GITLEAKS_SHA256} ${ASSET}" | sha256sum -c -
curl -fsSL --retry 3 -o "${TARBALL}" "${BASE_URL}/${TARBALL}"
curl -fsSL --retry 3 "${BASE_URL}/gitleaks_${VERSION}_checksums.txt" \
| grep " ${TARBALL}\$" | sha256sum -c -

mkdir -p "$HOME/.local/bin"
tar -xzf "${ASSET}" -C "$HOME/.local/bin" gitleaks
tar -xzf "${TARBALL}" -C "$HOME/.local/bin" gitleaks
echo "$HOME/.local/bin" >> "$GITHUB_PATH"

"$HOME/.local/bin/gitleaks" version
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ Because consumers pin to `@main`, changes to workflows and the blocklist take ef
- `dependency-analysis.yml` — `workflow_call` + runs on PR. Two responsibilities:
1. **Lockfile compliance** — enforces PNPM only (rejects `yarn.lock` / `package-lock.json`), requires a lockfile, and enforces a minimum `lockfileVersion` (`8.9.9`).
2. **Blocklist scan** — downloads `compromised-packages.txt` from `main` and greps every `pnpm-lock.yaml` for `name@version` matches.
- `gitleaks-analysis.yml` — `workflow_call`, supporting `pull_request` and `workflow_dispatch` only (org repos call it on both; `workflow_dispatch` is mostly for debugging the pipeline). Installs the gitleaks release pinned by `GITLEAKS_VERSION` + `GITLEAKS_SHA256` (bump both together; Dependabot/Renovate don't). On a PR it scans `base..head`: every non-merge commit not already on the destination branch (whatever it is called, and whether or not the commits are on other branches), so it never fails on secrets already on the base and fails on a secret in one of the PR's commits. On `workflow_dispatch` it scans the ref's whole history. Pull requests are checked out shallow (`fetch-depth` = PR commits + 2): the scan deepens the base side until the range holds no parentless commit (a shallow boundary would be read as a root and its whole tree scanned). Event SHAs missing from the clone are re-fetched with the job token. Fails closed: leaks, gitleaks errors, unresolvable ranges and unsupported events (push, schedule, …) all fail the job. `pull_request_target` always fails (not compliant with the security posture), from a first step that runs before checkout.
- `gitleaks-analysis.yml` — `workflow_call`, supporting `pull_request` and `workflow_dispatch` only (org repos call it on both; `workflow_dispatch` is mostly for debugging the pipeline). Resolves the gitleaks version like the `centreon-ubuntu-26.04` runner images (`centreon-images`, `runners/base-ubuntu-26.04/Dockerfile`): `GITLEAKS_VERSION: latest` is the newest stable release published at least 4 days before the run (read from the public releases feed, `releases.atom`, without any token), falling back to `GITLEAKS_FALLBACK_VERSION` with a `gitleaks-version-fallback` warning when the feed cannot be resolved; set a version to pin it. Keeps the runner's own gitleaks when `gitleaks version` matches (e.g. on `centreon-ubuntu-26.04-small`), and otherwise installs that release for the runner's architecture, verified against the release's checksums file. On a PR it scans `base..head`: every non-merge commit not already on the destination branch (whatever it is called, and whether or not the commits are on other branches), so it never fails on secrets already on the base and fails on a secret in one of the PR's commits. On `workflow_dispatch` it scans the ref's whole history. Pull requests are checked out shallow (`fetch-depth` = PR commits + 2): the scan deepens the base side until the range holds no parentless commit (a shallow boundary would be read as a root and its whole tree scanned). Event SHAs missing from the clone are re-fetched with the job token. Fails closed: leaks, gitleaks errors, unresolvable ranges and unsupported events (push, schedule, …) all fail the job. `pull_request_target` always fails (not compliant with the security posture), from a first step that runs before checkout.

**Warn-then-enforce gating** (in `dependency-analysis.yml`): findings are reported as `[WARNING]` until a deadline, after which they become `[ERROR]` and fail the build. Controlled by two repository-level GitHub variables:
- `OVERRIDE_DEPENDENCY_ENFORCEMENT_DATE` — the date enforcement begins.
- `OVERRIDE_DEPENDENCY_SCAN` — set to `"true"` to bypass the scan entirely.

Findings accumulate in `error_log.txt`, which is posted back to the PR as a sticky comment via `marocchino/sticky-pull-request-comment`. The job only fails if `fail_the_build=true` (set when enforcement is active and findings exist).

**Self-hosted runner selection**: jobs use a conditional `runs-on` that picks a per-org self-hosted runner only for **private** repos — `centreon-security` for the `centreon` org, `quanta-security` for the `quanta-computing` org — and falls back to `ubuntu-24.04` for public repos or any other owner. The same expression is used in both `dependency-analysis.yml` and `gitleaks-analysis.yml`; keep them in sync when editing.
**Self-hosted runner selection**: jobs use a conditional `runs-on` that picks a per-org self-hosted runner only for **private** repos — for the `centreon` org, `centreon-ubuntu-26.04-small` in `gitleaks-analysis.yml` and `centreon-ubuntu-26.04-slim` in `dependency-analysis.yml`; `quanta-security` for the `quanta-computing` org — and falls back to `ubuntu-26.04` for public repos or any other owner. Apart from the `centreon` runner, the expression is the same in both workflows; keep them in sync when editing.

### The blocklist (`blacklist/compromised-packages.txt`)

Expand Down