From 5f73a7d69e101c51bb115f5c275e4ff0bc7f7f49 Mon Sep 17 00:00:00 2001 From: "v.oleynikov" Date: Tue, 28 Jul 2026 08:39:23 +0300 Subject: [PATCH] fix(gitleaks): compose the centralized and module configs instead of choosing one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- gitleaks/action.yml | 49 +++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/gitleaks/action.yml b/gitleaks/action.yml index 71a79ca..90ab226 100644 --- a/gitleaks/action.yml +++ b/gitleaks/action.yml @@ -71,28 +71,47 @@ runs: exit 1 fi - - name: Check for optional config + - name: Compose the scan config id: config shell: bash run: | set -euo pipefail + # A module's .gitleaks.toml must extend the centralized config, not replace it. + # Gitleaks chains configs through [extend] path, so drop the module's own [extend] + # section, point a fresh one at the config copied above, and keep the rest of the + # module config verbatim. Resulting chain: + # gitleaks defaults <- centralized base config <- module config + # The module config stays standalone-valid, so `gitleaks detect` still works locally. + BASE_CONFIG="$(pwd)/gitleaks.base.toml" + MERGED_CONFIG="${RUNNER_TEMP}/gitleaks.merged.toml" + if [[ -f ".gitleaks.toml" ]]; then - # Local config exists - check if it has [extend] section - if grep -q "^\[extend\]" .gitleaks.toml; then - # Has extend section - use as is - echo "config_arg=--config .gitleaks.toml" >> "$GITHUB_OUTPUT" - echo "✅ Found local config with [extend] section - using as is" - else - # No extend section - warn and ignore, use base config only - echo "⚠️ WARNING: Local config file .gitleaks.toml exists but does not contain [extend] section" - echo " We cannot be sure this is the expected extend configuration." - echo " Ignoring local config file and using base config only." - echo "config_arg=--config gitleaks.base.toml" >> "$GITHUB_OUTPUT" + MODULE_EXTEND_PATH="$(awk ' + /^[[:space:]]*\[extend\]/ { in_extend = 1; next } + /^[[:space:]]*\[/ { in_extend = 0 } + in_extend && /^[[:space:]]*path[[:space:]]*=/ { print } + ' .gitleaks.toml)" + if [[ -n "$MODULE_EXTEND_PATH" ]]; then + echo "⚠️ WARNING: .gitleaks.toml declares its own [extend] path:" + echo " $MODULE_EXTEND_PATH" + echo " It is replaced by the centralized config." fi + + { + printf '[extend]\nuseDefault = false\npath = "%s"\n\n' "$BASE_CONFIG" + awk ' + /^[[:space:]]*\[extend\]/ { skip = 1; next } + /^[[:space:]]*\[/ { skip = 0 } + !skip + ' .gitleaks.toml + } > "$MERGED_CONFIG" + + echo "config_arg=--config $MERGED_CONFIG" >> "$GITHUB_OUTPUT" + echo "✅ Composed centralized config + module .gitleaks.toml into $MERGED_CONFIG" else - # Use centralized config only + # No module config - use centralized config only echo "config_arg=--config gitleaks.base.toml" >> "$GITHUB_OUTPUT" - echo "🔹 Using centralized config only (no local customization)" + echo "🔹 Using centralized config only (no module .gitleaks.toml)" fi - name: Gitleaks scan (full) @@ -200,4 +219,4 @@ runs: shell: bash if: always() run: | - rm gitleaks.base.toml + rm -f gitleaks.base.toml "${RUNNER_TEMP}/gitleaks.merged.toml"