Skip to content

Commit 63f18ae

Browse files
committed
New hook: secrets-scanner
Add a secrets-scanner hook that scans files modified during a Copilot coding agent session for leaked secrets, credentials, and sensitive data. The hook runs on sessionEnd and inspects files in one of three scopes: - diff: only files changed in the current session (default) - staged: only files currently staged in the git index - all: every tracked file in the repository Detected pattern categories: - AWS access keys and secret keys - GCP service account credentials - Azure client secrets and storage connection strings - GitHub personal access tokens - Slack tokens (bot, user, webhook) - Private key headers (RSA, EC, DSA, OpenSSH, PEM) - Generic high-entropy bearer tokens - Internal IP:port strings Configurable via environment variables (SCAN_MODE, SCAN_SCOPE, SECRETS_ALLOWLIST) so teams can tune for their workflow without editing the script. Patterns are POSIX ERE (grep -E) compatible, with no PCRE metacharacters, for portability across macOS and Linux. Files: hooks.json, scan-secrets.sh, README.md
1 parent b1f3346 commit 63f18ae

File tree

4 files changed

+487
-0
lines changed

4 files changed

+487
-0
lines changed

docs/README.hooks.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,6 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-hooks) for guidelines on how to
3232
| Name | Description | Events | Bundled Assets |
3333
| ---- | ----------- | ------ | -------------- |
3434
| [Governance Audit](../hooks/governance-audit/README.md) | Scans Copilot agent prompts for threat signals and logs governance events | sessionStart, sessionEnd, userPromptSubmitted | `audit-prompt.sh`<br />`audit-session-end.sh`<br />`audit-session-start.sh`<br />`hooks.json` |
35+
| [Secrets Scanner](../hooks/secrets-scanner/README.md) | Scans files modified during a Copilot coding agent session for leaked secrets, credentials, and sensitive data | sessionEnd | `hooks.json`<br />`scan-secrets.sh` |
3536
| [Session Auto-Commit](../hooks/session-auto-commit/README.md) | Automatically commits and pushes changes when a Copilot coding agent session ends | sessionEnd | `auto-commit.sh`<br />`hooks.json` |
3637
| [Session Logger](../hooks/session-logger/README.md) | Logs all Copilot coding agent session activity for audit and analysis | sessionStart, sessionEnd, userPromptSubmitted | `hooks.json`<br />`log-prompt.sh`<br />`log-session-end.sh`<br />`log-session-start.sh` |

