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
30 changes: 26 additions & 4 deletions actions/leak-scan/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -207,18 +207,40 @@ run_pr_diff_scan() {

printf '%s\n' "$changed_files" > changed-files.txt

# Build a temporary gitleaks config that extends the default rules and adds
# allowlist.paths entries for any EXCLUDE_PATHS prefixes. gitleaks 8.x does
# not support path filtering flags, so path exclusion must be expressed via the
# config allowlist. The temp config is cleaned up after the scan.
local gitleaks_config_flag=()
local gitleaks_tmp_config=""
local regex_prefix
if [[ -n "${EXCLUDE_PATHS:-}" ]]; then
gitleaks_tmp_config=$(mktemp --suffix=.toml)
{
printf '[extend]\nuseDefault = true\n\n[allowlist]\npaths = [\n'
for excl_prefix in $EXCLUDE_PATHS; do
# Escape the prefix as a regex: anchor with ^ and escape dots/special chars.
regex_prefix=$(printf '%s' "$excl_prefix" | sed 's/\./\\./g')
printf " '^%s',\n" "$regex_prefix"
done
printf ']\n'
} > "$gitleaks_tmp_config"
gitleaks_config_flag=(--config="$gitleaks_tmp_config")
fi

local gitleaks_exit=0
if command -v gitleaks >/dev/null 2>&1; then
gitleaks detect \
--source=. \
--files-at-commit="$HEAD_REF" \
--include-paths=changed-files.txt \
gitleaks git \
--log-opts="${BASE_REF}..${HEAD_REF}" \
"${gitleaks_config_flag[@]}" \
--redact \
--report-path=gitleaks-diff-report.json \
. \
2>&1 || gitleaks_exit=$?
else
printf '::warning::gitleaks not found; skipping gitleaks pr-diff scan\n'
fi
[[ -n "$gitleaks_tmp_config" ]] && rm -f "$gitleaks_tmp_config"

# Run path deny-list on changed files
PATHS="$(printf '%s\n' "$changed_files" | tr '\n' ' ')"
Expand Down
60 changes: 60 additions & 0 deletions tests/test_deploy_platform_d.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,36 @@ def test_all_refs_mode_line_in_script(self) -> None:
"run.sh must not pass --all as a standalone flag to gitleaks detect (gitleaks v8.x removed it)",
)

def test_pr_diff_mode_uses_gitleaks_git_with_log_opts(self) -> None:
"""T-D2: pr-diff mode uses 'gitleaks git --log-opts' (not --files-at-commit or --include-paths).

gitleaks 8.x removed --files-at-commit and --include-paths from the detect
subcommand. The correct replacement is 'gitleaks git --log-opts=BASE..HEAD'
which scans the git commit range that corresponds to the PR diff.
"""
script_text = LEAK_SCAN_RUN.read_text(encoding="utf-8")
self.assertIn(
"gitleaks git",
script_text,
"run.sh pr-diff block must use 'gitleaks git' (not 'gitleaks detect' with removed flags)",
)
self.assertIn(
'--log-opts="${BASE_REF}..${HEAD_REF}"',
script_text,
"run.sh pr-diff block must pass --log-opts=BASE..HEAD to gitleaks git",
)
# Regression guards: these flags were removed in gitleaks 8.x
self.assertNotIn(
"--files-at-commit",
script_text,
"run.sh must not use --files-at-commit (removed in gitleaks 8.x; causes 'unknown flag' exit 1)",
)
self.assertNotIn(
"--include-paths",
script_text,
"run.sh must not use --include-paths (removed in gitleaks 8.x; causes 'unknown flag' exit 1)",
)

def test_path_mode_detects_ipv4_literal(self) -> None:
"""T-D2: path mode detects IPv4 literal and emits redacted gate-summary."""
with tempfile.TemporaryDirectory() as tmpdir:
Expand Down Expand Up @@ -789,6 +819,36 @@ def test_pr_diff_keeps_graceful_warning_when_gitleaks_absent(self) -> None:
"run.sh pr-diff mode must keep the graceful gitleaks-missing warning (not fail-closed)",
)

# T-D7g: pr-diff mode must not contain removed gitleaks v8.x flags
def test_pr_diff_does_not_use_removed_gitleaks_flags(self) -> None:
"""T-D7g: --files-at-commit and --include-paths were removed in gitleaks 8.x.

These flags cause 'gitleaks detect' to print help and exit 1, breaking
every pipeline run. This test is a static drift guard that catches any
re-introduction of the removed flags.
"""
self.assertNotIn(
"--files-at-commit",
self.run_sh_text,
"run.sh must not contain --files-at-commit (removed in gitleaks 8.x; breaks CI)",
)
self.assertNotIn(
"--include-paths",
self.run_sh_text,
"run.sh must not contain --include-paths (removed in gitleaks 8.x; breaks CI)",
)
# Positive guard: pr-diff must use the correct v8 invocation
self.assertIn(
"gitleaks git",
self.run_sh_text,
"run.sh pr-diff block must use 'gitleaks git' subcommand (correct for gitleaks 8.x)",
)
self.assertIn(
"--log-opts=",
self.run_sh_text,
"run.sh pr-diff block must use --log-opts to specify commit range (gitleaks 8.x API)",
)


if __name__ == "__main__":
unittest.main()