@intentsolutionsio/penetration-tester 2.0.0 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +8 -3
- package/README.md +8 -0
- package/commands/pentest.md +5 -0
- package/package.json +8 -3
- package/skills/analyzing-tls-config/SKILL.md +221 -0
- package/skills/analyzing-tls-config/references/AUTHORIZATION.md +133 -0
- package/skills/analyzing-tls-config/references/PLAYBOOK.md +267 -0
- package/skills/analyzing-tls-config/references/THEORY.md +128 -0
- package/skills/analyzing-tls-config/scripts/analyze_tls.py +415 -0
- package/skills/auditing-cors-policy/SKILL.md +186 -0
- package/skills/auditing-cors-policy/references/PLAYBOOK.md +220 -0
- package/skills/auditing-cors-policy/references/THEORY.md +142 -0
- package/skills/auditing-cors-policy/scripts/audit_cors.py +350 -0
- package/skills/auditing-npm-dependencies/SKILL.md +254 -0
- package/skills/auditing-npm-dependencies/references/PLAYBOOK.md +175 -0
- package/skills/auditing-npm-dependencies/references/THEORY.md +122 -0
- package/skills/auditing-npm-dependencies/scripts/audit_npm.py +408 -0
- package/skills/auditing-python-dependencies/SKILL.md +251 -0
- package/skills/auditing-python-dependencies/references/PLAYBOOK.md +193 -0
- package/skills/auditing-python-dependencies/references/THEORY.md +122 -0
- package/skills/auditing-python-dependencies/scripts/audit_python.py +459 -0
- package/skills/checking-http-security-headers/SKILL.md +176 -0
- package/skills/checking-http-security-headers/references/PLAYBOOK.md +212 -0
- package/skills/checking-http-security-headers/references/THEORY.md +137 -0
- package/skills/checking-http-security-headers/scripts/check_headers.py +362 -0
- package/skills/checking-license-compliance/SKILL.md +225 -0
- package/skills/checking-license-compliance/references/PLAYBOOK.md +161 -0
- package/skills/checking-license-compliance/references/THEORY.md +152 -0
- package/skills/checking-license-compliance/scripts/check_licenses.py +461 -0
- package/skills/composing-vulnerability-report/SKILL.md +212 -0
- package/skills/composing-vulnerability-report/references/PLAYBOOK.md +180 -0
- package/skills/composing-vulnerability-report/references/THEORY.md +178 -0
- package/skills/composing-vulnerability-report/scripts/compose_report.py +396 -0
- package/skills/confirming-pentest-authorization/SKILL.md +247 -0
- package/skills/confirming-pentest-authorization/references/PLAYBOOK.md +189 -0
- package/skills/confirming-pentest-authorization/references/THEORY.md +167 -0
- package/skills/confirming-pentest-authorization/scripts/check_authorization.py +457 -0
- package/skills/defining-pentest-scope/SKILL.md +227 -0
- package/skills/defining-pentest-scope/references/PLAYBOOK.md +238 -0
- package/skills/defining-pentest-scope/references/THEORY.md +170 -0
- package/skills/defining-pentest-scope/scripts/define_scope.py +472 -0
- package/skills/detecting-command-injection-patterns/SKILL.md +144 -0
- package/skills/detecting-command-injection-patterns/references/PLAYBOOK.md +302 -0
- package/skills/detecting-command-injection-patterns/references/THEORY.md +206 -0
- package/skills/detecting-command-injection-patterns/scripts/scan_cmdi.py +290 -0
- package/skills/detecting-debug-endpoints/SKILL.md +207 -0
- package/skills/detecting-debug-endpoints/references/PLAYBOOK.md +402 -0
- package/skills/detecting-debug-endpoints/references/THEORY.md +218 -0
- package/skills/detecting-debug-endpoints/scripts/probe_debug.py +518 -0
- package/skills/detecting-directory-listing/SKILL.md +206 -0
- package/skills/detecting-directory-listing/references/PLAYBOOK.md +277 -0
- package/skills/detecting-directory-listing/references/THEORY.md +203 -0
- package/skills/detecting-directory-listing/scripts/probe_directory_listing.py +180 -0
- package/skills/detecting-eval-exec-usage/SKILL.md +128 -0
- package/skills/detecting-eval-exec-usage/references/PLAYBOOK.md +306 -0
- package/skills/detecting-eval-exec-usage/references/THEORY.md +159 -0
- package/skills/detecting-eval-exec-usage/scripts/scan_eval.py +223 -0
- package/skills/detecting-exposed-secrets-files/SKILL.md +179 -0
- package/skills/detecting-exposed-secrets-files/references/PLAYBOOK.md +274 -0
- package/skills/detecting-exposed-secrets-files/references/THEORY.md +174 -0
- package/skills/detecting-exposed-secrets-files/scripts/probe_secrets.py +207 -0
- package/skills/detecting-insecure-deserialization/SKILL.md +148 -0
- package/skills/detecting-insecure-deserialization/references/PLAYBOOK.md +333 -0
- package/skills/detecting-insecure-deserialization/references/THEORY.md +199 -0
- package/skills/detecting-insecure-deserialization/scripts/scan_deserialization.py +250 -0
- package/skills/detecting-sql-injection-patterns/SKILL.md +161 -0
- package/skills/detecting-sql-injection-patterns/references/PLAYBOOK.md +317 -0
- package/skills/detecting-sql-injection-patterns/references/THEORY.md +261 -0
- package/skills/detecting-sql-injection-patterns/scripts/scan_sqli.py +354 -0
- package/skills/detecting-ssl-cert-issues/SKILL.md +182 -0
- package/skills/detecting-ssl-cert-issues/references/PLAYBOOK.md +203 -0
- package/skills/detecting-ssl-cert-issues/references/THEORY.md +133 -0
- package/skills/detecting-ssl-cert-issues/scripts/check_cert_chain.py +481 -0
- package/skills/detecting-weak-cryptography/SKILL.md +147 -0
- package/skills/detecting-weak-cryptography/references/PLAYBOOK.md +466 -0
- package/skills/detecting-weak-cryptography/references/THEORY.md +194 -0
- package/skills/detecting-weak-cryptography/scripts/scan_weak_crypto.py +417 -0
- package/skills/fingerprinting-server-software/SKILL.md +191 -0
- package/skills/fingerprinting-server-software/references/PLAYBOOK.md +337 -0
- package/skills/fingerprinting-server-software/references/THEORY.md +183 -0
- package/skills/fingerprinting-server-software/scripts/fingerprint_server.py +347 -0
- package/skills/generating-executive-summary/SKILL.md +261 -0
- package/skills/generating-executive-summary/references/PLAYBOOK.md +201 -0
- package/skills/generating-executive-summary/references/THEORY.md +195 -0
- package/skills/generating-executive-summary/scripts/exec_summary.py +538 -0
- package/skills/mapping-findings-to-owasp-top10/SKILL.md +235 -0
- package/skills/mapping-findings-to-owasp-top10/references/PLAYBOOK.md +193 -0
- package/skills/mapping-findings-to-owasp-top10/references/THEORY.md +160 -0
- package/skills/mapping-findings-to-owasp-top10/scripts/map_owasp.py +540 -0
- package/skills/performing-penetration-testing/SKILL.md +282 -190
- package/skills/performing-penetration-testing/references/OWASP_TOP_10.md +22 -0
- package/skills/performing-penetration-testing/references/REMEDIATION_PLAYBOOK.md +46 -0
- package/skills/performing-penetration-testing/references/SECURITY_HEADERS.md +41 -0
- package/skills/performing-penetration-testing/scripts/code_security_scanner.py +144 -79
- package/skills/performing-penetration-testing/scripts/dependency_auditor.py +116 -93
- package/skills/performing-penetration-testing/scripts/security_scanner.py +574 -446
- package/skills/probing-dangerous-http-methods/SKILL.md +182 -0
- package/skills/probing-dangerous-http-methods/references/PLAYBOOK.md +234 -0
- package/skills/probing-dangerous-http-methods/references/THEORY.md +145 -0
- package/skills/probing-dangerous-http-methods/scripts/probe_methods.py +263 -0
- package/skills/recording-pentest-engagement/SKILL.md +253 -0
- package/skills/recording-pentest-engagement/references/PLAYBOOK.md +203 -0
- package/skills/recording-pentest-engagement/references/THEORY.md +195 -0
- package/skills/recording-pentest-engagement/scripts/record_engagement.py +461 -0
- package/skills/scanning-for-hardcoded-secrets/SKILL.md +215 -0
- package/skills/scanning-for-hardcoded-secrets/references/PLAYBOOK.md +325 -0
- package/skills/scanning-for-hardcoded-secrets/references/THEORY.md +175 -0
- package/skills/scanning-for-hardcoded-secrets/scripts/scan_secrets.py +395 -0
- package/skills/tracing-transitive-vulnerabilities/SKILL.md +235 -0
- package/skills/tracing-transitive-vulnerabilities/references/PLAYBOOK.md +233 -0
- package/skills/tracing-transitive-vulnerabilities/references/THEORY.md +138 -0
- package/skills/tracing-transitive-vulnerabilities/scripts/trace_vulns.py +484 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
# Hardcoded-Secrets Remediation Playbook
|
|
2
|
+
|
|
3
|
+
## After detection: the standing procedure
|
|
4
|
+
|
|
5
|
+
1. **Rotate.** Same hour. The window between detection and rotation
|
|
6
|
+
is dead time for the credential's usefulness to anyone but
|
|
7
|
+
attackers.
|
|
8
|
+
2. **Audit upstream logs.** Check the provider's API audit log
|
|
9
|
+
(AWS CloudTrail, GitHub audit log, Stripe events, etc.) for any
|
|
10
|
+
request against the credential since the leak commit timestamp.
|
|
11
|
+
3. **Remove from source.** Replace the literal with an env-var
|
|
12
|
+
lookup or secrets-manager fetch. See per-language patterns
|
|
13
|
+
below.
|
|
14
|
+
4. **Add a pre-commit gate.** Wire this skill into the pre-commit
|
|
15
|
+
hook so the same engineer doesn't re-introduce the same class
|
|
16
|
+
tomorrow.
|
|
17
|
+
5. **(Optional) Scrub history.** Only if private repo with
|
|
18
|
+
controlled clones AND credential is non-rotatable AND
|
|
19
|
+
coordination overhead is acceptable.
|
|
20
|
+
|
|
21
|
+
## Per-language migration patterns
|
|
22
|
+
|
|
23
|
+
### Python — `python-dotenv`
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
# Before (vulnerable):
|
|
27
|
+
STRIPE_KEY = "sk_live_abc123..."
|
|
28
|
+
|
|
29
|
+
# After:
|
|
30
|
+
import os
|
|
31
|
+
from dotenv import load_dotenv
|
|
32
|
+
load_dotenv()
|
|
33
|
+
STRIPE_KEY = os.environ["STRIPE_KEY"] # KeyError on missing
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`.env` (gitignored):
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
STRIPE_KEY=sk_live_abc123...
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`.gitignore`:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
.env
|
|
46
|
+
.env.local
|
|
47
|
+
.env.production
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Python — pydantic-settings (typed config)
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
54
|
+
|
|
55
|
+
class Settings(BaseSettings):
|
|
56
|
+
model_config = SettingsConfigDict(env_file=".env", case_sensitive=False)
|
|
57
|
+
stripe_key: str
|
|
58
|
+
aws_access_key_id: str
|
|
59
|
+
aws_secret_access_key: str
|
|
60
|
+
|
|
61
|
+
settings = Settings()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Node.js — dotenv
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
// Before:
|
|
68
|
+
const STRIPE_KEY = "sk_live_abc123...";
|
|
69
|
+
|
|
70
|
+
// After:
|
|
71
|
+
require('dotenv').config();
|
|
72
|
+
const STRIPE_KEY = process.env.STRIPE_KEY;
|
|
73
|
+
if (!STRIPE_KEY) throw new Error("STRIPE_KEY not configured");
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Ruby on Rails — `Rails.application.credentials`
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
# Edit (creates / decrypts encrypted credentials)
|
|
80
|
+
EDITOR=vim rails credentials:edit
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
In the editor:
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
stripe:
|
|
87
|
+
api_key: sk_live_abc123...
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Then in code:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
Rails.application.credentials.dig(:stripe, :api_key)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The encrypted file (`config/credentials.yml.enc`) commits to git;
|
|
97
|
+
the decryption key (`config/master.key`) does NOT. Both are
|
|
98
|
+
required to read the secret at runtime.
|
|
99
|
+
|
|
100
|
+
### Go — `envconfig`
|
|
101
|
+
|
|
102
|
+
```go
|
|
103
|
+
package main
|
|
104
|
+
|
|
105
|
+
import (
|
|
106
|
+
"log"
|
|
107
|
+
"github.com/kelseyhightower/envconfig"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
type Config struct {
|
|
111
|
+
StripeKey string `envconfig:"STRIPE_KEY" required:"true"`
|
|
112
|
+
AWSAccessKeyID string `envconfig:"AWS_ACCESS_KEY_ID" required:"true"`
|
|
113
|
+
AWSSecretKey string `envconfig:"AWS_SECRET_ACCESS_KEY" required:"true"`
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func main() {
|
|
117
|
+
var c Config
|
|
118
|
+
if err := envconfig.Process("", &c); err != nil {
|
|
119
|
+
log.Fatal(err)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Rust — `dotenvy` + `envy`
|
|
125
|
+
|
|
126
|
+
```rust
|
|
127
|
+
use serde::Deserialize;
|
|
128
|
+
|
|
129
|
+
#[derive(Deserialize)]
|
|
130
|
+
struct Config {
|
|
131
|
+
stripe_key: String,
|
|
132
|
+
aws_access_key_id: String,
|
|
133
|
+
aws_secret_access_key: String,
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
fn main() {
|
|
137
|
+
dotenvy::dotenv().ok();
|
|
138
|
+
let config: Config = envy::from_env().expect("missing env config");
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Java — Spring Boot `@Value`
|
|
143
|
+
|
|
144
|
+
```java
|
|
145
|
+
@Component
|
|
146
|
+
public class StripeConfig {
|
|
147
|
+
@Value("${stripe.api.key}")
|
|
148
|
+
private String stripeKey;
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
`application.yml`:
|
|
153
|
+
|
|
154
|
+
```yaml
|
|
155
|
+
stripe:
|
|
156
|
+
api:
|
|
157
|
+
key: ${STRIPE_KEY} # interpolated from environment
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Production: set `STRIPE_KEY` in the environment (Kubernetes secret,
|
|
161
|
+
ECS task definition, etc.) and Spring picks it up at startup.
|
|
162
|
+
|
|
163
|
+
## Secrets managers
|
|
164
|
+
|
|
165
|
+
For production, env vars alone aren't sufficient (they leak via
|
|
166
|
+
process listings, container introspection, error reports). Use a
|
|
167
|
+
dedicated secrets manager.
|
|
168
|
+
|
|
169
|
+
### AWS Secrets Manager
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
import boto3, json
|
|
173
|
+
client = boto3.client("secretsmanager")
|
|
174
|
+
secret_value = client.get_secret_value(SecretId="prod/stripe")["SecretString"]
|
|
175
|
+
config = json.loads(secret_value)
|
|
176
|
+
STRIPE_KEY = config["api_key"]
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
IAM policy grants the runtime role `secretsmanager:GetSecretValue`
|
|
180
|
+
on the specific secret ARN. No literal in source; no literal in
|
|
181
|
+
env-var dumps.
|
|
182
|
+
|
|
183
|
+
### GCP Secret Manager
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
from google.cloud import secretmanager
|
|
187
|
+
|
|
188
|
+
client = secretmanager.SecretManagerServiceClient()
|
|
189
|
+
name = "projects/my-project/secrets/stripe-key/versions/latest"
|
|
190
|
+
response = client.access_secret_version(request={"name": name})
|
|
191
|
+
STRIPE_KEY = response.payload.data.decode("UTF-8")
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### HashiCorp Vault
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# At runtime, runtime fetches the secret
|
|
198
|
+
vault kv get -field=api_key secret/stripe
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Or via the SDK:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
import hvac
|
|
205
|
+
client = hvac.Client(url="https://vault.internal:8200")
|
|
206
|
+
client.auth.approle.login(role_id=..., secret_id=...)
|
|
207
|
+
secret = client.secrets.kv.v2.read_secret_version(path="stripe")
|
|
208
|
+
STRIPE_KEY = secret["data"]["data"]["api_key"]
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Doppler / 1Password Secrets Automation / Bitwarden Secrets
|
|
212
|
+
|
|
213
|
+
All follow the same pattern: SDK call at startup, no literals in
|
|
214
|
+
source.
|
|
215
|
+
|
|
216
|
+
## Pre-commit hook integration
|
|
217
|
+
|
|
218
|
+
### Using `pre-commit` framework
|
|
219
|
+
|
|
220
|
+
`.pre-commit-config.yaml`:
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
repos:
|
|
224
|
+
- repo: local
|
|
225
|
+
hooks:
|
|
226
|
+
- id: scan-secrets
|
|
227
|
+
name: Scan for hardcoded secrets
|
|
228
|
+
entry: python3 plugins/security/penetration-tester/skills/scanning-for-hardcoded-secrets/scripts/scan_secrets.py
|
|
229
|
+
language: system
|
|
230
|
+
args: ['--min-severity', 'high']
|
|
231
|
+
pass_filenames: false
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Install: `pre-commit install`. Now every `git commit` runs the
|
|
235
|
+
scan; commits abort if the scan finds a high/critical credential.
|
|
236
|
+
|
|
237
|
+
### Husky (Node projects)
|
|
238
|
+
|
|
239
|
+
`package.json`:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"husky": {
|
|
244
|
+
"hooks": {
|
|
245
|
+
"pre-commit": "python3 plugins/security/penetration-tester/skills/scanning-for-hardcoded-secrets/scripts/scan_secrets.py --min-severity high . || exit 1"
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Provider rotation procedures
|
|
252
|
+
|
|
253
|
+
### AWS access key
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
# Create new key (keep old active until apps cut over)
|
|
257
|
+
aws iam create-access-key --user-name myuser
|
|
258
|
+
|
|
259
|
+
# Update app config / secrets manager with new key
|
|
260
|
+
# Verify apps are using new key (CloudTrail will show old key inactive)
|
|
261
|
+
|
|
262
|
+
# Then deactivate + delete old key
|
|
263
|
+
aws iam update-access-key --user-name myuser --access-key-id AKIAOLD --status Inactive
|
|
264
|
+
# After 24h grace:
|
|
265
|
+
aws iam delete-access-key --user-name myuser --access-key-id AKIAOLD
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### GitHub PAT
|
|
269
|
+
|
|
270
|
+
Settings → Developer settings → Personal access tokens → revoke
|
|
271
|
+
old, generate new. Update CI / local config.
|
|
272
|
+
|
|
273
|
+
### Stripe
|
|
274
|
+
|
|
275
|
+
Dashboard → Developers → API keys → roll secret key. Two-step:
|
|
276
|
+
generate new, deploy, deactivate old.
|
|
277
|
+
|
|
278
|
+
### Anthropic
|
|
279
|
+
|
|
280
|
+
Console → API keys → revoke old, create new. Update env / secrets
|
|
281
|
+
manager.
|
|
282
|
+
|
|
283
|
+
### Slack
|
|
284
|
+
|
|
285
|
+
App settings → OAuth & Permissions → "Reinstall to Workspace" with
|
|
286
|
+
admin approval generates fresh bot/user tokens.
|
|
287
|
+
|
|
288
|
+
## GitHub Secret Scanning integration
|
|
289
|
+
|
|
290
|
+
If your repo is on GitHub:
|
|
291
|
+
|
|
292
|
+
- Settings → Code security → enable "Secret scanning alerts"
|
|
293
|
+
- Settings → Code security → enable "Push protection" (this is the
|
|
294
|
+
game-changer: blocks pushes that contain detected credential
|
|
295
|
+
shapes, BEFORE the commit lands on the remote)
|
|
296
|
+
|
|
297
|
+
GitHub's pattern library is roughly equivalent to this skill's;
|
|
298
|
+
running both is defense-in-depth.
|
|
299
|
+
|
|
300
|
+
## CI integration
|
|
301
|
+
|
|
302
|
+
```yaml
|
|
303
|
+
- name: Hardcoded-secrets scan
|
|
304
|
+
run: |
|
|
305
|
+
python3 plugins/security/penetration-tester/skills/scanning-for-hardcoded-secrets/scripts/scan_secrets.py \
|
|
306
|
+
. --min-severity high --format json --output secrets-scan.json
|
|
307
|
+
- name: Fail on findings
|
|
308
|
+
run: |
|
|
309
|
+
if [ "$(jq 'length' secrets-scan.json)" != "0" ]; then
|
|
310
|
+
cat secrets-scan.json
|
|
311
|
+
exit 1
|
|
312
|
+
fi
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
## Verification after remediation
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
python3 ${CLAUDE_PLUGIN_ROOT}/skills/scanning-for-hardcoded-secrets/scripts/scan_secrets.py \
|
|
319
|
+
/path/to/repo --min-severity high
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Expected: exit 0, zero high/critical findings. MEDIUM entropy-based
|
|
323
|
+
findings may persist if your codebase legitimately contains high-
|
|
324
|
+
entropy literals in test fixtures or build artifacts; verify
|
|
325
|
+
manually.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Hardcoded-Secrets Theory
|
|
2
|
+
|
|
3
|
+
## Why this class persists despite being well-known
|
|
4
|
+
|
|
5
|
+
The pattern is well-understood: don't hardcode credentials in source.
|
|
6
|
+
Every engineering team knows this. Every framework's getting-started
|
|
7
|
+
guide opens with "set this in your `.env` file, not in code." Yet
|
|
8
|
+
the class remains the #1 root cause of credential compromise year
|
|
9
|
+
over year.
|
|
10
|
+
|
|
11
|
+
Three reasons:
|
|
12
|
+
|
|
13
|
+
1. **The "just for testing" trap.** An engineer is debugging
|
|
14
|
+
integration with a new API. The right thing is to set
|
|
15
|
+
`STRIPE_KEY=...` in the local env and read from there. The fast
|
|
16
|
+
thing is to paste the key into the integration test file as a
|
|
17
|
+
literal. The test works, the engineer moves on, the literal
|
|
18
|
+
stays.
|
|
19
|
+
|
|
20
|
+
2. **Migration leftovers.** A codebase migrated from one secrets
|
|
21
|
+
pattern to another (e.g., from `.env` to AWS Secrets Manager)
|
|
22
|
+
often leaves stale literals from the pre-migration state, even if
|
|
23
|
+
the runtime fetches from the new location.
|
|
24
|
+
|
|
25
|
+
3. **Test fixtures with real keys.** Integration tests need real
|
|
26
|
+
credentials to test against real APIs. Some teams check those
|
|
27
|
+
into a `tests/fixtures/` directory with full intent. The test
|
|
28
|
+
harness is now a permanent credential leak surface.
|
|
29
|
+
|
|
30
|
+
The defensive answer is automated detection: scan on every commit
|
|
31
|
+
(pre-commit hook), every push (CI gate), every release (full audit).
|
|
32
|
+
Tools like gitleaks, trufflehog, and GitHub Secret Scanning all
|
|
33
|
+
implement variations of the same regex library this skill uses.
|
|
34
|
+
|
|
35
|
+
## Why provider-specific regex is the right pattern
|
|
36
|
+
|
|
37
|
+
The naive approach is entropy detection: "find any long string with
|
|
38
|
+
high randomness." The problem is the false-positive rate. Hash
|
|
39
|
+
digests, base64-encoded image data, minified JavaScript, and
|
|
40
|
+
compiled artifacts all look entropy-shaped.
|
|
41
|
+
|
|
42
|
+
Provider-specific regex works because credential issuers use
|
|
43
|
+
prefixed shapes intentionally — partly for routing, partly so their
|
|
44
|
+
own scanners can find leaks in customer code. Examples:
|
|
45
|
+
|
|
46
|
+
| Provider | Prefix | Why prefixed |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| AWS | `AKIA`, `ASIA`, `ABIA` | IAM type indicator (`AKIA` = long-term, `ASIA` = STS session) |
|
|
49
|
+
| GitHub | `ghp_`, `gho_`, `ghs_`, `ghu_`, `ghr_` | Token-class router |
|
|
50
|
+
| Stripe | `sk_live_`, `sk_test_`, `rk_live_`, `pk_live_` | Environment + role |
|
|
51
|
+
| Anthropic | `sk-ant-api03-` | Format-version + env |
|
|
52
|
+
| OpenAI | `sk-` and `sk-proj-` | Origin (user vs project) |
|
|
53
|
+
| Slack | `xoxb-`, `xoxp-`, `xoxa-` | Token type |
|
|
54
|
+
| Twilio | `AC`, `SK` | Account SID vs API key SID |
|
|
55
|
+
| Google | `AIza` | Service-account vs user-creds router |
|
|
56
|
+
|
|
57
|
+
The prefix means the provider's own scanner can find the leak the
|
|
58
|
+
moment it lands in a public repo (GitHub Secret Scanning auto-
|
|
59
|
+
notifies the provider). The scanner runs at machine speed: a public
|
|
60
|
+
gist containing `AKIA...` is detected in seconds, and AWS gets a
|
|
61
|
+
push notification.
|
|
62
|
+
|
|
63
|
+
This skill scans the same set on the assumption: if the provider's
|
|
64
|
+
bots will find it within seconds, the defensive posture is to find
|
|
65
|
+
it before the commit lands.
|
|
66
|
+
|
|
67
|
+
## The 1-minute leak window
|
|
68
|
+
|
|
69
|
+
For public repos, the time between commit landing and credential
|
|
70
|
+
extraction is on the order of seconds. GitHub Secret Scanning is
|
|
71
|
+
roughly real-time; bot operators scraping the public push event
|
|
72
|
+
stream are also real-time. By the time a developer notices the
|
|
73
|
+
credential in their `git log` and force-pushes a rewrite, the
|
|
74
|
+
extraction has happened. The credential must be considered
|
|
75
|
+
compromised from the moment of push, regardless of subsequent
|
|
76
|
+
history-scrub.
|
|
77
|
+
|
|
78
|
+
For private repos: window depends on access posture. If contractors
|
|
79
|
+
can clone, the window is "until you trust every contractor with
|
|
80
|
+
every credential ever committed." If only employees clone, the
|
|
81
|
+
window is "until any employee departs."
|
|
82
|
+
|
|
83
|
+
Either way: rotate is mandatory, history-scrub is optional.
|
|
84
|
+
|
|
85
|
+
## Entropy as a fallback
|
|
86
|
+
|
|
87
|
+
Provider regex covers known credential shapes. New providers and
|
|
88
|
+
custom internal tokens don't match.
|
|
89
|
+
|
|
90
|
+
Shannon entropy measures information density in a string. Higher
|
|
91
|
+
entropy = less compressible = more "random-looking." Real
|
|
92
|
+
credentials are by design high-entropy (an attacker brute-forcing a
|
|
93
|
+
low-entropy credential succeeds instantly).
|
|
94
|
+
|
|
95
|
+
The threshold of ~4.5 bits/char is empirically calibrated:
|
|
96
|
+
|
|
97
|
+
- English text: ~3.5
|
|
98
|
+
- Base64: ~6.0
|
|
99
|
+
- Hex: ~4.0
|
|
100
|
+
- Random 32-char: ~5.0+
|
|
101
|
+
|
|
102
|
+
Above 4.5 in a field labeled `key:`, `token:`, `secret:`,
|
|
103
|
+
`password=` is a strong signal. Below 4.5 is usually English /
|
|
104
|
+
placeholder / template variable.
|
|
105
|
+
|
|
106
|
+
False positives:
|
|
107
|
+
|
|
108
|
+
- High-entropy hashes (commit SHAs, content hashes) appearing in a
|
|
109
|
+
field labeled `key:` (e.g., `cache_key: a3b9...`). Use context to
|
|
110
|
+
filter.
|
|
111
|
+
- Long base64-encoded test fixtures (PDF content, certificate
|
|
112
|
+
blobs). The entropy check passes; the human-verification step
|
|
113
|
+
rejects.
|
|
114
|
+
|
|
115
|
+
The skill emits these as MEDIUM, not CRITICAL, with explicit
|
|
116
|
+
"requires verification" framing.
|
|
117
|
+
|
|
118
|
+
## History-scrub decision framework
|
|
119
|
+
|
|
120
|
+
After finding a leaked credential in source:
|
|
121
|
+
|
|
122
|
+
**Always rotate the credential.** Non-negotiable.
|
|
123
|
+
|
|
124
|
+
**History scrub depends on these inputs:**
|
|
125
|
+
|
|
126
|
+
1. **Is the repo public?** Yes → scrub is roughly futile. Anyone
|
|
127
|
+
who cloned has the history; mirror sites cache it. Public repo
|
|
128
|
+
= "publish a forever-archive of every commit ever made."
|
|
129
|
+
2. **Is the credential still in the file's current state?** Yes →
|
|
130
|
+
scrub removes the live exposure. No (file's been fixed) → history
|
|
131
|
+
is the only remaining exposure surface.
|
|
132
|
+
3. **Are clones controlled?** Yes (private repo, employees only) →
|
|
133
|
+
force-push + force-pull on every clone is feasible. No → don't
|
|
134
|
+
bother.
|
|
135
|
+
4. **How long has the credential been in history?** Days → scrub
|
|
136
|
+
may catch unindexed copies. Months → assume copies exist
|
|
137
|
+
permanently.
|
|
138
|
+
|
|
139
|
+
**Pragmatic default:** rotate, fix the current state, don't scrub
|
|
140
|
+
history. The credential is dead either way; the historical
|
|
141
|
+
disclosure of "we leaked something at this point in time" is mostly
|
|
142
|
+
narrative, not technical risk, once the credential is rotated.
|
|
143
|
+
|
|
144
|
+
**Exception:** if the credential CAN'T be rotated (e.g., it's
|
|
145
|
+
embedded in a customer's deployed binary), scrub becomes the only
|
|
146
|
+
remediation. Plan accordingly.
|
|
147
|
+
|
|
148
|
+
## Why test directories are excluded by default
|
|
149
|
+
|
|
150
|
+
Test fixtures often contain credential-shaped strings that ARE
|
|
151
|
+
placeholders:
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
# tests/fixtures/auth.py
|
|
155
|
+
TEST_API_KEY = "ghp_FAKE0000000000000000000000000000000000"
|
|
156
|
+
TEST_AWS_KEY = "AKIATESTKEY12345678"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
These match the regex but are deliberately fake. Scanning tests by
|
|
160
|
+
default produces high false-positive rates that train operators to
|
|
161
|
+
ignore findings.
|
|
162
|
+
|
|
163
|
+
The `--include-tests` flag is for the audit case: when reviewing an
|
|
164
|
+
inherited codebase, you DO want to know whether fixtures contain
|
|
165
|
+
real credentials someone forgot to redact. The flag is opt-in.
|
|
166
|
+
|
|
167
|
+
## Primary sources
|
|
168
|
+
|
|
169
|
+
- [CWE-798 Use of Hard-coded Credentials](https://cwe.mitre.org/data/definitions/798.html)
|
|
170
|
+
- [CWE-321 Use of Hard-coded Cryptographic Key](https://cwe.mitre.org/data/definitions/321.html)
|
|
171
|
+
- [OWASP A07:2021 Identification and Authentication Failures](https://owasp.org/Top10/A07_2021-Identification_and_Authentication_Failures/)
|
|
172
|
+
- [GitHub Secret Scanning patterns](https://docs.github.com/en/code-security/secret-scanning/secret-scanning-patterns)
|
|
173
|
+
- [AWS IAM access-key reference](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html)
|
|
174
|
+
- [Trufflehog detector list](https://github.com/trufflesecurity/trufflehog/tree/main/pkg/detectors)
|
|
175
|
+
- [Gitleaks rules](https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml)
|