openpitstop 1.0.0
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/LICENSE +21 -0
- package/README.md +512 -0
- package/demo-repo/README.md +24 -0
- package/demo-repo/package-lock.json +4483 -0
- package/demo-repo/package.json +17 -0
- package/demo-repo/src/config.js +5 -0
- package/demo-repo/src/db.js +31 -0
- package/demo-repo/src/server.js +13 -0
- package/demo-repo/src/userController.js +22 -0
- package/demo-repo/src/userRepo.js +18 -0
- package/demo-repo/src/userService.js +59 -0
- package/demo-repo/test/user.test.js +12 -0
- package/demo-repo-fintech/README.md +27 -0
- package/demo-repo-fintech/package-lock.json +1023 -0
- package/demo-repo-fintech/package.json +14 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-conc-1-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-retry-2-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/pitstop-repro-ledger-order-ord-webhook-3-charged-2.test.mjs +175 -0
- package/demo-repo-fintech/src/gateway.js +26 -0
- package/demo-repo-fintech/src/server.js +61 -0
- package/demo-repo-generators/package-lock.json +1508 -0
- package/demo-repo-generators/package.json +17 -0
- package/demo-repo-generators/public/index.html +10 -0
- package/demo-repo-generators/scripts/build.js +17 -0
- package/demo-repo-generators/src/counter.js +16 -0
- package/demo-repo-integrity/README.md +49 -0
- package/demo-repo-integrity/package.json +12 -0
- package/demo-repo-integrity/src/money.js +19 -0
- package/demo-repo-integrity/test/money.test.js +17 -0
- package/dist/analyzers/accessibility.d.ts +18 -0
- package/dist/analyzers/accessibility.js +248 -0
- package/dist/analyzers/accessibility.js.map +1 -0
- package/dist/analyzers/dependencyGraph.d.ts +14 -0
- package/dist/analyzers/dependencyGraph.js +206 -0
- package/dist/analyzers/dependencyGraph.js.map +1 -0
- package/dist/analyzers/devex.d.ts +19 -0
- package/dist/analyzers/devex.js +301 -0
- package/dist/analyzers/devex.js.map +1 -0
- package/dist/analyzers/duplication.d.ts +2 -0
- package/dist/analyzers/duplication.js +47 -0
- package/dist/analyzers/duplication.js.map +1 -0
- package/dist/analyzers/index.d.ts +12 -0
- package/dist/analyzers/index.js +38 -0
- package/dist/analyzers/index.js.map +1 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.d.ts +2 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.js +164 -0
- package/dist/analyzers/integrity/assertionLiteralTamper.js.map +1 -0
- package/dist/analyzers/integrity/exceptionSwallow.d.ts +2 -0
- package/dist/analyzers/integrity/exceptionSwallow.js +198 -0
- package/dist/analyzers/integrity/exceptionSwallow.js.map +1 -0
- package/dist/analyzers/integrity/exitCheat.d.ts +2 -0
- package/dist/analyzers/integrity/exitCheat.js +31 -0
- package/dist/analyzers/integrity/exitCheat.js.map +1 -0
- package/dist/analyzers/integrity/git.d.ts +10 -0
- package/dist/analyzers/integrity/git.js +181 -0
- package/dist/analyzers/integrity/git.js.map +1 -0
- package/dist/analyzers/integrity/hardcodedMatch.d.ts +2 -0
- package/dist/analyzers/integrity/hardcodedMatch.js +93 -0
- package/dist/analyzers/integrity/hardcodedMatch.js.map +1 -0
- package/dist/analyzers/integrity/helpers.d.ts +21 -0
- package/dist/analyzers/integrity/helpers.js +66 -0
- package/dist/analyzers/integrity/helpers.js.map +1 -0
- package/dist/analyzers/integrity/index.d.ts +10 -0
- package/dist/analyzers/integrity/index.js +27 -0
- package/dist/analyzers/integrity/index.js.map +1 -0
- package/dist/analyzers/integrity/mockOverreach.d.ts +2 -0
- package/dist/analyzers/integrity/mockOverreach.js +72 -0
- package/dist/analyzers/integrity/mockOverreach.js.map +1 -0
- package/dist/analyzers/integrity/suppressionCreep.d.ts +2 -0
- package/dist/analyzers/integrity/suppressionCreep.js +27 -0
- package/dist/analyzers/integrity/suppressionCreep.js.map +1 -0
- package/dist/analyzers/integrity/testTamper.d.ts +2 -0
- package/dist/analyzers/integrity/testTamper.js +153 -0
- package/dist/analyzers/integrity/testTamper.js.map +1 -0
- package/dist/analyzers/integrity/types.d.ts +63 -0
- package/dist/analyzers/integrity/types.js +13 -0
- package/dist/analyzers/integrity/types.js.map +1 -0
- package/dist/analyzers/ledger/attacks.d.ts +17 -0
- package/dist/analyzers/ledger/attacks.js +135 -0
- package/dist/analyzers/ledger/attacks.js.map +1 -0
- package/dist/analyzers/ledger/discover.d.ts +15 -0
- package/dist/analyzers/ledger/discover.js +122 -0
- package/dist/analyzers/ledger/discover.js.map +1 -0
- package/dist/analyzers/ledger/evidence.d.ts +22 -0
- package/dist/analyzers/ledger/evidence.js +127 -0
- package/dist/analyzers/ledger/evidence.js.map +1 -0
- package/dist/analyzers/ledger/harness.d.ts +4 -0
- package/dist/analyzers/ledger/harness.js +270 -0
- package/dist/analyzers/ledger/harness.js.map +1 -0
- package/dist/analyzers/ledger/index.d.ts +14 -0
- package/dist/analyzers/ledger/index.js +116 -0
- package/dist/analyzers/ledger/index.js.map +1 -0
- package/dist/analyzers/ledger/types.d.ts +31 -0
- package/dist/analyzers/ledger/types.js +2 -0
- package/dist/analyzers/ledger/types.js.map +1 -0
- package/dist/analyzers/perf.d.ts +2 -0
- package/dist/analyzers/perf.js +106 -0
- package/dist/analyzers/perf.js.map +1 -0
- package/dist/analyzers/reliability.d.ts +22 -0
- package/dist/analyzers/reliability.js +339 -0
- package/dist/analyzers/reliability.js.map +1 -0
- package/dist/analyzers/routes.d.ts +17 -0
- package/dist/analyzers/routes.js +115 -0
- package/dist/analyzers/routes.js.map +1 -0
- package/dist/analyzers/security.d.ts +2 -0
- package/dist/analyzers/security.js +302 -0
- package/dist/analyzers/security.js.map +1 -0
- package/dist/analyzers/suiteRunner.d.ts +64 -0
- package/dist/analyzers/suiteRunner.js +381 -0
- package/dist/analyzers/suiteRunner.js.map +1 -0
- package/dist/analyzers/tests.d.ts +2 -0
- package/dist/analyzers/tests.js +215 -0
- package/dist/analyzers/tests.js.map +1 -0
- package/dist/analyzers/types.d.ts +203 -0
- package/dist/analyzers/types.js +2 -0
- package/dist/analyzers/types.js.map +1 -0
- package/dist/analyzers/util.d.ts +35 -0
- package/dist/analyzers/util.js +209 -0
- package/dist/analyzers/util.js.map +1 -0
- package/dist/ciCheckA.d.ts +2 -0
- package/dist/ciCheckA.js +5 -0
- package/dist/ciCheckA.js.map +1 -0
- package/dist/ciCheckB.d.ts +2 -0
- package/dist/ciCheckB.js +6 -0
- package/dist/ciCheckB.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +65 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/budget.d.ts +33 -0
- package/dist/commands/budget.js +108 -0
- package/dist/commands/budget.js.map +1 -0
- package/dist/commands/ci.d.ts +14 -0
- package/dist/commands/ci.js +157 -0
- package/dist/commands/ci.js.map +1 -0
- package/dist/commands/demo.d.ts +2 -0
- package/dist/commands/demo.js +163 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/digest.d.ts +8 -0
- package/dist/commands/digest.js +189 -0
- package/dist/commands/digest.js.map +1 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +98 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/drive.d.ts +3 -0
- package/dist/commands/drive.js +170 -0
- package/dist/commands/drive.js.map +1 -0
- package/dist/commands/gate.d.ts +2 -0
- package/dist/commands/gate.js +136 -0
- package/dist/commands/gate.js.map +1 -0
- package/dist/commands/honesty.d.ts +2 -0
- package/dist/commands/honesty.js +182 -0
- package/dist/commands/honesty.js.map +1 -0
- package/dist/commands/inspect.d.ts +2 -0
- package/dist/commands/inspect.js +244 -0
- package/dist/commands/inspect.js.map +1 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +81 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/integrity.d.ts +2 -0
- package/dist/commands/integrity.js +74 -0
- package/dist/commands/integrity.js.map +1 -0
- package/dist/commands/memory.d.ts +6 -0
- package/dist/commands/memory.js +90 -0
- package/dist/commands/memory.js.map +1 -0
- package/dist/commands/pen.d.ts +17 -0
- package/dist/commands/pen.js +158 -0
- package/dist/commands/pen.js.map +1 -0
- package/dist/commands/prompt.d.ts +2 -0
- package/dist/commands/prompt.js +37 -0
- package/dist/commands/prompt.js.map +1 -0
- package/dist/commands/readyCheck.d.ts +21 -0
- package/dist/commands/readyCheck.js +68 -0
- package/dist/commands/readyCheck.js.map +1 -0
- package/dist/commands/report.d.ts +2 -0
- package/dist/commands/report.js +48 -0
- package/dist/commands/report.js.map +1 -0
- package/dist/commands/repro.d.ts +3 -0
- package/dist/commands/repro.js +61 -0
- package/dist/commands/repro.js.map +1 -0
- package/dist/commands/scan.d.ts +27 -0
- package/dist/commands/scan.js +348 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/share.d.ts +2 -0
- package/dist/commands/share.js +163 -0
- package/dist/commands/share.js.map +1 -0
- package/dist/commands/trends.d.ts +2 -0
- package/dist/commands/trends.js +103 -0
- package/dist/commands/trends.js.map +1 -0
- package/dist/commands/try.d.ts +12 -0
- package/dist/commands/try.js +208 -0
- package/dist/commands/try.js.map +1 -0
- package/dist/commands/verify.d.ts +44 -0
- package/dist/commands/verify.js +381 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/commands/watch.d.ts +10 -0
- package/dist/commands/watch.js +70 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/evidence.d.ts +37 -0
- package/dist/evidence.js +61 -0
- package/dist/evidence.js.map +1 -0
- package/dist/graph/correlate.d.ts +7 -0
- package/dist/graph/correlate.js +246 -0
- package/dist/graph/correlate.js.map +1 -0
- package/dist/graph/integrity.d.ts +22 -0
- package/dist/graph/integrity.js +39 -0
- package/dist/graph/integrity.js.map +1 -0
- package/dist/installer/targets.d.ts +23 -0
- package/dist/installer/targets.js +162 -0
- package/dist/installer/targets.js.map +1 -0
- package/dist/memory/store.d.ts +33 -0
- package/dist/memory/store.js +131 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/pen/dynamic.d.ts +36 -0
- package/dist/pen/dynamic.js +622 -0
- package/dist/pen/dynamic.js.map +1 -0
- package/dist/pen/fix.d.ts +35 -0
- package/dist/pen/fix.js +239 -0
- package/dist/pen/fix.js.map +1 -0
- package/dist/pen/proof.d.ts +46 -0
- package/dist/pen/proof.js +112 -0
- package/dist/pen/proof.js.map +1 -0
- package/dist/pen/report.d.ts +11 -0
- package/dist/pen/report.js +258 -0
- package/dist/pen/report.js.map +1 -0
- package/dist/pen/static.d.ts +17 -0
- package/dist/pen/static.js +565 -0
- package/dist/pen/static.js.map +1 -0
- package/dist/pen/store.d.ts +11 -0
- package/dist/pen/store.js +36 -0
- package/dist/pen/store.js.map +1 -0
- package/dist/pen/types.d.ts +112 -0
- package/dist/pen/types.js +53 -0
- package/dist/pen/types.js.map +1 -0
- package/dist/report/format.d.ts +126 -0
- package/dist/report/format.js +706 -0
- package/dist/report/format.js.map +1 -0
- package/dist/report/score.d.ts +47 -0
- package/dist/report/score.js +164 -0
- package/dist/report/score.js.map +1 -0
- package/dist/repro/framework.d.ts +26 -0
- package/dist/repro/framework.js +224 -0
- package/dist/repro/framework.js.map +1 -0
- package/dist/repro/generate.d.ts +14 -0
- package/dist/repro/generate.js +669 -0
- package/dist/repro/generate.js.map +1 -0
- package/dist/repro/ids.d.ts +32 -0
- package/dist/repro/ids.js +175 -0
- package/dist/repro/ids.js.map +1 -0
- package/dist/repro/index.d.ts +24 -0
- package/dist/repro/index.js +69 -0
- package/dist/repro/index.js.map +1 -0
- package/dist/repro/pen.d.ts +26 -0
- package/dist/repro/pen.js +321 -0
- package/dist/repro/pen.js.map +1 -0
- package/dist/repro/run.d.ts +18 -0
- package/dist/repro/run.js +36 -0
- package/dist/repro/run.js.map +1 -0
- package/dist/sandbox/proxy.d.ts +41 -0
- package/dist/sandbox/proxy.js +231 -0
- package/dist/sandbox/proxy.js.map +1 -0
- package/dist/sandbox/startCmd.d.ts +24 -0
- package/dist/sandbox/startCmd.js +71 -0
- package/dist/sandbox/startCmd.js.map +1 -0
- package/dist/ui/spinner.d.ts +18 -0
- package/dist/ui/spinner.js +26 -0
- package/dist/ui/spinner.js.map +1 -0
- package/dist/verify/metrics.d.ts +13 -0
- package/dist/verify/metrics.js +72 -0
- package/dist/verify/metrics.js.map +1 -0
- package/package.json +68 -0
- package/scripts/cheat-demo.cjs +102 -0
- package/templates/ledger/preload.cjs +331 -0
- package/templates/pen/preload.cjs +196 -0
- package/templates/pitstop.prompt.md +371 -0
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pen/static.ts — the static phase of `pitstop pen`.
|
|
3
|
+
*
|
|
4
|
+
* Cheap, dependency-free, honest heuristics. Every finding is labelled with
|
|
5
|
+
* its confidence: `heuristic` (pattern matched, no proof yet), `indicated`
|
|
6
|
+
* (the data flow is concrete: user input reaching a sink). Nothing is ever
|
|
7
|
+
* labelled `proven` here — proof is the dynamic phase's job.
|
|
8
|
+
*/
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { walkFiles } from "../analyzers/util.js";
|
|
12
|
+
import { findRoutesInFile, ROUTE_EXTS } from "../analyzers/routes.js";
|
|
13
|
+
import { findingIdFor } from "../repro/ids.js";
|
|
14
|
+
const JS_EXTS = [".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
15
|
+
const PY_EXTS = [".py"];
|
|
16
|
+
const SECRET_PATTERNS = [
|
|
17
|
+
{
|
|
18
|
+
type: "hardcoded-secret",
|
|
19
|
+
severity: "critical",
|
|
20
|
+
re: /\bAKIA[0-9A-Z]{16}\b/,
|
|
21
|
+
name: "AWS access key",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
type: "hardcoded-secret",
|
|
25
|
+
severity: "critical",
|
|
26
|
+
re: /\bgh[pousr]_[A-Za-z0-9]{20,}\b/,
|
|
27
|
+
name: "GitHub token",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: "hardcoded-secret",
|
|
31
|
+
severity: "high",
|
|
32
|
+
re: /\bsk_(live|prod)_[A-Za-z0-9]{16,}\b/,
|
|
33
|
+
name: "live Stripe secret key",
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: "hardcoded-secret",
|
|
37
|
+
severity: "high",
|
|
38
|
+
re: /\brzp_live_[A-Za-z0-9]{10,}\b|\brz_live_[A-Za-z0-9]{10,}\b/,
|
|
39
|
+
name: "live Razorpay key",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
type: "hardcoded-secret",
|
|
43
|
+
severity: "high",
|
|
44
|
+
re: /\bAIza[0-9A-Za-z_-]{35}\b/,
|
|
45
|
+
name: "Google API key",
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "hardcoded-secret",
|
|
49
|
+
severity: "high",
|
|
50
|
+
re: /\bxox[baprs]-[0-9A-Za-z-]{10,}\b/,
|
|
51
|
+
name: "Slack token",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: "hardcoded-secret",
|
|
55
|
+
severity: "critical",
|
|
56
|
+
re: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
|
|
57
|
+
name: "private key block",
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
/** Values that are clearly placeholders — never flag these. */
|
|
61
|
+
const PLACEHOLDER_RE = /^(test|example|changeme|change-me|dummy|fake|placeholder|your|secret|password|token|xxx+|\.\.\.|none|null|undefined)$/i;
|
|
62
|
+
const TEST_CRED_RE = /(test|demo|sample|example|fake|pitstop|mock)/i;
|
|
63
|
+
function scanSecrets(repo) {
|
|
64
|
+
const hits = [];
|
|
65
|
+
const files = walkFiles(repo, [...JS_EXTS, ...PY_EXTS, ".env", ".env.production", ".env.local", ".env.example"].filter((e, i, a) => a.indexOf(e) === i));
|
|
66
|
+
for (const file of files) {
|
|
67
|
+
let content;
|
|
68
|
+
try {
|
|
69
|
+
content = fs.readFileSync(file, "utf8");
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
// Strip comments so placeholders in docs/tests don't scream.
|
|
75
|
+
const lines = content.split(/\r?\n/);
|
|
76
|
+
lines.forEach((raw, i) => {
|
|
77
|
+
const line = raw.trim();
|
|
78
|
+
if (!line || line.startsWith("#") || line.startsWith("//") || line.startsWith("*"))
|
|
79
|
+
return;
|
|
80
|
+
for (const p of SECRET_PATTERNS) {
|
|
81
|
+
const m = line.match(p.re);
|
|
82
|
+
if (!m)
|
|
83
|
+
continue;
|
|
84
|
+
const value = m[0];
|
|
85
|
+
if (p.type === "hardcoded-secret" && /(?:password|passwd|secret|api[_-]?key|token)\s*[:=]\s*["']?([^"'\s,;)]+)/i.test(line)) {
|
|
86
|
+
const v = (line.match(/(?:password|passwd|secret|api[_-]?key|token)\s*[:=]\s*["']?([^"'\s,;)]+)/i) || [])[1] || "";
|
|
87
|
+
if (PLACEHOLDER_RE.test(v) || TEST_CRED_RE.test(v))
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
hits.push({ file, line: i + 1, pattern: p, value });
|
|
91
|
+
break; // one secret per line is enough
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return hits;
|
|
96
|
+
}
|
|
97
|
+
/** Generic `SOMETHING_KEY = "value"` where value is clearly real (high entropy-ish). */
|
|
98
|
+
const GENERIC_KEY_RE = /\b(?:[A-Z][A-Z0-9_]{3,})?(?:PASSWORD|PASSWD|SECRET|API_?KEY|TOKEN|PRIVATE_?KEY)\s*[:=]\s*["']([^"'\s]{12,})["']/i;
|
|
99
|
+
function scanGenericKeys(repo) {
|
|
100
|
+
const hits = [];
|
|
101
|
+
const files = walkFiles(repo, [...JS_EXTS, ...PY_EXTS, ".env", ".env.production", ".env.local", ".env.example"].filter((e, i, a) => a.indexOf(e) === i));
|
|
102
|
+
for (const file of files) {
|
|
103
|
+
let content;
|
|
104
|
+
try {
|
|
105
|
+
content = fs.readFileSync(file, "utf8");
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
const lines = content.split(/\r?\n/);
|
|
111
|
+
for (let i = 0; i < lines.length; i++) {
|
|
112
|
+
const line = lines[i].trim();
|
|
113
|
+
if (!line || line.startsWith("#") || line.startsWith("//"))
|
|
114
|
+
continue;
|
|
115
|
+
const m = line.match(GENERIC_KEY_RE);
|
|
116
|
+
if (!m)
|
|
117
|
+
continue;
|
|
118
|
+
const v = m[1];
|
|
119
|
+
if (PLACEHOLDER_RE.test(v) || TEST_CRED_RE.test(v))
|
|
120
|
+
continue;
|
|
121
|
+
// Entropy-ish guard: real secrets are rarely a repeated char or a word.
|
|
122
|
+
if (/^([a-z0-9])\1{10,}$/i.test(v))
|
|
123
|
+
continue;
|
|
124
|
+
hits.push({
|
|
125
|
+
file,
|
|
126
|
+
line: i + 1,
|
|
127
|
+
pattern: {
|
|
128
|
+
type: "hardcoded-secret",
|
|
129
|
+
severity: "high",
|
|
130
|
+
re: GENERIC_KEY_RE,
|
|
131
|
+
name: "hardcoded credential",
|
|
132
|
+
},
|
|
133
|
+
value: v,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return hits;
|
|
138
|
+
}
|
|
139
|
+
/* ------------------------------------------------------------------ */
|
|
140
|
+
/* 2. Route inventory (static, for the dynamic phase) */
|
|
141
|
+
/* ------------------------------------------------------------------ */
|
|
142
|
+
const SENSITIVE_WORDS = [
|
|
143
|
+
"admin",
|
|
144
|
+
"dashboard",
|
|
145
|
+
"private",
|
|
146
|
+
"account",
|
|
147
|
+
"profile",
|
|
148
|
+
"settings",
|
|
149
|
+
"order",
|
|
150
|
+
"charge",
|
|
151
|
+
"payment",
|
|
152
|
+
"refund",
|
|
153
|
+
"transfer",
|
|
154
|
+
"webhook",
|
|
155
|
+
"secret",
|
|
156
|
+
"token",
|
|
157
|
+
"otp",
|
|
158
|
+
"verify",
|
|
159
|
+
"user",
|
|
160
|
+
"users",
|
|
161
|
+
"auth",
|
|
162
|
+
"login",
|
|
163
|
+
"signin",
|
|
164
|
+
"session",
|
|
165
|
+
];
|
|
166
|
+
const LOGIN_WORDS = ["login", "signin", "auth", "otp", "verify", "token", "password", "forgot"];
|
|
167
|
+
export function findRoutes(repo) {
|
|
168
|
+
const out = [];
|
|
169
|
+
const add = (file, method, p, line) => {
|
|
170
|
+
const lower = p.toLowerCase();
|
|
171
|
+
out.push({
|
|
172
|
+
method: method.toUpperCase(),
|
|
173
|
+
path: p,
|
|
174
|
+
file,
|
|
175
|
+
line,
|
|
176
|
+
sensitive: SENSITIVE_WORDS.some((w) => lower.includes(w)),
|
|
177
|
+
loginLike: LOGIN_WORDS.some((w) => lower.includes(w)),
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
for (const file of walkFiles(repo, ROUTE_EXTS)) {
|
|
181
|
+
let content;
|
|
182
|
+
try {
|
|
183
|
+
content = fs.readFileSync(file, "utf8");
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const ext = path.extname(file).toLowerCase();
|
|
189
|
+
for (const r of findRoutesInFile(content, ext))
|
|
190
|
+
add(file, r.method, r.path, r.line);
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
194
|
+
const STATIC_RULES = [
|
|
195
|
+
{
|
|
196
|
+
type: "insecure-cors",
|
|
197
|
+
severity: "high",
|
|
198
|
+
confidence: "indicated",
|
|
199
|
+
title: "CORS allows any origin",
|
|
200
|
+
describe: () => "CORS is wide open — either a bare `cors()` (defaults to origin: * with credentials off) or an explicit `origin: \"*\"`. Any website can read the responses of this endpoint from a victim's browser.",
|
|
201
|
+
fix: "Restrict origins to an explicit allowlist: `cors({ origin: [/^https?:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?$/, ...] })` — or better, list only your real production origins and never combine `origin: *` with credentials.",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
type: "missing-security-headers",
|
|
205
|
+
severity: "medium",
|
|
206
|
+
confidence: "indicated",
|
|
207
|
+
title: "no helmet / security headers middleware",
|
|
208
|
+
describe: () => "An Express app without helmet() (or manual CSP / X-Frame-Options / X-Content-Type-Options headers). Clickjacking, MIME sniffing and mixed-content protections are all off.",
|
|
209
|
+
fix: "Install helmet and `app.use(helmet());` after app creation (deterministic patch generated by `pitstop pen --fix` when helmet is already installed).",
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
type: "cookie-without-httponly",
|
|
213
|
+
severity: "medium",
|
|
214
|
+
confidence: "indicated",
|
|
215
|
+
title: "cookie set without httpOnly",
|
|
216
|
+
describe: () => "A session/security cookie is being set without `httpOnly` (and likely without `secure`), so an XSS in any page can steal it via document.cookie.",
|
|
217
|
+
fix: "Set `{ httpOnly: true, secure: true, sameSite: \"strict\" }` on every session/auth cookie.",
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
type: "no-rate-limit",
|
|
221
|
+
severity: "medium",
|
|
222
|
+
confidence: "heuristic",
|
|
223
|
+
title: "no rate limiting on authentication paths",
|
|
224
|
+
describe: () => "express-rate-limit is not installed and login-ish routes exist — credential stuffing / brute force is unthrottled. The dynamic phase verifies this live.",
|
|
225
|
+
fix: "Add express-rate-limit and apply a strict limiter to /login /verify /otp /token routes (e.g. 5 requests / 15 min per IP).",
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
type: "info-leak",
|
|
229
|
+
severity: "high",
|
|
230
|
+
confidence: "indicated",
|
|
231
|
+
title: "env secrets printed to console",
|
|
232
|
+
describe: () => "A console statement forwards process.env values (API keys, tokens, DB credentials) into stdout logs where they are one grep away from exfiltration.",
|
|
233
|
+
fix: "Remove the log, or redact secrets before logging; never log raw process.env.",
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
type: "arbitrary-code-execution",
|
|
237
|
+
severity: "high",
|
|
238
|
+
confidence: "heuristic",
|
|
239
|
+
title: "eval / new Function in application code",
|
|
240
|
+
describe: () => "`eval` or `new Function` turns attacker-influenced strings into executable code. It is never needed for this; replace with a lookup table or JSON.parse.",
|
|
241
|
+
fix: "Remove eval/new Function. If a template engine is genuinely required, use a sandboxed one (e.g. a compiled template with no global access).",
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
type: "sql-injection",
|
|
245
|
+
severity: "critical",
|
|
246
|
+
confidence: "indicated",
|
|
247
|
+
title: "SQL built by string interpolation",
|
|
248
|
+
describe: () => "A query string is assembled by concatenating or interpolating variables into SQL. Any route feeding these variables becomes a live injection point.",
|
|
249
|
+
fix: "Use parameterized queries (?, $1 placeholders) or an ORM. Never interpolate values into SQL text.",
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
type: "command-injection",
|
|
253
|
+
severity: "critical",
|
|
254
|
+
confidence: "indicated",
|
|
255
|
+
title: "child_process called with request-derived input",
|
|
256
|
+
describe: () => "exec/spawn/execFile is called with a value that can trace back to request data (query, params, body). If the shell interprets it, that is remote code execution.",
|
|
257
|
+
fix: "Do not shell out with user input. If unavoidable: execFile with a fixed command, no shell, and validate input against a strict allowlist (e.g. /^[a-z0-9-]{1,64}$/).",
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
type: "path-traversal",
|
|
261
|
+
severity: "high",
|
|
262
|
+
confidence: "indicated",
|
|
263
|
+
title: "filesystem access with request-derived paths",
|
|
264
|
+
describe: () => "fs.readFile/readdir/createReadStream receives a path built from request data — ../ sequences can read or enumerate anything the process can.",
|
|
265
|
+
fix: "Resolve the path and verify it stays inside a root directory: `path.resolve(root, name)` then check `startsWith(root + path.sep)`; reject anything that escapes.",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
type: "ssrf",
|
|
269
|
+
severity: "high",
|
|
270
|
+
confidence: "indicated",
|
|
271
|
+
title: "outbound HTTP built from request-derived URLs",
|
|
272
|
+
describe: () => "fetch/http/axios/got is called with a URL that can trace back to request data — the server can be coerced into attacking internal hosts (metadata endpoints, admin panels).",
|
|
273
|
+
fix: "Allow only an explicit allowlist of hosts; forbid private ranges (127.0.0.0/8, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 169.254.169.254); resolve and re-check the IP before sending.",
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
type: "xss-sink",
|
|
277
|
+
severity: "high",
|
|
278
|
+
confidence: "indicated",
|
|
279
|
+
title: "HTML sink with request-derived data",
|
|
280
|
+
describe: () => "dangerouslySetInnerHTML / v-html / innerHTML / document.write receives data that can trace back to request data — stored or reflected XSS.",
|
|
281
|
+
fix: "Never put user data into HTML strings; use text nodes / escaped interpolation. If it must be HTML, sanitize with DOMPurify server-side or an HTML escaper at the sink.",
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
type: "prototype-pollution",
|
|
285
|
+
severity: "high",
|
|
286
|
+
confidence: "indicated",
|
|
287
|
+
title: "object merge/assign of untrusted data",
|
|
288
|
+
describe: () => "A recursive merge/assign (or __proto__ handling) appears with untrusted input — the classic prototype-pollution gadget (admin bypass, RCE via sanitized options).",
|
|
289
|
+
fix: "Merge only plain, schema-known objects; reject keys __proto__ / constructor / prototype at the boundary before any merge.",
|
|
290
|
+
},
|
|
291
|
+
];
|
|
292
|
+
const TYPES_WITH_RE = new Map(STATIC_RULES.filter((r) => r.type !== "no-rate-limit").map((r) => [r.type, r]));
|
|
293
|
+
const PATTERNS = [
|
|
294
|
+
// SQL: template literal query with ${} or concatenated + with a variable.
|
|
295
|
+
{
|
|
296
|
+
type: "sql-injection",
|
|
297
|
+
re: /\b(?:query|execute|exec|raw|run|find|findOne|aggregate|executeSql|sql)\s*\(\s*(?:[`"])[^`"\n]*\$\{/i,
|
|
298
|
+
requiresUserInput: false,
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
type: "sql-injection",
|
|
302
|
+
re: /\b(?:query|execute|exec|raw|run)\s*\(\s*(?:'|")[^'"\n]*"\s*\+\s*(?!['"])[\w.[\]]+/i,
|
|
303
|
+
requiresUserInput: true,
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
type: "command-injection",
|
|
307
|
+
re: /\b(?:exec|execFile|spawn|spawnSync|execSync|execFileSync|fork)\s*\(\s*(?:[`"])[^`"\n]*\$\{/i,
|
|
308
|
+
requiresUserInput: false,
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
type: "command-injection",
|
|
312
|
+
re: /\b(?:exec|spawn)\s*\(\s*[`"'][^`"'\n]*[`"']\s*\+/i,
|
|
313
|
+
requiresUserInput: true,
|
|
314
|
+
},
|
|
315
|
+
{
|
|
316
|
+
type: "path-traversal",
|
|
317
|
+
re: /\b(?:readFileSync|readFile|readdir|readdirSync|createReadStream|writeFileSync|writeFile|existsSync|statSync)\s*\(\s*(?:[`"])[^`"\n]*\$\{/i,
|
|
318
|
+
requiresUserInput: false,
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
type: "path-traversal",
|
|
322
|
+
re: /\b(?:readFileSync|readFile|createReadStream|readdirSync|readdir)\s*\(\s*path\.join\s*\([^)]*(?:req\.|query|body|params|input|user|data)[^)]*\)/i,
|
|
323
|
+
requiresUserInput: true,
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
type: "ssrf",
|
|
327
|
+
re: /\b(?:fetch|axios|request|got|http|https)\s*\(\s*(?:[`"])[^`"\n]*\$\{(?:url|uri|target|link|image|src|webhook|callback)[^}]*\}/i,
|
|
328
|
+
requiresUserInput: false,
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
type: "ssrf",
|
|
332
|
+
re: /\b(?:fetch|axios|request|got)\s*\(\s*(?:req\.(?:body|query|params)|[a-z]+\.(?:url|uri|webhook|callback|image|src))\s*[),]/i,
|
|
333
|
+
requiresUserInput: true,
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
type: "xss-sink",
|
|
337
|
+
re: /\b(?:dangerouslySetInnerHTML|v-html)\b|document\.write\s*\(/i,
|
|
338
|
+
requiresUserInput: false,
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
type: "xss-sink",
|
|
342
|
+
re: /\binnerHTML\s*=\s*[^;]*(?:req\.|body\.|query\.|params\.|data\.|input)/i,
|
|
343
|
+
requiresUserInput: true,
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
type: "prototype-pollution",
|
|
347
|
+
re: /["']__proto__["']\s*[:=]\s*\{|Object\.assign\s*\([^)]*\b[a-z]+,[^)]*\)/i,
|
|
348
|
+
requiresUserInput: false,
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
type: "arbitrary-code-execution",
|
|
352
|
+
re: /\beval\s*\(|\bnew\s+Function\s*\(/i,
|
|
353
|
+
requiresUserInput: false,
|
|
354
|
+
},
|
|
355
|
+
];
|
|
356
|
+
const USER_INPUT_RE = /\b(?:req|request|ctx|context|body|query|params|headers|input|payload|user|data|arg|value|url|name|id|file|content|message)\b/i;
|
|
357
|
+
function hasUserInput(line) {
|
|
358
|
+
return USER_INPUT_RE.test(line);
|
|
359
|
+
}
|
|
360
|
+
/* ------------------------------------------------------------------ */
|
|
361
|
+
/* 4. Config hygiene rules (whole-file, dependency-aware) */
|
|
362
|
+
/* ------------------------------------------------------------------ */
|
|
363
|
+
function findAppLine(content) {
|
|
364
|
+
const lines = content.split(/\r?\n/);
|
|
365
|
+
for (let i = 0; i < lines.length; i++) {
|
|
366
|
+
if (/const\s+app\s*=\s*express\s*\(/.test(lines[i]) || /express\s*\(\s*\)/.test(lines[i])) {
|
|
367
|
+
return { line: i + 1, text: lines[i] };
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
export function analyzeStatic(repo) {
|
|
373
|
+
const findings = [];
|
|
374
|
+
const routes = findRoutes(repo);
|
|
375
|
+
const jsFiles = walkFiles(repo, JS_EXTS);
|
|
376
|
+
const pyFiles = walkFiles(repo, PY_EXTS);
|
|
377
|
+
const allFiles = [...jsFiles, ...pyFiles];
|
|
378
|
+
let pkg = {};
|
|
379
|
+
try {
|
|
380
|
+
pkg = JSON.parse(fs.readFileSync(path.join(repo, "package.json"), "utf8").replace(/^\uFEFF/, ""));
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
/* no package.json */
|
|
384
|
+
}
|
|
385
|
+
const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };
|
|
386
|
+
const packages = Object.keys(deps);
|
|
387
|
+
const hasExpress = packages.includes("express");
|
|
388
|
+
const framework = hasExpress
|
|
389
|
+
? "express"
|
|
390
|
+
: packages.length > 0
|
|
391
|
+
? "other"
|
|
392
|
+
: "none";
|
|
393
|
+
const push = (f) => {
|
|
394
|
+
findings.push({
|
|
395
|
+
id: findingIdFor("pen", f.type, f.file ?? f.route, f.title),
|
|
396
|
+
source: "pen-static",
|
|
397
|
+
...f,
|
|
398
|
+
});
|
|
399
|
+
};
|
|
400
|
+
// Secrets.
|
|
401
|
+
for (const hit of [...scanSecrets(repo), ...scanGenericKeys(repo)]) {
|
|
402
|
+
push({
|
|
403
|
+
type: hit.pattern.type,
|
|
404
|
+
severity: hit.pattern.severity,
|
|
405
|
+
confidence: "proven",
|
|
406
|
+
title: `${hit.pattern.name} committed to source`,
|
|
407
|
+
description: `${hit.pattern.name} found in ${path.relative(repo, hit.file).replace(/\\/g, "/")}:${hit.line} (${hit.value.slice(0, 12)}…). This credential is recoverable from git history and any CI log that runs the repo.`,
|
|
408
|
+
file: hit.file,
|
|
409
|
+
line: hit.line,
|
|
410
|
+
repro: `grep -n "${hit.value.slice(0, 12)}" "${path.relative(repo, hit.file).replace(/\\/g, "/")}"`,
|
|
411
|
+
fix: "Rotate the credential NOW (it may already be compromised), remove it from source, and move it to an environment variable / secret manager. Add the value to .gitignore'd .env and to a secrets policy so it never returns.",
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
// Per-file pattern rules.
|
|
415
|
+
const codeLines = [];
|
|
416
|
+
for (const file of allFiles) {
|
|
417
|
+
let content;
|
|
418
|
+
try {
|
|
419
|
+
content = fs.readFileSync(file, "utf8");
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
content.split(/\r?\n/).forEach((text, i) => {
|
|
425
|
+
const t = text.trim();
|
|
426
|
+
if (!t || t.startsWith("//") || t.startsWith("#") || t.startsWith("*"))
|
|
427
|
+
return;
|
|
428
|
+
codeLines.push({ file, line: i + 1, text: t });
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
const seenPattern = new Set();
|
|
432
|
+
for (const entry of PATTERNS) {
|
|
433
|
+
for (const { file, line, text } of codeLines) {
|
|
434
|
+
if (!entry.re.test(text))
|
|
435
|
+
continue;
|
|
436
|
+
if (entry.requiresUserInput && !hasUserInput(text))
|
|
437
|
+
continue;
|
|
438
|
+
if (entry.type === "arbitrary-code-execution" && /node_modules|\.test\.|spec\.|__tests__|dist\//.test(file))
|
|
439
|
+
continue;
|
|
440
|
+
const rule = TYPES_WITH_RE.get(entry.type);
|
|
441
|
+
if (!rule)
|
|
442
|
+
continue;
|
|
443
|
+
const key = `${entry.type}|${path.relative(repo, file)}|${line}`;
|
|
444
|
+
if (seenPattern.has(key))
|
|
445
|
+
continue;
|
|
446
|
+
seenPattern.add(key);
|
|
447
|
+
push({
|
|
448
|
+
type: rule.type,
|
|
449
|
+
severity: rule.severity,
|
|
450
|
+
confidence: rule.confidence,
|
|
451
|
+
title: rule.title,
|
|
452
|
+
description: rule.describe(file, line, entry.re.exec(text)) + ` (${path.relative(repo, file).replace(/\\/g, "/")}:${line})`,
|
|
453
|
+
file,
|
|
454
|
+
line,
|
|
455
|
+
repro: `open ${path.relative(repo, file).replace(/\\/g, "/")}:${line}`,
|
|
456
|
+
fix: rule.fix,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// Whole-file config hygiene (JS only, express-aware).
|
|
461
|
+
const seenFileRule = new Set();
|
|
462
|
+
for (const file of jsFiles) {
|
|
463
|
+
let content;
|
|
464
|
+
try {
|
|
465
|
+
content = fs.readFileSync(file, "utf8");
|
|
466
|
+
}
|
|
467
|
+
catch {
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
const c = content;
|
|
471
|
+
if (/cors\s*\(\s*\)|origin\s*:\s*["']\*["']/.test(c)) {
|
|
472
|
+
const line = findLineOf(c, /cors\s*\(\s*\)|origin\s*:\s*["']\*["']/);
|
|
473
|
+
const rule = TYPES_WITH_RE.get("insecure-cors");
|
|
474
|
+
const key = `insecure-cors|${file}`;
|
|
475
|
+
if (!seenFileRule.has(key)) {
|
|
476
|
+
seenFileRule.add(key);
|
|
477
|
+
push({
|
|
478
|
+
type: rule.type,
|
|
479
|
+
severity: rule.severity,
|
|
480
|
+
confidence: rule.confidence,
|
|
481
|
+
title: rule.title,
|
|
482
|
+
description: rule.describe(file, line, null),
|
|
483
|
+
file,
|
|
484
|
+
line,
|
|
485
|
+
fix: rule.fix,
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (hasExpress && !/\bhelmet\s*\(/.test(c) && !/\bapp\.use\s*\(\s*helmet/.test(c) && /\bexpress\s*\(/.test(c)) {
|
|
490
|
+
const line = findLineOf(c, /\bexpress\s*\(/);
|
|
491
|
+
const rule = TYPES_WITH_RE.get("missing-security-headers");
|
|
492
|
+
const key = `missing-security-headers|${file}`;
|
|
493
|
+
if (!seenFileRule.has(key)) {
|
|
494
|
+
seenFileRule.add(key);
|
|
495
|
+
push({
|
|
496
|
+
type: rule.type,
|
|
497
|
+
severity: rule.severity,
|
|
498
|
+
confidence: rule.confidence,
|
|
499
|
+
title: rule.title,
|
|
500
|
+
description: rule.describe(file, line, null),
|
|
501
|
+
file,
|
|
502
|
+
line,
|
|
503
|
+
fix: rule.fix,
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
if (/res\.cookie\s*\(/.test(c) && !/\bhttpOnly\s*:\s*true/.test(c)) {
|
|
508
|
+
const line = findLineOf(c, /res\.cookie\s*\(/);
|
|
509
|
+
const rule = TYPES_WITH_RE.get("cookie-without-httponly");
|
|
510
|
+
const key = `cookie-without-httponly|${file}`;
|
|
511
|
+
if (!seenFileRule.has(key)) {
|
|
512
|
+
seenFileRule.add(key);
|
|
513
|
+
push({
|
|
514
|
+
type: rule.type,
|
|
515
|
+
severity: rule.severity,
|
|
516
|
+
confidence: rule.confidence,
|
|
517
|
+
title: rule.title,
|
|
518
|
+
description: rule.describe(file, line, null),
|
|
519
|
+
file,
|
|
520
|
+
line,
|
|
521
|
+
fix: rule.fix,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (/console\.(?:log|debug|info)\([^)]*process\.env/.test(c)) {
|
|
526
|
+
const line = findLineOf(c, /console\.(?:log|debug|info)\([^)]*process\.env/);
|
|
527
|
+
const rule = TYPES_WITH_RE.get("info-leak");
|
|
528
|
+
const key = `info-leak|${file}`;
|
|
529
|
+
if (!seenFileRule.has(key)) {
|
|
530
|
+
seenFileRule.add(key);
|
|
531
|
+
push({
|
|
532
|
+
type: rule.type,
|
|
533
|
+
severity: rule.severity,
|
|
534
|
+
confidence: rule.confidence,
|
|
535
|
+
title: rule.title,
|
|
536
|
+
description: rule.describe(file, line, null),
|
|
537
|
+
file,
|
|
538
|
+
line,
|
|
539
|
+
fix: rule.fix,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
// no-rate-limit: package-level rule.
|
|
545
|
+
if (!packages.includes("express-rate-limit") && routes.some((r) => r.loginLike)) {
|
|
546
|
+
const rule = STATIC_RULES.find((r) => r.type === "no-rate-limit");
|
|
547
|
+
push({
|
|
548
|
+
type: rule.type,
|
|
549
|
+
severity: rule.severity,
|
|
550
|
+
confidence: rule.confidence,
|
|
551
|
+
title: rule.title,
|
|
552
|
+
description: rule.describe(repo, 0, null) + ` Login-ish routes: ${routes.filter((r) => r.loginLike).slice(0, 3).map((r) => `${r.method} ${r.path}`).join(", ")}.`,
|
|
553
|
+
fix: rule.fix,
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
return { findings, routes, framework, packages };
|
|
557
|
+
}
|
|
558
|
+
function findLineOf(content, re) {
|
|
559
|
+
const lines = content.split(/\r?\n/);
|
|
560
|
+
for (let i = 0; i < lines.length; i++)
|
|
561
|
+
if (re.test(lines[i]))
|
|
562
|
+
return i + 1;
|
|
563
|
+
return 1;
|
|
564
|
+
}
|
|
565
|
+
//# sourceMappingURL=static.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static.js","sourceRoot":"","sources":["../../src/pen/static.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pen/store.ts — persistence for pen results (sealed evidence, like every
|
|
3
|
+
* other OpenPitStop artifact) and lookup helpers for `pitstop repro` / inspect.
|
|
4
|
+
*/
|
|
5
|
+
import type { PenFinding, PenResult } from "./types.js";
|
|
6
|
+
export declare function persistPen(repo: string, result: PenResult): {
|
|
7
|
+
file: string;
|
|
8
|
+
};
|
|
9
|
+
export declare function loadPenLatest(repo: string): PenResult | null;
|
|
10
|
+
export declare function enumeratePenFindings(r: PenResult): PenFinding[];
|
|
11
|
+
export declare function resolvePenFinding(r: PenResult, id: string): PenFinding | null;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pen/store.ts — persistence for pen results (sealed evidence, like every
|
|
3
|
+
* other OpenPitStop artifact) and lookup helpers for `pitstop repro` / inspect.
|
|
4
|
+
*/
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { seal } from "../evidence.js";
|
|
8
|
+
export function persistPen(repo, result) {
|
|
9
|
+
const sealed = seal(result, `pitstop pen result for ${repo}`);
|
|
10
|
+
const outDir = path.join(repo, ".pitstop");
|
|
11
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
12
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
13
|
+
const file = path.join(outDir, `pen-${ts}.json`);
|
|
14
|
+
const json = JSON.stringify(sealed, null, 2);
|
|
15
|
+
fs.writeFileSync(file, json);
|
|
16
|
+
fs.writeFileSync(path.join(outDir, "pen-latest.json"), json);
|
|
17
|
+
return { file };
|
|
18
|
+
}
|
|
19
|
+
export function loadPenLatest(repo) {
|
|
20
|
+
const p = path.join(repo, ".pitstop", "pen-latest.json");
|
|
21
|
+
if (!fs.existsSync(p))
|
|
22
|
+
return null;
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(fs.readFileSync(p, "utf8"));
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function enumeratePenFindings(r) {
|
|
31
|
+
return r.findings ?? [];
|
|
32
|
+
}
|
|
33
|
+
export function resolvePenFinding(r, id) {
|
|
34
|
+
return enumeratePenFindings(r).find((f) => f.id === id) ?? null;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/pen/store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAGtC,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,MAAiB;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,0BAA0B,IAAI,EAAE,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAC3C,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,EAAE,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IACjD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7C,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAC7B,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAC;IAC7D,OAAO,EAAE,IAAI,EAAE,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC;IACzD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAc,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,CAAY;IAC/C,OAAO,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,CAAY,EAAE,EAAU;IACxD,OAAO,oBAAoB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI,CAAC;AAClE,CAAC"}
|