hooks/secrets-scanner/README.md

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
---
2+
name: 'Secrets Scanner'
3+
description: 'Scans files modified during a Copilot coding agent session for leaked secrets, credentials, and sensitive data'
4+
tags: ['security', 'secrets', 'scanning', 'pre-commit']
5+
---
6+
7+
# Secrets Scanner Hook
8+
9+
Scans files modified during a GitHub Copilot coding agent session for accidentally leaked secrets, credentials, API keys, and other sensitive data before they are committed.
10+
11+
## Overview
12+
13+
AI coding agents generate and modify code rapidly, which increases the risk of hardcoded secrets slipping into the codebase. This hook acts as a safety net by scanning all modified files at session end for 20+ categories of secret patterns, including:
14+
15+
- **Cloud credentials**: AWS access keys, GCP service account keys, Azure client secrets
16+
- **Platform tokens**: GitHub PATs, npm tokens, Slack tokens, Stripe keys
17+
- **Private keys**: RSA, EC, OpenSSH, PGP, DSA private key blocks
18+
- **Connection strings**: Database URIs (PostgreSQL, MongoDB, MySQL, Redis, MSSQL)
19+
- **Generic secrets**: API keys, passwords, bearer tokens, JWTs
20+
- **Internal infrastructure**: Private IP addresses with ports
21+
22+
## Features
23+
24+
- **Two scan modes**: `warn` (log only) or `block` (exit non-zero to prevent commit)
25+
- **Two scan scopes**: `diff` (modified files vs HEAD) or `staged` (git-staged files only)
26+
- **Smart filtering**: Skips binary files, lock files, and placeholder/example values
27+
- **Allowlist support**: Exclude known false positives via `SECRETS_ALLOWLIST`
28+
- **Structured logging**: JSON Lines output for integration with monitoring tools
29+
- **Redacted output**: Findings are truncated in logs to avoid re-exposing secrets
30+
- **Zero dependencies**: Uses only standard Unix tools (`grep`, `file`, `git`)
31+
32+
## Installation
33+
34+
1. Copy the hook folder to your repository:
35+
36+
```bash
37+
cp -r hooks/secrets-scanner .github/hooks/
38+
```
39+
40+
2. Ensure the script is executable:
41+
42+
```bash
43+
chmod +x .github/hooks/secrets-scanner/scan-secrets.sh
44+
```
45+
46+
3. Create the logs directory and add it to `.gitignore`:
47+
48+
```bash
49+
mkdir -p logs/copilot/secrets
50+
echo "logs/" >> .gitignore
51+
```
52+
53+
4. Commit the hook configuration to your repository's default branch.
54+
55+
## Configuration
56+
57+
The hook is configured in `hooks.json` to run on the `sessionEnd` event:
58+
59+
```json
60+
{
61+
"version": 1,
62+
"hooks": {
63+
"sessionEnd": [
64+
{
65+
"type": "command",
66+
"bash": ".github/hooks/secrets-scanner/scan-secrets.sh",
67+
"cwd": ".",
68+
"env": {
69+
"SCAN_MODE": "warn",
70+
"SCAN_SCOPE": "diff"
71+
},
72+
"timeoutSec": 30
73+
}
74+
]
75+
}
76+
}
77+
```
78+
79+
### Environment Variables
80+
81+
| Variable | Values | Default | Description |
82+
|----------|--------|---------|-------------|
83+
| `SCAN_MODE` | `warn`, `block` | `warn` | `warn` logs findings only; `block` exits non-zero to prevent auto-commit |
84+
| `SCAN_SCOPE` | `diff`, `staged` | `diff` | `diff` scans uncommitted changes vs HEAD; `staged` scans only staged files |
85+
| `SKIP_SECRETS_SCAN` | `true` | unset | Disable the scanner entirely |
86+
| `SECRETS_LOG_DIR` | path | `logs/copilot/secrets` | Directory where scan logs are written |
87+
| `SECRETS_ALLOWLIST` | comma-separated | unset | Patterns to ignore (e.g., `test_key_123,example.com`) |
88+
89+
## How It Works
90+
91+
1. When a Copilot coding agent session ends, the hook executes
92+
2. Collects all modified files using `git diff` (respects the configured scope)
93+
3. Filters out binary files and lock files
94+
4. Scans each text file line-by-line against 20+ regex patterns for known secret formats
95+
5. Skips matches that look like placeholders (e.g., values containing `example`, `changeme`, `your_`)
96+
6. Checks matches against the allowlist if configured
97+
7. Reports findings with file path, line number, pattern name, and severity
98+
8. Writes a structured JSON log entry for audit purposes
99+
9. In `block` mode, exits non-zero to signal the agent to stop before committing
100+
101+
## Detected Secret Patterns
102+
103+
| Pattern | Severity | Example Match |
104+
|---------|----------|---------------|
105+
| `AWS_ACCESS_KEY` | critical | `AKIAIOSFODNN7EXAMPLE` |
106+
| `AWS_SECRET_KEY` | critical | `aws_secret_access_key = wJalr...` |
107+
| `GCP_SERVICE_ACCOUNT` | critical | `"type": "service_account"` |
108+
| `GCP_API_KEY` | high | `AIzaSyC...` |
109+
| `AZURE_CLIENT_SECRET` | critical | `azure_client_secret = ...` |
110+
| `GITHUB_PAT` | critical | `ghp_xxxxxxxxxxxx...` |
111+
| `GITHUB_FINE_GRAINED_PAT` | critical | `github_pat_...` |
112+
| `PRIVATE_KEY` | critical | `-----BEGIN RSA PRIVATE KEY-----` |
113+
| `GENERIC_SECRET` | high | `api_key = "sk-..."` |
114+
| `CONNECTION_STRING` | high | `postgresql://user:pass@host/db` |
115+
| `SLACK_TOKEN` | high | `xoxb-...` |
116+
| `STRIPE_SECRET_KEY` | critical | `sk_live_...` |
117+
| `NPM_TOKEN` | high | `npm_...` |
118+
| `JWT_TOKEN` | medium | `eyJhbGci...` |
119+
| `INTERNAL_IP_PORT` | medium | `192.168.1.1:8080` |
120+
121+
See the full list in `scan-secrets.sh`.
122+
123+
## Example Output
124+
125+
### Clean scan
126+
127+
```
128+
🔍 Scanning 5 modified file(s) for secrets...
129+
✅ No secrets detected in 5 scanned file(s)
130+
```
131+
132+
### Findings detected (warn mode)
133+
134+
```
135+
🔍 Scanning 3 modified file(s) for secrets...
136+
137+
⚠️ Found 2 potential secret(s) in modified files:
138+
139+
FILE LINE PATTERN SEVERITY
140+
---- ---- ------- --------
141+
src/config.ts 12 GITHUB_PAT critical
142+
.env.local 3 CONNECTION_STRING high
143+
144+
💡 Review the findings above. Set SCAN_MODE=block to prevent commits with secrets.
145+
```
146+
147+
### Findings detected (block mode)
148+
149+
```
150+
🔍 Scanning 3 modified file(s) for secrets...
151+
152+
⚠️ Found 1 potential secret(s) in modified files:
153+
154+
FILE LINE PATTERN SEVERITY
155+
---- ---- ------- --------
156+
lib/auth.py 45 AWS_ACCESS_KEY critical
157+
158+
🚫 Session blocked: resolve the findings above before committing.
159+
Set SCAN_MODE=warn to log without blocking, or add patterns to SECRETS_ALLOWLIST.
160+
```
161+
162+
## Log Format
163+
164+
Scan events are written to `logs/copilot/secrets/scan.log` in JSON Lines format:
165+
166+
```json
167+
{"timestamp":"2026-03-13T10:30:00Z","event":"secrets_found","mode":"warn","scope":"diff","files_scanned":3,"finding_count":2,"findings":[{"file":"src/config.ts","line":12,"pattern":"GITHUB_PAT","severity":"critical","match":"ghp_...xyz1"}]}
168+
```
169+
170+
```json
171+
{"timestamp":"2026-03-13T10:30:00Z","event":"scan_complete","mode":"warn","scope":"diff","status":"clean","files_scanned":5}
172+
```
173+
174+
## Pairing with Other Hooks
175+
176+
This hook pairs well with the **Session Auto-Commit** hook. When both are installed, order them so that `secrets-scanner` runs first:
177+
178+
1. Secrets scanner runs at `sessionEnd`, catches leaked secrets
179+
2. Auto-commit runs at `sessionEnd`, only commits if all previous hooks pass
180+
181+
Set `SCAN_MODE=block` to prevent auto-commit when secrets are detected.
182+
183+
## Customization
184+
185+
- **Add custom patterns**: Edit the `PATTERNS` array in `scan-secrets.sh` to add project-specific secret formats
186+
- **Adjust sensitivity**: Change severity levels or remove patterns that generate false positives
187+
- **Allowlist known values**: Use `SECRETS_ALLOWLIST` for test fixtures or known safe patterns
188+
- **Change log location**: Set `SECRETS_LOG_DIR` to route logs to your preferred directory
189+
190+
## Disabling
191+
192+
To temporarily disable the scanner:
193+
194+
- Set `SKIP_SECRETS_SCAN=true` in the hook environment
195+
- Or remove the `sessionEnd` entry from `hooks.json`
196+
197+
## Limitations
198+
199+
- Pattern-based detection; does not perform entropy analysis or contextual validation
200+
- May produce false positives for test fixtures or example code (use the allowlist to suppress these)
201+
- Scans only text files; binary secrets (keystores, certificates in DER format) are not detected
202+
- Requires `git` to be available in the execution environment

hooks/secrets-scanner/hooks.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"version": 1,
3+
"hooks": {
4+
"sessionEnd": [
5+
{
6+
"type": "command",
7+
"bash": ".github/hooks/secrets-scanner/scan-secrets.sh",
8+
"cwd": ".",
9+
"env": {
10+
"SCAN_MODE": "warn",
11+
"SCAN_SCOPE": "diff"
12+
},
13+
"timeoutSec": 30
14+
}
15+
]
16+
}
17+
}

0 commit comments

Comments
 (0)