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,10 @@
|
|
|
1
|
+
import type { FileChange, IntegrityFinding } from "./types.js";
|
|
2
|
+
export { detectTestTamper } from "./testTamper.js";
|
|
3
|
+
export { detectExceptionSwallow } from "./exceptionSwallow.js";
|
|
4
|
+
export { detectSuppressionCreep } from "./suppressionCreep.js";
|
|
5
|
+
export { detectHardcodedMatch } from "./hardcodedMatch.js";
|
|
6
|
+
export { detectMockOverreach } from "./mockOverreach.js";
|
|
7
|
+
export { detectExitCheat } from "./exitCheat.js";
|
|
8
|
+
export { detectAssertionLiteralTamper } from "./assertionLiteralTamper.js";
|
|
9
|
+
/** Run every detector against the diff. Order is stable for reporting. */
|
|
10
|
+
export declare function runDetectors(changes: FileChange[]): IntegrityFinding[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { detectTestTamper } from "./testTamper.js";
|
|
2
|
+
import { detectExceptionSwallow } from "./exceptionSwallow.js";
|
|
3
|
+
import { detectSuppressionCreep } from "./suppressionCreep.js";
|
|
4
|
+
import { detectHardcodedMatch } from "./hardcodedMatch.js";
|
|
5
|
+
import { detectMockOverreach } from "./mockOverreach.js";
|
|
6
|
+
import { detectExitCheat } from "./exitCheat.js";
|
|
7
|
+
import { detectAssertionLiteralTamper } from "./assertionLiteralTamper.js";
|
|
8
|
+
export { detectTestTamper } from "./testTamper.js";
|
|
9
|
+
export { detectExceptionSwallow } from "./exceptionSwallow.js";
|
|
10
|
+
export { detectSuppressionCreep } from "./suppressionCreep.js";
|
|
11
|
+
export { detectHardcodedMatch } from "./hardcodedMatch.js";
|
|
12
|
+
export { detectMockOverreach } from "./mockOverreach.js";
|
|
13
|
+
export { detectExitCheat } from "./exitCheat.js";
|
|
14
|
+
export { detectAssertionLiteralTamper } from "./assertionLiteralTamper.js";
|
|
15
|
+
/** Run every detector against the diff. Order is stable for reporting. */
|
|
16
|
+
export function runDetectors(changes) {
|
|
17
|
+
return [
|
|
18
|
+
...detectTestTamper(changes),
|
|
19
|
+
...detectExceptionSwallow(changes),
|
|
20
|
+
...detectSuppressionCreep(changes),
|
|
21
|
+
...detectHardcodedMatch(changes),
|
|
22
|
+
...detectMockOverreach(changes),
|
|
23
|
+
...detectExitCheat(changes),
|
|
24
|
+
...detectAssertionLiteralTamper(changes),
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/analyzers/integrity/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAE3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAE3E,0EAA0E;AAC1E,MAAM,UAAU,YAAY,CAAC,OAAqB;IAChD,OAAO;QACL,GAAG,gBAAgB,CAAC,OAAO,CAAC;QAC5B,GAAG,sBAAsB,CAAC,OAAO,CAAC;QAClC,GAAG,sBAAsB,CAAC,OAAO,CAAC;QAClC,GAAG,oBAAoB,CAAC,OAAO,CAAC;QAChC,GAAG,mBAAmB,CAAC,OAAO,CAAC;QAC/B,GAAG,eAAe,CAAC,OAAO,CAAC;QAC3B,GAAG,4BAA4B,CAAC,OAAO,CAAC;KACzC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { isTestFile } from "./helpers.js";
|
|
2
|
+
/**
|
|
3
|
+
* mockOverreach — when a test file's diff mocks/stubs/patches the module UNDER
|
|
4
|
+
* TEST (rather than its external dependencies like network/DB), it can fake a
|
|
5
|
+
* passing result for code that was never really executed.
|
|
6
|
+
*
|
|
7
|
+
* SUSPICIOUS ONLY (never CONFIRMED): distinguishing "mock the SUT" from "mock a
|
|
8
|
+
* dependency" is heuristic; a human must confirm it wasn't a legitimate seam.
|
|
9
|
+
*/
|
|
10
|
+
const MOCK_RE = /(?:jest|vi|sinon|td|module)\s*\.\s*(?:mock|spyOn|stub|patch)\s*\(\s*['"`]?([^'"`\s,)]+)['"`]?/gi;
|
|
11
|
+
const PY_PATCH_RE = /@mock\.patch\s*\(\s*['"`]([^'"`]+)['"`]/gi;
|
|
12
|
+
const MONKEY_RE = /monkeypatch\.setattr\s*\(\s*([^,)\s]+)/gi;
|
|
13
|
+
function moduleUnderTest(path) {
|
|
14
|
+
const base = path.replace(/\\/g, "/").split("/").pop() ?? "";
|
|
15
|
+
const names = [];
|
|
16
|
+
let m = base;
|
|
17
|
+
m = m.replace(/\.(test|spec)\.[^.]+$/, "");
|
|
18
|
+
m = m.replace(/^test_/, "");
|
|
19
|
+
m = m.replace(/_test$/, "");
|
|
20
|
+
names.push(m);
|
|
21
|
+
names.push(m + ".js");
|
|
22
|
+
names.push(m + ".ts");
|
|
23
|
+
names.push(m + ".py");
|
|
24
|
+
return names;
|
|
25
|
+
}
|
|
26
|
+
function basenameOf(target) {
|
|
27
|
+
const cleaned = target.replace(/['"`]/g, "").replace(/^.*[\\/]/, "");
|
|
28
|
+
return cleaned.replace(/\.[^.]+$/, "");
|
|
29
|
+
}
|
|
30
|
+
function targetMatches(target, candidates) {
|
|
31
|
+
const base = basenameOf(target).toLowerCase();
|
|
32
|
+
return candidates.some((c) => c.toLowerCase() === base);
|
|
33
|
+
}
|
|
34
|
+
export function detectMockOverreach(changes) {
|
|
35
|
+
const findings = [];
|
|
36
|
+
for (const c of changes) {
|
|
37
|
+
if (!isTestFile(c.path))
|
|
38
|
+
continue;
|
|
39
|
+
if (c.status === "added" || c.status === "modified") {
|
|
40
|
+
const candidates = moduleUnderTest(c.path);
|
|
41
|
+
for (const a of c.added) {
|
|
42
|
+
const text = a.text;
|
|
43
|
+
let hit = false;
|
|
44
|
+
for (const re of [MOCK_RE, MONKEY_RE, PY_PATCH_RE]) {
|
|
45
|
+
re.lastIndex = 0;
|
|
46
|
+
let m;
|
|
47
|
+
while ((m = re.exec(text))) {
|
|
48
|
+
const target = m[1];
|
|
49
|
+
if (targetMatches(target, candidates)) {
|
|
50
|
+
hit = true;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (hit)
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
if (hit) {
|
|
58
|
+
findings.push({
|
|
59
|
+
detector: "mockOverreach",
|
|
60
|
+
pattern: "module-under-test-mocked",
|
|
61
|
+
confidence: "suspicious",
|
|
62
|
+
file: c.path,
|
|
63
|
+
line: a.line,
|
|
64
|
+
evidence: `module under test (${candidates[0]}) appears to be mocked/stubbed/patched in its own test: ${text.trim()}`,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return findings;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=mockOverreach.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mockOverreach.js","sourceRoot":"","sources":["../../../src/analyzers/integrity/mockOverreach.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* suppressionCreep — grep added lines for newly introduced suppression markers.
|
|
3
|
+
*
|
|
4
|
+
* SUSPICIOUS ONLY (never CONFIRMED): a suppression comment might be a legitimate,
|
|
5
|
+
* documented workaround or a locally-acknowledged false positive. A human must
|
|
6
|
+
* judge whether it's hiding a real problem.
|
|
7
|
+
*/
|
|
8
|
+
const SUPPRESSION_RE = /\b(eslint-disable|eslint-disable-next-line|eslint-disable-line|@ts-ignore|ts-ignore|ts-nocheck|ts-expect-error|nolint|noqa|type:\s*ignore|SuppressWarnings)\b/i;
|
|
9
|
+
export function detectSuppressionCreep(changes) {
|
|
10
|
+
const findings = [];
|
|
11
|
+
for (const c of changes) {
|
|
12
|
+
for (const a of c.added) {
|
|
13
|
+
if (SUPPRESSION_RE.test(a.text)) {
|
|
14
|
+
findings.push({
|
|
15
|
+
detector: "suppressionCreep",
|
|
16
|
+
pattern: "suppression-comment-added",
|
|
17
|
+
confidence: "suspicious",
|
|
18
|
+
file: c.path,
|
|
19
|
+
line: a.line,
|
|
20
|
+
evidence: `newly added suppression marker: ${a.text.trim()}`,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return findings;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=suppressionCreep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suppressionCreep.js","sourceRoot":"","sources":["../../../src/analyzers/integrity/suppressionCreep.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,MAAM,cAAc,GAClB,gKAAgK,CAAC;AAEnK,MAAM,UAAU,sBAAsB,CAAC,OAAqB;IAC1D,MAAM,QAAQ,GAAuB,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACxB,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,kBAAkB;oBAC5B,OAAO,EAAE,2BAA2B;oBACpC,UAAU,EAAE,YAAY;oBACxB,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,QAAQ,EAAE,mCAAmC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE;iBAC7D,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { isTestFile } from "./helpers.js";
|
|
2
|
+
function extractTestNames(code) {
|
|
3
|
+
if (!code)
|
|
4
|
+
return [];
|
|
5
|
+
const out = [];
|
|
6
|
+
const js = /(?:test|it|fit)\s*\(\s*['"`]([^'"`]+)['"`]/g;
|
|
7
|
+
let m;
|
|
8
|
+
while ((m = js.exec(code)))
|
|
9
|
+
out.push({ name: m[1], line: lineOf(code, m.index) });
|
|
10
|
+
const py = /def\s+(test_\w+)\s*\(/g;
|
|
11
|
+
while ((m = py.exec(code)))
|
|
12
|
+
out.push({ name: m[1], line: lineOf(code, m.index) });
|
|
13
|
+
return out;
|
|
14
|
+
}
|
|
15
|
+
function lineOf(code, index) {
|
|
16
|
+
let line = 1;
|
|
17
|
+
for (let i = 0; i < index && i < code.length; i++)
|
|
18
|
+
if (code[i] === "\n")
|
|
19
|
+
line++;
|
|
20
|
+
return line;
|
|
21
|
+
}
|
|
22
|
+
const SKIP_RE = /\b(xit|xtest|xdescribe|fit|fdescribe|test\s*\.\s*skip|it\s*\.\s*skip|describe\s*\.\s*skip|pending\s*\(|@pytest\.mark\.(skip|skipif|xfail))\b/;
|
|
23
|
+
const TOLERANCE_KEYWORDS = [
|
|
24
|
+
"tolerance",
|
|
25
|
+
"threshold",
|
|
26
|
+
"epsilon",
|
|
27
|
+
"timeout",
|
|
28
|
+
"retries",
|
|
29
|
+
"maxretries",
|
|
30
|
+
"accuracy",
|
|
31
|
+
"precision",
|
|
32
|
+
"limit",
|
|
33
|
+
"interval",
|
|
34
|
+
"latency",
|
|
35
|
+
];
|
|
36
|
+
const EXACT_RE = /\b(toBe\(|toEqual\(|toStrictEqual\(|assertEqual\(|assertEquals\(|assert\.equal\(|assert\.strictEqual\(|assert\.deepEqual\(|assert deepEqual|assert almost equal)\b/;
|
|
37
|
+
const LOOSER_RE = /\b(toBeGreaterThan\(|toBeGreaterThanOrEqual\(|toBeLessThan\(|toBeLessThanOrEqual\(|toBeCloseTo\(|toBeTruthy\(|assertTrue\(|assertGreater|assertLess|assert almost|assertWarns)\b/;
|
|
38
|
+
export function detectTestTamper(changes) {
|
|
39
|
+
const findings = [];
|
|
40
|
+
for (const c of changes) {
|
|
41
|
+
if (!isTestFile(c.path))
|
|
42
|
+
continue;
|
|
43
|
+
// CONFIRMED: whole test file deleted.
|
|
44
|
+
if (c.status === "deleted") {
|
|
45
|
+
findings.push({
|
|
46
|
+
detector: "testTamper",
|
|
47
|
+
pattern: "test-file-deleted",
|
|
48
|
+
confidence: "confirmed",
|
|
49
|
+
file: c.path,
|
|
50
|
+
evidence: `entire test file "${c.path}" was deleted in this change`,
|
|
51
|
+
});
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const beforeNames = extractTestNames(c.before);
|
|
55
|
+
const afterNames = new Set(extractTestNames(c.after).map((n) => n.name));
|
|
56
|
+
const beforeNameSet = new Set(beforeNames.map((n) => n.name));
|
|
57
|
+
// CONFIRMED: an entire test case (by name) was removed.
|
|
58
|
+
for (const n of beforeNames) {
|
|
59
|
+
if (!afterNames.has(n.name)) {
|
|
60
|
+
findings.push({
|
|
61
|
+
detector: "testTamper",
|
|
62
|
+
pattern: "test-case-removed",
|
|
63
|
+
confidence: "confirmed",
|
|
64
|
+
file: c.path,
|
|
65
|
+
line: n.line,
|
|
66
|
+
evidence: `test case "${n.name}" present before but gone after — whole test block removed`,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// SUSPICIOUS: test marked as skipped.
|
|
71
|
+
for (const a of c.added) {
|
|
72
|
+
if (SKIP_RE.test(a.text)) {
|
|
73
|
+
findings.push({
|
|
74
|
+
detector: "testTamper",
|
|
75
|
+
pattern: "test-marked-skipped",
|
|
76
|
+
confidence: "suspicious",
|
|
77
|
+
file: c.path,
|
|
78
|
+
line: a.line,
|
|
79
|
+
evidence: `test marked as skipped/suppressed: ${a.text.trim()}`,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// SUSPICIOUS: a numeric tolerance/threshold literal was loosened.
|
|
84
|
+
const loosened = detectLoosenedTolerance(c);
|
|
85
|
+
findings.push(...loosened);
|
|
86
|
+
// SUSPICIOUS: exact-equality replaced with a looser assertion on the same line.
|
|
87
|
+
const loosenedEq = detectLoosenedEquality(c);
|
|
88
|
+
findings.push(...loosenedEq);
|
|
89
|
+
}
|
|
90
|
+
return findings;
|
|
91
|
+
}
|
|
92
|
+
function detectLoosenedTolerance(c) {
|
|
93
|
+
const out = [];
|
|
94
|
+
const addedByKw = new Map();
|
|
95
|
+
const removedByKw = new Map();
|
|
96
|
+
const scan = (text, bucket) => {
|
|
97
|
+
const lower = text.toLowerCase();
|
|
98
|
+
for (const kw of TOLERANCE_KEYWORDS) {
|
|
99
|
+
const re = new RegExp(`${kw}\\D*?(\\d+\\.?\\d*)`, "i");
|
|
100
|
+
const m = lower.match(re);
|
|
101
|
+
if (m) {
|
|
102
|
+
const v = parseFloat(m[1]);
|
|
103
|
+
if (!Number.isNaN(v))
|
|
104
|
+
(bucket.get(kw) ?? bucket.set(kw, []).get(kw)).push(v);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
for (const a of c.added)
|
|
109
|
+
scan(a.text, addedByKw);
|
|
110
|
+
for (const r of c.removed)
|
|
111
|
+
scan(r.text, removedByKw);
|
|
112
|
+
for (const [kw, added] of addedByKw) {
|
|
113
|
+
const removed = removedByKw.get(kw);
|
|
114
|
+
if (!removed)
|
|
115
|
+
continue;
|
|
116
|
+
const maxAdded = Math.max(...added);
|
|
117
|
+
const maxRemoved = Math.max(...removed);
|
|
118
|
+
if (maxAdded > maxRemoved) {
|
|
119
|
+
out.push({
|
|
120
|
+
detector: "testTamper",
|
|
121
|
+
pattern: "tolerance-loosened",
|
|
122
|
+
confidence: "suspicious",
|
|
123
|
+
file: c.path,
|
|
124
|
+
evidence: `numeric ${kw} increased from ${maxRemoved} to ${maxAdded} (threshold/tolerance loosened)`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
function detectLoosenedEquality(c) {
|
|
131
|
+
const out = [];
|
|
132
|
+
for (const r of c.removed) {
|
|
133
|
+
if (!EXACT_RE.test(r.text))
|
|
134
|
+
continue;
|
|
135
|
+
for (const a of c.added) {
|
|
136
|
+
if (Math.abs(a.line - r.line) > 3)
|
|
137
|
+
continue;
|
|
138
|
+
if (LOOSER_RE.test(a.text)) {
|
|
139
|
+
out.push({
|
|
140
|
+
detector: "testTamper",
|
|
141
|
+
pattern: "equality-loosened",
|
|
142
|
+
confidence: "suspicious",
|
|
143
|
+
file: c.path,
|
|
144
|
+
line: a.line,
|
|
145
|
+
evidence: `exact-equality assertion replaced with a looser one near line ${r.line}: "${a.text.trim()}"`,
|
|
146
|
+
});
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=testTamper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testTamper.js","sourceRoot":"","sources":["../../../src/analyzers/integrity/testTamper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the diff-scoped integrity (AI-agent-cheat) detectors.
|
|
3
|
+
*
|
|
4
|
+
* CALIBRATION (enforced in logic + comments throughout this folder):
|
|
5
|
+
* - `confidence: "confirmed"` is reserved for truly unambiguous patterns
|
|
6
|
+
* ONLY: a whole test file deleted, a whole test-case block removed, or a
|
|
7
|
+
* `process.exit(0)` / `sys.exit(0)` / `os._exit(0)` added inside app code.
|
|
8
|
+
* - Everything else is at most `"suspicious"` — it needs human judgment and
|
|
9
|
+
* must NEVER auto-block. A legitimate refactor that updates a test because
|
|
10
|
+
* the spec changed must surface with evidence, not get silently reverted.
|
|
11
|
+
*/
|
|
12
|
+
export type Confidence = "confirmed" | "suspicious";
|
|
13
|
+
export interface IntegrityFinding {
|
|
14
|
+
/** Detector name, e.g. "testTamper". */
|
|
15
|
+
detector: string;
|
|
16
|
+
/** Short pattern name, e.g. "test-file-deleted". */
|
|
17
|
+
pattern: string;
|
|
18
|
+
confidence: Confidence;
|
|
19
|
+
/** Primary file (relative path). */
|
|
20
|
+
file: string;
|
|
21
|
+
/** 1-based line in that file (when known). */
|
|
22
|
+
line?: number;
|
|
23
|
+
/** Human-readable evidence / explanation. */
|
|
24
|
+
evidence: string;
|
|
25
|
+
/** Optional second location (used by hardcodedMatch cross-reference). */
|
|
26
|
+
file2?: string;
|
|
27
|
+
line2?: number;
|
|
28
|
+
}
|
|
29
|
+
/** One changed line with its 1-based line number in the relevant file version. */
|
|
30
|
+
export interface DiffLine {
|
|
31
|
+
line: number;
|
|
32
|
+
text: string;
|
|
33
|
+
}
|
|
34
|
+
export type FileStatus = "added" | "deleted" | "modified" | "renamed";
|
|
35
|
+
/**
|
|
36
|
+
* A single file's change, scoped from a git diff. `before` is the content at
|
|
37
|
+
* the `from` ref (undefined when the file was added); `after` is the content at
|
|
38
|
+
* the `to` ref / working tree (undefined when deleted). `added` / `removed` are
|
|
39
|
+
* the exact + / - lines with their line numbers in `after` / `before`.
|
|
40
|
+
*/
|
|
41
|
+
export interface FileChange {
|
|
42
|
+
path: string;
|
|
43
|
+
status: FileStatus;
|
|
44
|
+
before?: string;
|
|
45
|
+
after?: string;
|
|
46
|
+
added: DiffLine[];
|
|
47
|
+
removed: DiffLine[];
|
|
48
|
+
language: "js" | "ts" | "python" | "unknown";
|
|
49
|
+
}
|
|
50
|
+
export type Verdict = "CLEAN" | "SUSPICIOUS" | "CONFIRMED_CHEAT";
|
|
51
|
+
export interface IntegrityReport {
|
|
52
|
+
timestamp: string;
|
|
53
|
+
repo: string;
|
|
54
|
+
from: string;
|
|
55
|
+
to: string;
|
|
56
|
+
verdict: Verdict;
|
|
57
|
+
findings: IntegrityFinding[];
|
|
58
|
+
summary: {
|
|
59
|
+
confirmed: number;
|
|
60
|
+
suspicious: number;
|
|
61
|
+
total: number;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the diff-scoped integrity (AI-agent-cheat) detectors.
|
|
3
|
+
*
|
|
4
|
+
* CALIBRATION (enforced in logic + comments throughout this folder):
|
|
5
|
+
* - `confidence: "confirmed"` is reserved for truly unambiguous patterns
|
|
6
|
+
* ONLY: a whole test file deleted, a whole test-case block removed, or a
|
|
7
|
+
* `process.exit(0)` / `sys.exit(0)` / `os._exit(0)` added inside app code.
|
|
8
|
+
* - Everything else is at most `"suspicious"` — it needs human judgment and
|
|
9
|
+
* must NEVER auto-block. A legitimate refactor that updates a test because
|
|
10
|
+
* the spec changed must surface with evidence, not get silently reverted.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/analyzers/integrity/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { LedgerEndpoint, LedgerScenario } from "../types.js";
|
|
2
|
+
import type { AttackReport, ScenarioRun } from "./types.js";
|
|
3
|
+
export interface AttackOptions {
|
|
4
|
+
baseUrl: string;
|
|
5
|
+
endpoints: LedgerEndpoint[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* attacks.ts — fires the ledger attack scenarios against each discovered
|
|
9
|
+
* endpoint, always with a fake idempotency key / order id, targeting only the
|
|
10
|
+
* locally-running app (whose outbound HTTP is sandboxed by the preload).
|
|
11
|
+
*
|
|
12
|
+
* (a) duplicate-webhook — same payload fired twice, 300ms apart.
|
|
13
|
+
* (b) concurrent-double-submit — two identical requests fired simultaneously.
|
|
14
|
+
* (c) delayed-retry — request 1 succeeds, same key arrives 2s later.
|
|
15
|
+
*/
|
|
16
|
+
export declare function runAttacks(opts: AttackOptions): Promise<AttackReport>;
|
|
17
|
+
export type { LedgerScenario, ScenarioRun };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const AMOUNT = 1000;
|
|
2
|
+
const CURRENCY = "INR";
|
|
3
|
+
const WEBHOOK_DELAY_MS = 300;
|
|
4
|
+
const RETRY_DELAY_MS = 2000;
|
|
5
|
+
/**
|
|
6
|
+
* attacks.ts — fires the ledger attack scenarios against each discovered
|
|
7
|
+
* endpoint, always with a fake idempotency key / order id, targeting only the
|
|
8
|
+
* locally-running app (whose outbound HTTP is sandboxed by the preload).
|
|
9
|
+
*
|
|
10
|
+
* (a) duplicate-webhook — same payload fired twice, 300ms apart.
|
|
11
|
+
* (b) concurrent-double-submit — two identical requests fired simultaneously.
|
|
12
|
+
* (c) delayed-retry — request 1 succeeds, same key arrives 2s later.
|
|
13
|
+
*/
|
|
14
|
+
export async function runAttacks(opts) {
|
|
15
|
+
const runs = [];
|
|
16
|
+
let idx = 0;
|
|
17
|
+
for (let i = 0; i < opts.endpoints.length; i++) {
|
|
18
|
+
const ep = opts.endpoints[i];
|
|
19
|
+
if (ep.webhook) {
|
|
20
|
+
idx += 1;
|
|
21
|
+
const run = await runDuplicateWebhook(opts.baseUrl, ep, i, idx);
|
|
22
|
+
if (run)
|
|
23
|
+
runs.push(run);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
idx += 1;
|
|
27
|
+
const conc = await runConcurrent(opts.baseUrl, ep, i, idx);
|
|
28
|
+
if (conc)
|
|
29
|
+
runs.push(conc);
|
|
30
|
+
idx += 1;
|
|
31
|
+
const retry = await runDelayedRetry(opts.baseUrl, ep, i, idx);
|
|
32
|
+
if (retry)
|
|
33
|
+
runs.push(retry);
|
|
34
|
+
}
|
|
35
|
+
return { runs };
|
|
36
|
+
}
|
|
37
|
+
async function postRaw(baseUrl, path, body, headers) {
|
|
38
|
+
try {
|
|
39
|
+
const res = await fetch(`${baseUrl}${path}`, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: { "Content-Type": "application/json", ...headers },
|
|
42
|
+
body: JSON.stringify(body),
|
|
43
|
+
});
|
|
44
|
+
let text = "";
|
|
45
|
+
try {
|
|
46
|
+
text = await res.text();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
/* ignore */
|
|
50
|
+
}
|
|
51
|
+
let parsed = text;
|
|
52
|
+
try {
|
|
53
|
+
parsed = JSON.parse(text);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
/* keep raw */
|
|
57
|
+
}
|
|
58
|
+
return { status: res.status, body: parsed, ok: res.ok };
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return { status: 0, body: null, ok: false };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
65
|
+
/** (a) duplicate webhook — same payload twice, 300ms apart. */
|
|
66
|
+
async function runDuplicateWebhook(baseUrl, ep, epIndex, n) {
|
|
67
|
+
const orderId = `ord_webhook_${n}`;
|
|
68
|
+
const paymentId = `pay_webhook_${n}`;
|
|
69
|
+
const key = `idem_webhook_${n}`;
|
|
70
|
+
const payload = {
|
|
71
|
+
event: "payment.captured",
|
|
72
|
+
id: `${orderId}_evt`,
|
|
73
|
+
payload: {
|
|
74
|
+
payment: { entity: { id: paymentId, amount: AMOUNT, currency: CURRENCY, order_id: orderId } },
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
const r1 = await postRaw(baseUrl, ep.path, payload, {});
|
|
78
|
+
await sleep(WEBHOOK_DELAY_MS);
|
|
79
|
+
const r2 = await postRaw(baseUrl, ep.path, payload, {});
|
|
80
|
+
void r1;
|
|
81
|
+
void r2;
|
|
82
|
+
return {
|
|
83
|
+
scenario: "duplicate-webhook",
|
|
84
|
+
endpointIndex: epIndex,
|
|
85
|
+
orderId,
|
|
86
|
+
idempotencyKey: key,
|
|
87
|
+
identifiers: [orderId, paymentId, key],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** (b) concurrent double-submit — two identical requests at once. */
|
|
91
|
+
async function runConcurrent(baseUrl, ep, epIndex, n) {
|
|
92
|
+
const orderId = `ord_conc_${n}`;
|
|
93
|
+
const key = `idem_conc_${n}`;
|
|
94
|
+
const body = { amount: AMOUNT, currency: CURRENCY };
|
|
95
|
+
const headers = { ["Idempotency-Key"]: key };
|
|
96
|
+
const [r1, r2] = await Promise.all([
|
|
97
|
+
postRaw(baseUrl, resolvePath(ep.path, orderId), body, headers),
|
|
98
|
+
postRaw(baseUrl, resolvePath(ep.path, orderId), body, headers),
|
|
99
|
+
]);
|
|
100
|
+
void r1;
|
|
101
|
+
void r2;
|
|
102
|
+
return {
|
|
103
|
+
scenario: "concurrent-double-submit",
|
|
104
|
+
endpointIndex: epIndex,
|
|
105
|
+
orderId,
|
|
106
|
+
idempotencyKey: key,
|
|
107
|
+
identifiers: [orderId, key],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
/** (c) delayed retry — request 1 succeeds, same key arrives 2s later. */
|
|
111
|
+
async function runDelayedRetry(baseUrl, ep, epIndex, n) {
|
|
112
|
+
const orderId = `ord_retry_${n}`;
|
|
113
|
+
const key = `idem_retry_${n}`;
|
|
114
|
+
const body = { amount: AMOUNT, currency: CURRENCY };
|
|
115
|
+
const headers = { ["Idempotency-Key"]: key };
|
|
116
|
+
const p = resolvePath(ep.path, orderId);
|
|
117
|
+
await postRaw(baseUrl, p, body, headers);
|
|
118
|
+
await sleep(RETRY_DELAY_MS);
|
|
119
|
+
await postRaw(baseUrl, p, body, headers);
|
|
120
|
+
return {
|
|
121
|
+
scenario: "delayed-retry",
|
|
122
|
+
endpointIndex: epIndex,
|
|
123
|
+
orderId,
|
|
124
|
+
idempotencyKey: key,
|
|
125
|
+
identifiers: [orderId, key],
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/** Substitute a fake order id into a path route param (`:id`, `{id}`, `<id>`). */
|
|
129
|
+
function resolvePath(route, orderId) {
|
|
130
|
+
return route
|
|
131
|
+
.replace(/:([A-Za-z_][\w-]*)/g, (_, name) => /id|order/i.test(name) ? orderId : name)
|
|
132
|
+
.replace(/\{([A-Za-z_][\w-]*)\}/g, (_, name) => /id|order/i.test(name) ? orderId : name)
|
|
133
|
+
.replace(/<([A-Za-z_][\w-]*)>/g, (_, name) => /id|order/i.test(name) ? orderId : name);
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=attacks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attacks.js","sourceRoot":"","sources":["../../../src/analyzers/ledger/attacks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { LedgerEndpoint } from "../types.js";
|
|
2
|
+
export interface LedgerDiscovery {
|
|
3
|
+
endpoints: LedgerEndpoint[];
|
|
4
|
+
/** Payment SDKs imported anywhere in the repo. */
|
|
5
|
+
sdkImports: string[];
|
|
6
|
+
/** Gateway hosts that must be mocked before any traffic fires. */
|
|
7
|
+
gatewayHosts: string[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* discover.ts — static, read-only phase of ledger mode.
|
|
11
|
+
* Heuristically finds money-moving endpoints (routes whose path/handler match a
|
|
12
|
+
* money keyword, or whose file imports a payment SDK) and the expected payload
|
|
13
|
+
* shape. This NEVER runs the app and never touches the network.
|
|
14
|
+
*/
|
|
15
|
+
export declare function discover(repo: string): LedgerDiscovery;
|