Skip to content

Commit 8f9d35b

Browse files
added custom secrets management
1 parent 6e513a2 commit 8f9d35b

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

.pre-commit-hooks.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,10 @@
210210
types: [text]
211211
stages: [pre-commit, pre-push, manual]
212212
minimum_pre_commit_version: 3.2.0
213+
214+
- id: detect-secrets
215+
name: detects secrets from custom regex file
216+
description: can take in a custom regex file to scan for custom secrets.
217+
entry: detect-secrets
218+
langauge: python
219+

pre_commit_hooks/detect_secrets.py

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
from __future__ import annotations
2+
3+
import argparse
4+
import re
5+
import subprocess
6+
from collections.abc import Sequence
7+
from pathlib import Path
8+
9+
10+
# -------------------------
11+
# Default secret patterns
12+
# -------------------------
13+
14+
DEFAULT_PATTERNS: dict[str, str] = {
15+
# GitLab
16+
"gitlab_pat": r"glpat-[0-9A-Za-z_-]{20,}",
17+
"gitlab_runner_token": r"glrt-[0-9A-Za-z_-]{20,}",
18+
19+
# GitHub
20+
"github_pat": r"ghp_[0-9A-Za-z]{36}",
21+
"github_fine_grained_pat": r"github_pat_[0-9A-Za-z_]{82}",
22+
23+
# AWS
24+
"aws_access_key": r"AKIA[0-9A-Z]{16}",
25+
"aws_secret_key": r"(?i)aws(.{0,20})?(secret|access)[-_ ]?key(.{0,20})?['\"][0-9a-zA-Z/+]{40}['\"]",
26+
27+
# Generic
28+
"generic_secret": r"(?i)(password|passwd|pwd|secret|token|api[_-]?key)\s*=\s*['\"].+['\"]",
29+
}
30+
31+
32+
33+
def load_custom_patterns(path: Path) -> dict[str, str]:
34+
patterns: dict[str, str] = {}
35+
for i, line in enumerate(path.read_text().splitlines(), start=1):
36+
line = line.strip()
37+
if not line or line.startswith("#"):
38+
continue
39+
patterns[f"custom_rule_{i}"] = line
40+
return patterns
41+
42+
43+
def is_binary(data: bytes) -> bool:
44+
return b"\x00" in data
45+
46+
47+
def git_tracked_files() -> list[Path]:
48+
"""Return all git-tracked files in the repo."""
49+
result = subprocess.run(
50+
["git", "ls-files"],
51+
stdout=subprocess.PIPE,
52+
stderr=subprocess.DEVNULL,
53+
text=True,
54+
check=False,
55+
)
56+
return [Path(p) for p in result.stdout.splitlines() if p]
57+
58+
59+
def main(argv: Sequence[str] | None = None) -> int:
60+
parser = argparse.ArgumentParser(description="Detect exposed secrets in repository")
61+
parser.add_argument(
62+
"--rules",
63+
type=Path,
64+
help="File containing custom regex rules (one per line)",
65+
)
66+
parser.add_argument(
67+
"filenames",
68+
nargs="*",
69+
help="Files to scan (if empty, scans entire repo)",
70+
)
71+
72+
args = parser.parse_args(argv)
73+
74+
patterns = dict(DEFAULT_PATTERNS)
75+
76+
if args.rules:
77+
if not args.rules.is_file():
78+
print(f"Rules file not found: {args.rules}")
79+
return 2
80+
patterns.update(load_custom_patterns(args.rules))
81+
82+
compiled = {
83+
name: re.compile(regex)
84+
for name, regex in patterns.items()
85+
}
86+
87+
files: list[Path]
88+
if args.filenames:
89+
files = [Path(f) for f in args.filenames]
90+
else:
91+
files = git_tracked_files()
92+
93+
findings: list[tuple[Path, str]] = []
94+
95+
for path in files:
96+
if not path.is_file():
97+
continue
98+
99+
try:
100+
data = path.read_bytes()
101+
except OSError:
102+
continue
103+
104+
if is_binary(data):
105+
continue
106+
107+
text = data.decode(errors="ignore")
108+
109+
for rule, regex in compiled.items():
110+
if regex.search(text):
111+
findings.append((path, rule))
112+
113+
if findings:
114+
print("Potential secrets detected:")
115+
for path, rule in findings:
116+
print(f" - {path} (matched: {rule})")
117+
return 1
118+
119+
return 0
120+
121+
122+
if __name__ == "__main__":
123+
raise SystemExit(main())

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ console_scripts =
6060
requirements-txt-fixer = pre_commit_hooks.requirements_txt_fixer:main
6161
sort-simple-yaml = pre_commit_hooks.sort_simple_yaml:main
6262
trailing-whitespace-fixer = pre_commit_hooks.trailing_whitespace_fixer:main
63+
detect-secrets = pre_commit_hooks.detect_secrets:main
6364

6465
[bdist_wheel]
6566
universal = True

0 commit comments

Comments
 (0)