Skip to content
Closed
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
15 changes: 15 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ useDefault = true

[allowlist]
description = "Paths excluded from secret scanning"
# Match against the whole source line rather than the extracted secret, so the
# regexes below can require the surrounding package-URL context.
regexTarget = "line"
regexes = [
Comment on lines +11 to +12
# Dependency lockfiles record every artifact as a PyPI download URL whose path
# embeds the artifact's own content hash, e.g.
# https://files.pythonhosted.org/packages/30/4b/90c9378...a7/parso-0.8.7.tar.gz
# Those hex segments are public integrity digests, but their length and
# entropy collide with generic provider-token rules (parso 0.8.7 currently
# trips `square-access-token`). Anchoring on the public PyPI CDN host keeps
# the rest of the lockfile scanned: a private index URL carrying real
# credentials (https://user:token@pypi.internal/...) does not match this and
# is still reported.
'''https://files\.pythonhosted\.org/packages/''',
]
paths = [
# Vendored saved web pages from Google (AI Studio / APIs Explorer): contain
# Google's own public page keys, not EventRelay credentials.
Expand Down
112 changes: 112 additions & 0 deletions tests/unit/test_secret_scan_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"""Guards for the gitleaks allowlist used by the ``gitleaks (working tree)`` job.

``uv.lock`` records every artifact as a PyPI download URL whose path embeds the
artifact's own content hash. One of those hex segments has enough length and
entropy to trip the default ``square-access-token`` rule, so the secret scan
failed on every pull request regardless of its diff.

The suppression has to stay narrow. Excluding the lockfile wholesale would also
hide a private index URL carrying real credentials, which is exactly the kind of
secret this job exists to catch. These checks pin the shape of the fix:

* the lockfile itself is still scanned,
* the suppression is anchored to the public PyPI CDN host, and
* the workflow actually loads this configuration.
"""

import tomllib
import unittest
from pathlib import Path


def _repo_root():
for candidate in Path(__file__).resolve().parents:
if (candidate / ".gitleaks.toml").exists():
return candidate
raise AssertionError("repository root not found")


REPO_ROOT = _repo_root()
CONFIG_PATH = REPO_ROOT / ".gitleaks.toml"
WORKFLOW_PATH = REPO_ROOT / ".github" / "workflows" / "secret-scan.yml"

# Lockfiles are the files the false positive lives in. They must never be
# allowlisted by path, because a private index URL embeds its credentials
# inline and would then go unreported.
SCANNED_LOCKFILES = ("uv.lock", "package-lock.json", "poetry.lock")


def _config():
with CONFIG_PATH.open("rb") as handle:
return tomllib.load(handle)


class SecretScanConfigTests(unittest.TestCase):
def setUp(self):
self.config = _config()
self.allowlist = self.config.get("allowlist", {})

def test_config_extends_the_default_ruleset(self):
"""Dropping ``useDefault`` would silently disable every built-in rule."""
self.assertTrue(
self.config.get("extend", {}).get("useDefault"),
".gitleaks.toml must extend the default gitleaks ruleset",
)

def test_lockfiles_are_not_excluded_by_path(self):
"""A path exclusion would hide real credentials in the same file."""
paths = self.allowlist.get("paths", [])
for lockfile in SCANNED_LOCKFILES:
for pattern in paths:
self.assertNotIn(
lockfile,
pattern,
Comment on lines +59 to +63
f"{lockfile} must stay scanned; found path allowlist "
f"{pattern!r}. Narrow the suppression to the benign "
f"pattern instead of excluding the file.",
)

def test_pypi_suppression_is_anchored_to_the_public_cdn_host(self):
"""The regex must require the CDN host, not just a hash-shaped string."""
regexes = self.allowlist.get("regexes", [])
self.assertTrue(
any("files" in r and "pythonhosted" in r for r in regexes),
"expected an allowlist regex anchored on files.pythonhosted.org; "
f"got {regexes!r}",
Comment on lines +71 to +75
)
for regex in regexes:
if "pythonhosted" not in regex:
continue
self.assertIn(
"/packages/",
regex,
"the suppression must require the /packages/ URL prefix so it "
"cannot match arbitrary text mentioning the host",
)

def test_regex_allowlist_matches_whole_lines(self):
"""``regexes`` compare against the extracted secret unless retargeted.

The secret here is a bare hex path segment, so the host anchor only
works when the allowlist is evaluated against the full line.
"""
if not self.allowlist.get("regexes"):
self.skipTest("no regex allowlist configured")
self.assertEqual(
self.allowlist.get("regexTarget"),
"line",
"regexTarget must be 'line' for the host-anchored regex to apply",
)

def test_workflow_loads_this_configuration(self):
"""An allowlist the scan never reads is not a fix."""
workflow = WORKFLOW_PATH.read_text(encoding="utf-8")
self.assertIn(
"--config .gitleaks.toml",
workflow,
"secret-scan.yml must run gitleaks with --config .gitleaks.toml",
)


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