shipready 1.5.0 → 1.5.1
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/README.md +14 -1
- package/dist/checks/secrets.js +11 -0
- package/dist/scanner.js +11 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -363,13 +363,26 @@ pnpm dev # run the CLI from source (tsx)
|
|
|
363
363
|
|
|
364
364
|
See [CONTRIBUTING.md](./CONTRIBUTING.md) for project structure and how to add new checks or secret patterns.
|
|
365
365
|
|
|
366
|
+
## How does it compare to gitleaks?
|
|
367
|
+
|
|
368
|
+
Measured head-to-head on a corpus of 7 real-format leaks and 10 false-positive baits (gitleaks v8.24.3):
|
|
369
|
+
|
|
370
|
+
| | shipready | gitleaks |
|
|
371
|
+
| --- | --- | --- |
|
|
372
|
+
| Real leaks caught | **7 / 7** | 4 / 7 |
|
|
373
|
+
| Error-level false positives | **0** | 1 |
|
|
374
|
+
| Newer key formats (`sk-proj-`, `sk-ant-`) | ✓ | missed |
|
|
375
|
+
| Committed `.env.backup` | ✓ | partial |
|
|
376
|
+
|
|
377
|
+
gitleaks remains more battle-tested for deep git-history forensics with hundreds of rules. shipready covers the leaks that actually happen in modern AI-assisted projects — plus README, env hygiene, and code hygiene that gitleaks doesn't check at all. Full methodology, fairness notes, and reproduction steps: [BENCHMARK.md](./BENCHMARK.md).
|
|
378
|
+
|
|
366
379
|
## Roadmap
|
|
367
380
|
|
|
368
381
|
- [x] `shipready staged` for pre-commit hooks
|
|
369
382
|
- [x] SARIF report output for GitHub code scanning
|
|
370
383
|
- [x] Git history scanning (`--history`)
|
|
371
384
|
- [x] Live key verification (`--verify`)
|
|
372
|
-
- [
|
|
385
|
+
- [x] Benchmark against gitleaks with published numbers ([BENCHMARK.md](./BENCHMARK.md))
|
|
373
386
|
- [ ] Framework-aware scoring (Next.js vs Vite vs Python have different needs)
|
|
374
387
|
- [ ] Optional AI-powered fix suggestions (bring your own key)
|
|
375
388
|
|
package/dist/checks/secrets.js
CHANGED
|
@@ -232,6 +232,17 @@ const PATTERNS = [
|
|
|
232
232
|
group: 1,
|
|
233
233
|
minEntropy: 3.8,
|
|
234
234
|
},
|
|
235
|
+
{
|
|
236
|
+
// Unquoted dotenv-style assignment (KEY=value at line start). Catches
|
|
237
|
+
// committed .env backups where values carry no quotes - a gap found by
|
|
238
|
+
// benchmarking against gitleaks. Anchored to line start so ordinary
|
|
239
|
+
// code assignments (always quoted) don't double-match.
|
|
240
|
+
kind: "Hardcoded credential",
|
|
241
|
+
re: /^[A-Z0-9_]*(?:API_?KEY|SECRET(?:_KEY)?|ACCESS_TOKEN|AUTH_TOKEN|PASSWORD|PASSWD|CREDENTIALS?)[A-Z0-9_]*=([A-Za-z0-9+/_.=-]{16,})\s*$/,
|
|
242
|
+
confidence: "medium",
|
|
243
|
+
group: 1,
|
|
244
|
+
minEntropy: 3.8,
|
|
245
|
+
},
|
|
235
246
|
];
|
|
236
247
|
/** Words and shapes that indicate a placeholder, not a real secret. */
|
|
237
248
|
const PLACEHOLDER_VALUE_RE = /your[-_ ]?|example|sample|placeholder|change[-_ ]?me|dummy|fake|insert|replace|redacted|deadbeef|lorem|goes[-_ ]?here|test[-_ ]?key|x{4,}|\*{3,}|\.\.\.|123456|abcdef/i;
|
package/dist/scanner.js
CHANGED
|
@@ -42,11 +42,18 @@ export function scanFiles(root, files, secretAllowlist = []) {
|
|
|
42
42
|
* an example is just as dangerous - but hygiene noise is skipped.
|
|
43
43
|
*/
|
|
44
44
|
const HYGIENE_EXEMPT_RE = /(?:^|\/)(?:examples?|demos?|benchmarks?|scripts?|playground)\//;
|
|
45
|
+
/**
|
|
46
|
+
* Standard env file names that legitimately hold real values and are
|
|
47
|
+
* expected to be gitignored - the env check owns those. Anything else
|
|
48
|
+
* that merely starts with ".env" (.env.backup, .env.old, .env.prod)
|
|
49
|
+
* is a copy someone made and MUST be scanned: benchmark testing showed
|
|
50
|
+
* gitleaks catches committed .env backups while we used to skip them.
|
|
51
|
+
*/
|
|
52
|
+
const STANDARD_ENV_RE = /^\.env(?:\.(?:local|development|production|test|staging))?(?:\.local)?$/;
|
|
45
53
|
for (const file of files) {
|
|
46
54
|
const base = path.basename(file);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const isEnvFile = base.startsWith(".env");
|
|
55
|
+
const isStandardEnv = STANDARD_ENV_RE.test(base);
|
|
56
|
+
const isEnvTemplate = base === ".env.example" || base === ".env.sample" || base === ".env.template";
|
|
50
57
|
const ext = path.extname(file).toLowerCase();
|
|
51
58
|
const isCode = CODE_ONLY.has(ext);
|
|
52
59
|
if (isProbablyBinary(root, file))
|
|
@@ -60,7 +67,7 @@ export function scanFiles(root, files, secretAllowlist = []) {
|
|
|
60
67
|
todos.push(...scanContentForTodos(content, file));
|
|
61
68
|
}
|
|
62
69
|
}
|
|
63
|
-
if (!
|
|
70
|
+
if (!isStandardEnv && !isEnvTemplate) {
|
|
64
71
|
secrets.push(...scanContentForSecrets(content, file, secretAllowlist));
|
|
65
72
|
}
|
|
66
73
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shipready",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "Pre-flight check for your repo before shipping. Scans AI-coded projects for secrets, env issues, debug leftovers, and generates AI-agent instruction files.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|