Skip to content

fix(gitleaks): compose the centralized and module configs instead of choosing one - #83

Closed
duckhawk wants to merge 1 commit into
mainfrom
fix/gitleaks-compose-base-and-module-config
Closed

fix(gitleaks): compose the centralized and module configs instead of choosing one#83
duckhawk wants to merge 1 commit into
mainfrom
fix/gitleaks-compose-base-and-module-config

Conversation

@duckhawk

@duckhawk duckhawk commented Jul 28, 2026

Copy link
Copy Markdown
Member

Closing — the CI does not need changing after all.

The choose-one logic here is deliberate: the base config must stay ours, and a module that wants additions is expected to declare [extend] useDefault = false + path = <base config> and extend it rather than override it. That is exactly what the header of gitleaks.base.toml documents. My diagnosis attributed the outcome to the CI; the outcome is real, but the cause is that all 17 storage modules wrote [extend] useDefault = true with no path, i.e. they extend gitleaks' own defaults and never the centralized config. The modules are misconfigured, not the action.

Fixing them module-side gives the same result with no CI change. Verified on a repo holding a Vault token, a .werf_secret_key, an h1: checksum and a docs placeholder:

Module config Rules that fired
useDefault = true (today) generic-api-key only
useDefault = false + path hashicorp-vault-token, werf-secret-key

In the second case generic-api-key also disappears, because the base allowlist suppresses the checksum — so the whole chain is live.

The differing base-config locations turned out not to be a blocker either. The GitHub and GitLab module sets are disjoint (no storage module has both .github/workflows and .gitlab-ci.yml), so each module only ever needs the path its own CI uses: gitleaks.base.toml here, /tmp/gitleaks.base.toml in modules-gitlab-ci. My claim that no portable value exists was wrong.

The v17 branch cut for this change is deleted.

Two things worth fixing separately

  1. grep -q "^\[extend\]" cannot enforce the invariant it was written for. It passes for [extend] useDefault = true, which extends gitleaks' defaults, not the centralized config — so it green-lights precisely the misconfiguration it was meant to catch. That is how 17 modules stayed silently unprotected. Worth replacing with a check that the declared path actually resolves to the base config, failing loudly otherwise.
  2. The documented path does not match the code. gitleaks/config/gitleaks.base.toml tells repositories to use path = "/home/runner/work/_temp/gitleaks.base.toml", while the action has copied the file to ./gitleaks.base.toml since Fix relative path for gitleaks config override #80. A module following the documentation verbatim gets FTL failed to load extended config.

Also worth noting: all 10 GitHub storage modules pin gitleaks@v6, which predates the centralized config entirely — no gitleaks/config/ directory, and it greps for an undotted gitleaks.toml that no module ships. They are being moved to @v16 separately.

…choosing one

The action picked one of the two configs and discarded the other. A module shipping
.gitleaks.toml with an [extend] section got its config used verbatim, so the
centralized gitleaks.base.toml was thrown away — and with it not only the shared
allowlists but the three rules that exist nowhere else: werf-secret-key,
hashicorp-vault-token and openbao-token. All 17 storage modules ship
[extend] useDefault = true, so none of them were scanned for those secrets at all.
Verified against a repo holding a live-format Vault token and a 32-hex
.werf_secret_key: the base config reports both, a module-style config reports
neither.

Compose the two instead. Gitleaks already chains configs through [extend] path, so
drop the module's own [extend] section, point a fresh one at the base config copied
above, and keep the rest of the module config verbatim. The chain becomes

  gitleaks defaults <- centralized base config <- module config

and all three levels stay active at once: a module's [[rules]] still fire, the base
rules fire again, and the base allowlists apply.

Doing the composition here rather than in the modules means no module has to
change: their .gitleaks.toml stays standalone-valid, so a local gitleaks run keeps
working. Pointing each module at the base config instead would need a hardcoded
path, and the two CI systems place that file differently ($RUNNER_TEMP here, /tmp
in modules-gitlab-ci).

The base config is referenced by absolute path, since the merged config is written
to $RUNNER_TEMP while the base config sits in the checkout.

Two behaviour changes fall out. A module config without an [extend] section is now
honoured instead of being discarded with a warning, and a module that declares its
own [extend] path gets a warning that the centralized config replaces it.

Dry-run across all 17 storage modules from common/modules_list.txt at full scan
scope: 0 findings everywhere, so re-enabling the base rules turns nobody red.

Paired with the same change in modules-gitlab-ci, so GitHub and GitLab do not
diverge.

Signed-off-by: v.oleynikov <vasily.oleynikov@flant.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant