Skip to content

Commit 1f12da0

Browse files
CuberHuberclaude
andcommitted
fix: keep the secrets-baseline plugin path portable across machines
`detect-secrets scan` rewrites `plugins_used[*].path` to the absolute `file://` URI of the developer's machine on every run, so committing the baseline as written poisons it for every other contributor. Add a small normalizer (`tools/normalize_secrets_baseline.py`) that rewrites the `KpmPasswordDetector` entry to a stable repo-relative URI, chain it into `make scan-init` and `make scan-update`, drop the existing absolute path from the committed baseline, and document the behaviour in the runbook. The KPM-aware audit wrapper already pre-imports the plugin module, so detect-secrets never has to resolve the URI at runtime. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 37827da commit 1f12da0

4 files changed

Lines changed: 64 additions & 7 deletions

File tree

.secrets.baseline

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Makefile

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
# accepts `--plugin` natively.
77
# See docs/runbooks/secrets.md for the full procedure.
88

9-
BASELINE := .secrets.baseline
10-
PLUGIN := tools/detect_secrets_plugins/kpm_password.py
11-
WRAPPER := tools/detect_secrets_audit.py
9+
BASELINE := .secrets.baseline
10+
PLUGIN := tools/detect_secrets_plugins/kpm_password.py
11+
WRAPPER := tools/detect_secrets_audit.py
12+
NORMALIZE := uv run python tools/normalize_secrets_baseline.py $(BASELINE)
1213

1314
SCAN := uv run detect-secrets scan \
1415
--plugin $(PLUGIN) \
@@ -44,9 +45,11 @@ scan:
4445

4546
scan-update:
4647
$(SCAN) --baseline $(BASELINE)
48+
@$(NORMALIZE)
4749

4850
scan-init:
4951
$(SCAN) > $(BASELINE)
52+
@$(NORMALIZE)
5053

5154
hook:
5255
uv run pre-commit run detect-secrets --all-files

docs/runbooks/secrets.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ uv run detect-secrets scan \
4646
git add .secrets.baseline
4747
```
4848

49-
Shortcut: `make scan-init` (does the scan; staging stays manual).
49+
Shortcut: `make scan-init` (does the scan and normalizes the
50+
custom-plugin path in the baseline so it stays portable across
51+
machines; staging stays manual).
5052

5153
## Update
5254

@@ -63,7 +65,9 @@ uv run detect-secrets scan \
6365
git add .secrets.baseline
6466
```
6567

66-
Shortcut: `make scan-update` (does the merge; staging stays manual).
68+
Shortcut: `make scan-update` (does the merge and normalizes the
69+
custom-plugin path in the baseline so it stays portable across
70+
machines; staging stays manual).
6771

6872
Commit the baseline change in the same commit as the code change
6973
that introduced the new finding.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""Rewrite the custom plugin path in ``.secrets.baseline`` to a portable form.
2+
3+
``detect-secrets scan`` stores the absolute ``file://`` URI of every
4+
``--plugin`` argument in ``plugins_used``. The URI is whatever path was
5+
on the developer's machine, so committing the baseline as written
6+
poisons it for every other contributor and CI runner.
7+
8+
This normalizer rewrites the ``KpmPasswordDetector`` entry's ``path``
9+
field to a stable, repository-relative URI. The KPM-aware audit wrapper
10+
pre-imports the plugin module before any baseline-driven lookup, so the
11+
URI is never resolved at runtime — only its textual stability matters.
12+
"""
13+
14+
from __future__ import annotations
15+
16+
import json
17+
import sys
18+
from pathlib import Path
19+
from typing import Any, Final, final
20+
21+
_PORTABLE_PLUGIN_PATH: Final[str] = (
22+
"file://tools/detect_secrets_plugins/kpm_password.py"
23+
)
24+
_PLUGIN_NAME: Final[str] = "KpmPasswordDetector"
25+
26+
27+
@final
28+
class PortableSecretsBaseline:
29+
"""A ``.secrets.baseline`` whose plugin paths are repo-relative."""
30+
31+
def __init__(self, path: Path) -> None:
32+
self._path = path
33+
34+
def normalize(self) -> None:
35+
with open(self._path, encoding="utf-8") as fh:
36+
data: dict[str, Any] = json.load(fh)
37+
for plugin in data.get("plugins_used", []):
38+
if plugin.get("name") == _PLUGIN_NAME:
39+
plugin["path"] = _PORTABLE_PLUGIN_PATH
40+
with open(self._path, "w", encoding="utf-8") as fh:
41+
json.dump(data, fh, indent=2)
42+
fh.write("\n")
43+
44+
45+
if __name__ == "__main__":
46+
if len(sys.argv) != 2:
47+
raise SystemExit(
48+
"usage: uv run python tools/normalize_secrets_baseline.py <baseline-path>"
49+
)
50+
PortableSecretsBaseline(Path(sys.argv[1])).normalize()

0 commit comments

Comments
 (0)