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.
Files changed (274) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +512 -0
  3. package/demo-repo/README.md +24 -0
  4. package/demo-repo/package-lock.json +4483 -0
  5. package/demo-repo/package.json +17 -0
  6. package/demo-repo/src/config.js +5 -0
  7. package/demo-repo/src/db.js +31 -0
  8. package/demo-repo/src/server.js +13 -0
  9. package/demo-repo/src/userController.js +22 -0
  10. package/demo-repo/src/userRepo.js +18 -0
  11. package/demo-repo/src/userService.js +59 -0
  12. package/demo-repo/test/user.test.js +12 -0
  13. package/demo-repo-fintech/README.md +27 -0
  14. package/demo-repo-fintech/package-lock.json +1023 -0
  15. package/demo-repo-fintech/package.json +14 -0
  16. package/demo-repo-fintech/pitstop-repro-ledger-order-ord-conc-1-charged-2.test.mjs +175 -0
  17. package/demo-repo-fintech/pitstop-repro-ledger-order-ord-retry-2-charged-2.test.mjs +175 -0
  18. package/demo-repo-fintech/pitstop-repro-ledger-order-ord-webhook-3-charged-2.test.mjs +175 -0
  19. package/demo-repo-fintech/src/gateway.js +26 -0
  20. package/demo-repo-fintech/src/server.js +61 -0
  21. package/demo-repo-generators/package-lock.json +1508 -0
  22. package/demo-repo-generators/package.json +17 -0
  23. package/demo-repo-generators/public/index.html +10 -0
  24. package/demo-repo-generators/scripts/build.js +17 -0
  25. package/demo-repo-generators/src/counter.js +16 -0
  26. package/demo-repo-integrity/README.md +49 -0
  27. package/demo-repo-integrity/package.json +12 -0
  28. package/demo-repo-integrity/src/money.js +19 -0
  29. package/demo-repo-integrity/test/money.test.js +17 -0
  30. package/dist/analyzers/accessibility.d.ts +18 -0
  31. package/dist/analyzers/accessibility.js +248 -0
  32. package/dist/analyzers/accessibility.js.map +1 -0
  33. package/dist/analyzers/dependencyGraph.d.ts +14 -0
  34. package/dist/analyzers/dependencyGraph.js +206 -0
  35. package/dist/analyzers/dependencyGraph.js.map +1 -0
  36. package/dist/analyzers/devex.d.ts +19 -0
  37. package/dist/analyzers/devex.js +301 -0
  38. package/dist/analyzers/devex.js.map +1 -0
  39. package/dist/analyzers/duplication.d.ts +2 -0
  40. package/dist/analyzers/duplication.js +47 -0
  41. package/dist/analyzers/duplication.js.map +1 -0
  42. package/dist/analyzers/index.d.ts +12 -0
  43. package/dist/analyzers/index.js +38 -0
  44. package/dist/analyzers/index.js.map +1 -0
  45. package/dist/analyzers/integrity/assertionLiteralTamper.d.ts +2 -0
  46. package/dist/analyzers/integrity/assertionLiteralTamper.js +164 -0
  47. package/dist/analyzers/integrity/assertionLiteralTamper.js.map +1 -0
  48. package/dist/analyzers/integrity/exceptionSwallow.d.ts +2 -0
  49. package/dist/analyzers/integrity/exceptionSwallow.js +198 -0
  50. package/dist/analyzers/integrity/exceptionSwallow.js.map +1 -0
  51. package/dist/analyzers/integrity/exitCheat.d.ts +2 -0
  52. package/dist/analyzers/integrity/exitCheat.js +31 -0
  53. package/dist/analyzers/integrity/exitCheat.js.map +1 -0
  54. package/dist/analyzers/integrity/git.d.ts +10 -0
  55. package/dist/analyzers/integrity/git.js +181 -0
  56. package/dist/analyzers/integrity/git.js.map +1 -0
  57. package/dist/analyzers/integrity/hardcodedMatch.d.ts +2 -0
  58. package/dist/analyzers/integrity/hardcodedMatch.js +93 -0
  59. package/dist/analyzers/integrity/hardcodedMatch.js.map +1 -0
  60. package/dist/analyzers/integrity/helpers.d.ts +21 -0
  61. package/dist/analyzers/integrity/helpers.js +66 -0
  62. package/dist/analyzers/integrity/helpers.js.map +1 -0
  63. package/dist/analyzers/integrity/index.d.ts +10 -0
  64. package/dist/analyzers/integrity/index.js +27 -0
  65. package/dist/analyzers/integrity/index.js.map +1 -0
  66. package/dist/analyzers/integrity/mockOverreach.d.ts +2 -0
  67. package/dist/analyzers/integrity/mockOverreach.js +72 -0
  68. package/dist/analyzers/integrity/mockOverreach.js.map +1 -0
  69. package/dist/analyzers/integrity/suppressionCreep.d.ts +2 -0
  70. package/dist/analyzers/integrity/suppressionCreep.js +27 -0
  71. package/dist/analyzers/integrity/suppressionCreep.js.map +1 -0
  72. package/dist/analyzers/integrity/testTamper.d.ts +2 -0
  73. package/dist/analyzers/integrity/testTamper.js +153 -0
  74. package/dist/analyzers/integrity/testTamper.js.map +1 -0
  75. package/dist/analyzers/integrity/types.d.ts +63 -0
  76. package/dist/analyzers/integrity/types.js +13 -0
  77. package/dist/analyzers/integrity/types.js.map +1 -0
  78. package/dist/analyzers/ledger/attacks.d.ts +17 -0
  79. package/dist/analyzers/ledger/attacks.js +135 -0
  80. package/dist/analyzers/ledger/attacks.js.map +1 -0
  81. package/dist/analyzers/ledger/discover.d.ts +15 -0
  82. package/dist/analyzers/ledger/discover.js +122 -0
  83. package/dist/analyzers/ledger/discover.js.map +1 -0
  84. package/dist/analyzers/ledger/evidence.d.ts +22 -0
  85. package/dist/analyzers/ledger/evidence.js +127 -0
  86. package/dist/analyzers/ledger/evidence.js.map +1 -0
  87. package/dist/analyzers/ledger/harness.d.ts +4 -0
  88. package/dist/analyzers/ledger/harness.js +270 -0
  89. package/dist/analyzers/ledger/harness.js.map +1 -0
  90. package/dist/analyzers/ledger/index.d.ts +14 -0
  91. package/dist/analyzers/ledger/index.js +116 -0
  92. package/dist/analyzers/ledger/index.js.map +1 -0
  93. package/dist/analyzers/ledger/types.d.ts +31 -0
  94. package/dist/analyzers/ledger/types.js +2 -0
  95. package/dist/analyzers/ledger/types.js.map +1 -0
  96. package/dist/analyzers/perf.d.ts +2 -0
  97. package/dist/analyzers/perf.js +106 -0
  98. package/dist/analyzers/perf.js.map +1 -0
  99. package/dist/analyzers/reliability.d.ts +22 -0
  100. package/dist/analyzers/reliability.js +339 -0
  101. package/dist/analyzers/reliability.js.map +1 -0
  102. package/dist/analyzers/routes.d.ts +17 -0
  103. package/dist/analyzers/routes.js +115 -0
  104. package/dist/analyzers/routes.js.map +1 -0
  105. package/dist/analyzers/security.d.ts +2 -0
  106. package/dist/analyzers/security.js +302 -0
  107. package/dist/analyzers/security.js.map +1 -0
  108. package/dist/analyzers/suiteRunner.d.ts +64 -0
  109. package/dist/analyzers/suiteRunner.js +381 -0
  110. package/dist/analyzers/suiteRunner.js.map +1 -0
  111. package/dist/analyzers/tests.d.ts +2 -0
  112. package/dist/analyzers/tests.js +215 -0
  113. package/dist/analyzers/tests.js.map +1 -0
  114. package/dist/analyzers/types.d.ts +203 -0
  115. package/dist/analyzers/types.js +2 -0
  116. package/dist/analyzers/types.js.map +1 -0
  117. package/dist/analyzers/util.d.ts +35 -0
  118. package/dist/analyzers/util.js +209 -0
  119. package/dist/analyzers/util.js.map +1 -0
  120. package/dist/ciCheckA.d.ts +2 -0
  121. package/dist/ciCheckA.js +5 -0
  122. package/dist/ciCheckA.js.map +1 -0
  123. package/dist/ciCheckB.d.ts +2 -0
  124. package/dist/ciCheckB.js +6 -0
  125. package/dist/ciCheckB.js.map +1 -0
  126. package/dist/cli.d.ts +2 -0
  127. package/dist/cli.js +65 -0
  128. package/dist/cli.js.map +1 -0
  129. package/dist/commands/budget.d.ts +33 -0
  130. package/dist/commands/budget.js +108 -0
  131. package/dist/commands/budget.js.map +1 -0
  132. package/dist/commands/ci.d.ts +14 -0
  133. package/dist/commands/ci.js +157 -0
  134. package/dist/commands/ci.js.map +1 -0
  135. package/dist/commands/demo.d.ts +2 -0
  136. package/dist/commands/demo.js +163 -0
  137. package/dist/commands/demo.js.map +1 -0
  138. package/dist/commands/digest.d.ts +8 -0
  139. package/dist/commands/digest.js +189 -0
  140. package/dist/commands/digest.js.map +1 -0
  141. package/dist/commands/doctor.d.ts +2 -0
  142. package/dist/commands/doctor.js +98 -0
  143. package/dist/commands/doctor.js.map +1 -0
  144. package/dist/commands/drive.d.ts +3 -0
  145. package/dist/commands/drive.js +170 -0
  146. package/dist/commands/drive.js.map +1 -0
  147. package/dist/commands/gate.d.ts +2 -0
  148. package/dist/commands/gate.js +136 -0
  149. package/dist/commands/gate.js.map +1 -0
  150. package/dist/commands/honesty.d.ts +2 -0
  151. package/dist/commands/honesty.js +182 -0
  152. package/dist/commands/honesty.js.map +1 -0
  153. package/dist/commands/inspect.d.ts +2 -0
  154. package/dist/commands/inspect.js +244 -0
  155. package/dist/commands/inspect.js.map +1 -0
  156. package/dist/commands/install.d.ts +2 -0
  157. package/dist/commands/install.js +81 -0
  158. package/dist/commands/install.js.map +1 -0
  159. package/dist/commands/integrity.d.ts +2 -0
  160. package/dist/commands/integrity.js +74 -0
  161. package/dist/commands/integrity.js.map +1 -0
  162. package/dist/commands/memory.d.ts +6 -0
  163. package/dist/commands/memory.js +90 -0
  164. package/dist/commands/memory.js.map +1 -0
  165. package/dist/commands/pen.d.ts +17 -0
  166. package/dist/commands/pen.js +158 -0
  167. package/dist/commands/pen.js.map +1 -0
  168. package/dist/commands/prompt.d.ts +2 -0
  169. package/dist/commands/prompt.js +37 -0
  170. package/dist/commands/prompt.js.map +1 -0
  171. package/dist/commands/readyCheck.d.ts +21 -0
  172. package/dist/commands/readyCheck.js +68 -0
  173. package/dist/commands/readyCheck.js.map +1 -0
  174. package/dist/commands/report.d.ts +2 -0
  175. package/dist/commands/report.js +48 -0
  176. package/dist/commands/report.js.map +1 -0
  177. package/dist/commands/repro.d.ts +3 -0
  178. package/dist/commands/repro.js +61 -0
  179. package/dist/commands/repro.js.map +1 -0
  180. package/dist/commands/scan.d.ts +27 -0
  181. package/dist/commands/scan.js +348 -0
  182. package/dist/commands/scan.js.map +1 -0
  183. package/dist/commands/share.d.ts +2 -0
  184. package/dist/commands/share.js +163 -0
  185. package/dist/commands/share.js.map +1 -0
  186. package/dist/commands/trends.d.ts +2 -0
  187. package/dist/commands/trends.js +103 -0
  188. package/dist/commands/trends.js.map +1 -0
  189. package/dist/commands/try.d.ts +12 -0
  190. package/dist/commands/try.js +208 -0
  191. package/dist/commands/try.js.map +1 -0
  192. package/dist/commands/verify.d.ts +44 -0
  193. package/dist/commands/verify.js +381 -0
  194. package/dist/commands/verify.js.map +1 -0
  195. package/dist/commands/watch.d.ts +10 -0
  196. package/dist/commands/watch.js +70 -0
  197. package/dist/commands/watch.js.map +1 -0
  198. package/dist/evidence.d.ts +37 -0
  199. package/dist/evidence.js +61 -0
  200. package/dist/evidence.js.map +1 -0
  201. package/dist/graph/correlate.d.ts +7 -0
  202. package/dist/graph/correlate.js +246 -0
  203. package/dist/graph/correlate.js.map +1 -0
  204. package/dist/graph/integrity.d.ts +22 -0
  205. package/dist/graph/integrity.js +39 -0
  206. package/dist/graph/integrity.js.map +1 -0
  207. package/dist/installer/targets.d.ts +23 -0
  208. package/dist/installer/targets.js +162 -0
  209. package/dist/installer/targets.js.map +1 -0
  210. package/dist/memory/store.d.ts +33 -0
  211. package/dist/memory/store.js +131 -0
  212. package/dist/memory/store.js.map +1 -0
  213. package/dist/pen/dynamic.d.ts +36 -0
  214. package/dist/pen/dynamic.js +622 -0
  215. package/dist/pen/dynamic.js.map +1 -0
  216. package/dist/pen/fix.d.ts +35 -0
  217. package/dist/pen/fix.js +239 -0
  218. package/dist/pen/fix.js.map +1 -0
  219. package/dist/pen/proof.d.ts +46 -0
  220. package/dist/pen/proof.js +112 -0
  221. package/dist/pen/proof.js.map +1 -0
  222. package/dist/pen/report.d.ts +11 -0
  223. package/dist/pen/report.js +258 -0
  224. package/dist/pen/report.js.map +1 -0
  225. package/dist/pen/static.d.ts +17 -0
  226. package/dist/pen/static.js +565 -0
  227. package/dist/pen/static.js.map +1 -0
  228. package/dist/pen/store.d.ts +11 -0
  229. package/dist/pen/store.js +36 -0
  230. package/dist/pen/store.js.map +1 -0
  231. package/dist/pen/types.d.ts +112 -0
  232. package/dist/pen/types.js +53 -0
  233. package/dist/pen/types.js.map +1 -0
  234. package/dist/report/format.d.ts +126 -0
  235. package/dist/report/format.js +706 -0
  236. package/dist/report/format.js.map +1 -0
  237. package/dist/report/score.d.ts +47 -0
  238. package/dist/report/score.js +164 -0
  239. package/dist/report/score.js.map +1 -0
  240. package/dist/repro/framework.d.ts +26 -0
  241. package/dist/repro/framework.js +224 -0
  242. package/dist/repro/framework.js.map +1 -0
  243. package/dist/repro/generate.d.ts +14 -0
  244. package/dist/repro/generate.js +669 -0
  245. package/dist/repro/generate.js.map +1 -0
  246. package/dist/repro/ids.d.ts +32 -0
  247. package/dist/repro/ids.js +175 -0
  248. package/dist/repro/ids.js.map +1 -0
  249. package/dist/repro/index.d.ts +24 -0
  250. package/dist/repro/index.js +69 -0
  251. package/dist/repro/index.js.map +1 -0
  252. package/dist/repro/pen.d.ts +26 -0
  253. package/dist/repro/pen.js +321 -0
  254. package/dist/repro/pen.js.map +1 -0
  255. package/dist/repro/run.d.ts +18 -0
  256. package/dist/repro/run.js +36 -0
  257. package/dist/repro/run.js.map +1 -0
  258. package/dist/sandbox/proxy.d.ts +41 -0
  259. package/dist/sandbox/proxy.js +231 -0
  260. package/dist/sandbox/proxy.js.map +1 -0
  261. package/dist/sandbox/startCmd.d.ts +24 -0
  262. package/dist/sandbox/startCmd.js +71 -0
  263. package/dist/sandbox/startCmd.js.map +1 -0
  264. package/dist/ui/spinner.d.ts +18 -0
  265. package/dist/ui/spinner.js +26 -0
  266. package/dist/ui/spinner.js.map +1 -0
  267. package/dist/verify/metrics.d.ts +13 -0
  268. package/dist/verify/metrics.js +72 -0
  269. package/dist/verify/metrics.js.map +1 -0
  270. package/package.json +68 -0
  271. package/scripts/cheat-demo.cjs +102 -0
  272. package/templates/ledger/preload.cjs +331 -0
  273. package/templates/pen/preload.cjs +196 -0
  274. package/templates/pitstop.prompt.md +371 -0
@@ -0,0 +1,122 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { walkFiles } from "../util.js";
4
+ import { findRoutesInFile, ROUTE_EXTS } from "../routes.js";
5
+ /** Keywords that mark a route as money-moving. */
6
+ const KEYWORDS = [
7
+ "charge",
8
+ "capture",
9
+ "payment",
10
+ "transfer",
11
+ "refund",
12
+ "webhook",
13
+ ];
14
+ /** Payment-SDK imports we watch for. */
15
+ const SDK_IMPORTS = ["razorpay", "stripe", "braintree"];
16
+ /** SDK package -> well-known gateway host(s). */
17
+ const SDK_TO_HOSTS = {
18
+ razorpay: ["https://api.razorpay.com"],
19
+ stripe: ["https://api.stripe.com"],
20
+ braintree: ["https://api.braintreegateway.com"],
21
+ };
22
+ /**
23
+ * discover.ts — static, read-only phase of ledger mode.
24
+ * Heuristically finds money-moving endpoints (routes whose path/handler match a
25
+ * money keyword, or whose file imports a payment SDK) and the expected payload
26
+ * shape. This NEVER runs the app and never touches the network.
27
+ */
28
+ export function discover(repo) {
29
+ const repoSdkImports = new Set();
30
+ const endpoints = [];
31
+ for (const file of walkFiles(repo, ROUTE_EXTS)) {
32
+ safeScanFile(repo, file, repoSdkImports, endpoints);
33
+ }
34
+ // Keep only money-moving candidates.
35
+ const money = endpoints.filter((e) => KEYWORDS.some((k) => e.matchedKeyword === k));
36
+ const sdkImports = [...repoSdkImports];
37
+ const gatewayHosts = [
38
+ ...new Set(sdkImports.flatMap((s) => SDK_TO_HOSTS[s] ?? [])),
39
+ ];
40
+ // Surface the repo-level payment-SDK set on every endpoint so the report is
41
+ // accurate even when the SDK is imported transitively (e.g. a routes file
42
+ // requiring a gateway wrapper that imports razorpay).
43
+ for (const ep of money)
44
+ ep.sdkImports = sdkImports;
45
+ return { endpoints: money, sdkImports, gatewayHosts };
46
+ }
47
+ function safeScanFile(repo, file, repoSdkImports, out) {
48
+ let content;
49
+ try {
50
+ content = fs.readFileSync(file, "utf8");
51
+ }
52
+ catch {
53
+ return;
54
+ }
55
+ const sdkImports = detectSdkImports(content);
56
+ for (const s of sdkImports)
57
+ repoSdkImports.add(s);
58
+ const routes = findRoutesInFile(content, path.extname(file).toLowerCase());
59
+ for (const r of routes) {
60
+ const kw = KEYWORDS.find((k) => r.path.toLowerCase().includes(k))
61
+ ?? keywordFromContext(content, r);
62
+ if (!kw)
63
+ continue;
64
+ const rel = path.relative(repo, file);
65
+ out.push(buildEndpoint(r, file, rel, sdkImports, kw, content));
66
+ }
67
+ }
68
+ function frameworkName(ext) {
69
+ switch (ext) {
70
+ case ".js":
71
+ case ".jsx":
72
+ case ".ts":
73
+ case ".tsx":
74
+ case ".mjs":
75
+ case ".cjs":
76
+ return "express/fastify";
77
+ case ".py":
78
+ return "python";
79
+ default:
80
+ return ext.slice(1);
81
+ }
82
+ }
83
+ function buildEndpoint(r, absFile, relFile, sdkImports, kw, content) {
84
+ const c = content.toLowerCase();
85
+ const idemIn = /\bidempotency\b/.test(c) ? "Idempotency-Key" : undefined;
86
+ const amountIn = /\bamounts?\b/.test(c) ? "amount" : undefined;
87
+ const currencyIn = /\bcurrencies?\b/.test(c) ? "currency" : undefined;
88
+ const orderIn = /\border_?id\b|\borderid\b/.test(c) ? "order_id" : undefined;
89
+ return {
90
+ method: r.method,
91
+ path: r.path,
92
+ file: absFile,
93
+ line: r.line,
94
+ framework: frameworkName(path.extname(absFile).toLowerCase()),
95
+ matchedKeyword: kw,
96
+ sdkImports: sdkImports.length ? sdkImports : [],
97
+ expectedPayload: {
98
+ idempotencyKeyHeader: idemIn,
99
+ amountField: amountIn,
100
+ currencyField: currencyIn,
101
+ orderIdField: orderIn,
102
+ },
103
+ webhook: r.path.toLowerCase().includes("webhook"),
104
+ };
105
+ }
106
+ function keywordFromContext(content, r) {
107
+ const before = content.slice(Math.max(0, r.line - 3), r.line).toLowerCase();
108
+ for (const k of KEYWORDS)
109
+ if (before.includes(k))
110
+ return k;
111
+ return undefined;
112
+ }
113
+ function detectSdkImports(content) {
114
+ const found = new Set();
115
+ for (const sdk of SDK_IMPORTS) {
116
+ const re = new RegExp(`(?:require\\s*\\(\\s*['"\`]${sdk}|from\\s+['"]${sdk}|import\\s+['"]${sdk})`, "i");
117
+ if (re.test(content))
118
+ found.add(sdk);
119
+ }
120
+ return [...found];
121
+ }
122
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../../src/analyzers/ledger/discover.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import type { LedgerEndpoint, LedgerEvidence } from "../types.js";
2
+ import type { AttackReport } from "./types.js";
3
+ export interface EvidenceInput {
4
+ repo: string;
5
+ discovery: {
6
+ endpoints: LedgerEndpoint[];
7
+ };
8
+ attacks: AttackReport;
9
+ gatewayLogPath: string;
10
+ }
11
+ export interface EvidenceOutput {
12
+ evidence: LedgerEvidence[];
13
+ note?: string;
14
+ }
15
+ /**
16
+ * evidence.ts — counts how many times the mocked gateway actually received a
17
+ * "charge" call for each idempotency key. Any key with >1 charge call is a
18
+ * PROVEN double-charge. Reads the gateway's own receipt log (written by the
19
+ * nock preload), so nothing here is simulated or fabricated.
20
+ */
21
+ export declare function analyzeEvidence(input: EvidenceInput): EvidenceOutput;
22
+ export declare function writeEvidenceFile(filePath: string, evidence: LedgerEvidence[], gatewayLogPath: string): void;
@@ -0,0 +1,127 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const SCENARIO_LABEL = {
4
+ "duplicate-webhook": "webhook replay",
5
+ "concurrent-double-submit": "concurrent double-submit",
6
+ "delayed-retry": "delayed retry (client timeout)",
7
+ };
8
+ const CHARGE_GROUPS_TO_REPORT = 20;
9
+ /**
10
+ * evidence.ts — counts how many times the mocked gateway actually received a
11
+ * "charge" call for each idempotency key. Any key with >1 charge call is a
12
+ * PROVEN double-charge. Reads the gateway's own receipt log (written by the
13
+ * nock preload), so nothing here is simulated or fabricated.
14
+ */
15
+ export function analyzeEvidence(input) {
16
+ const entries = readGatewayLog(input.gatewayLogPath);
17
+ if (entries.length === 0) {
18
+ return {
19
+ evidence: [],
20
+ note: "no gateway calls were recorded — the app may not have reached the payment " +
21
+ "gateway, or an outbound call may have been blocked. Check the ledger run logs.",
22
+ };
23
+ }
24
+ const chargeCalls = entries.filter((e) => e.charge);
25
+ const byKey = new Map();
26
+ for (const e of chargeCalls) {
27
+ const k = e.key || "(no-key)";
28
+ if (!byKey.has(k))
29
+ byKey.set(k, []);
30
+ byKey.get(k).push(e);
31
+ }
32
+ const evidence = [];
33
+ for (const [key, calls] of byKey) {
34
+ if (calls.length < 2)
35
+ continue; // single charge — idempotency held
36
+ const run = matchRun(input.attacks, key, calls);
37
+ const endpoint = run
38
+ ? input.discovery.endpoints[run.endpointIndex]
39
+ : undefined;
40
+ const scenarioLabel = run ? SCENARIO_LABEL[run.scenario] : "gateway replay";
41
+ const orderId = (run && run.orderId) || String(calls[0].orderId || key);
42
+ const idemKey = (run && run.idempotencyKey) || key;
43
+ const scenario = (run && run.scenario) || "duplicate-webhook";
44
+ const gapMs = calls.length > 1 ? msBetween(calls[0], calls[1]) : null;
45
+ const keyFrag = run && run.idempotencyKey ? ` (idempotency key "${idemKey}")` : "";
46
+ const summary = `order ${orderId} charged ${calls.length} times via ${scenarioLabel}` +
47
+ `${keyFrag}, ${gapMs}ms apart`;
48
+ evidence.push({
49
+ orderId,
50
+ idempotencyKey: idemKey,
51
+ scenario,
52
+ endpoint: endpoint ? endpoint.path : "(unknown endpoint)",
53
+ endpointFile: endpoint && endpoint.file ? path.relative(input.repo, endpoint.file) : undefined,
54
+ doubleCharged: true,
55
+ chargeCalls: calls,
56
+ summary,
57
+ });
58
+ }
59
+ const doubleCharges = evidence.length;
60
+ const keyNote = doubleCharges > 0
61
+ ? `${doubleCharges} proven double-charge(s) observed at the mocked gateway`
62
+ : undefined;
63
+ evidence.sort((a, b) => b.chargeCalls.length - a.chargeCalls.length);
64
+ const trimmed = evidence.slice(0, CHARGE_GROUPS_TO_REPORT);
65
+ return { evidence: trimmed, note: keyNote };
66
+ }
67
+ function readGatewayLog(p) {
68
+ const out = [];
69
+ if (!fs.existsSync(p))
70
+ return out;
71
+ for (const line of fs.readFileSync(p, "utf8").split(/\r?\n/)) {
72
+ const t = line.trim();
73
+ if (!t)
74
+ continue;
75
+ try {
76
+ out.push(JSON.parse(t));
77
+ }
78
+ catch {
79
+ /* skip malformed */
80
+ }
81
+ }
82
+ return out;
83
+ }
84
+ function matchRun(attacks, key, calls) {
85
+ // Prefer an exact identifier match, then a substring match, across the runs.
86
+ const ids = new Set();
87
+ for (const c of calls) {
88
+ if (c.orderId)
89
+ ids.add(c.orderId);
90
+ if (c.key)
91
+ ids.add(c.key);
92
+ }
93
+ ids.add(key);
94
+ let exact;
95
+ let sub;
96
+ for (const run of attacks.runs) {
97
+ for (const id of run.identifiers) {
98
+ if (ids.has(id)) {
99
+ if (!exact)
100
+ exact = run;
101
+ }
102
+ else if (key.length && (key.includes(id) || id.includes(key))) {
103
+ if (!sub)
104
+ sub = run;
105
+ }
106
+ }
107
+ }
108
+ return exact || sub;
109
+ }
110
+ function msBetween(a, b) {
111
+ const ta = new Date(a.at).getTime();
112
+ const tb = new Date(b.at).getTime();
113
+ if (!Number.isFinite(ta) || !Number.isFinite(tb))
114
+ return Math.round(Math.abs(tb - ta));
115
+ return Math.round(tb - ta);
116
+ }
117
+ export function writeEvidenceFile(filePath, evidence, gatewayLogPath) {
118
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
119
+ const doc = {
120
+ generatedAt: new Date().toISOString(),
121
+ gatewayLogPath,
122
+ doubleCharges: evidence.length,
123
+ evidence,
124
+ };
125
+ fs.writeFileSync(filePath, JSON.stringify(doc, null, 2));
126
+ }
127
+ //# sourceMappingURL=evidence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evidence.js","sourceRoot":"","sources":["../../../src/analyzers/ledger/evidence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { HarnessResult } from "./types.js";
2
+ export declare function startHarness(repo: string, gatewayHosts: string[]): Promise<HarnessResult>;
3
+ export declare function newEvidencePath(repo: string, ts: string): string;
4
+ export declare function readAbortReason(controlPath: string): string | null;
@@ -0,0 +1,270 @@
1
+ import { execa } from "execa";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import net from "node:net";
6
+ import { detectLanguage } from "../util.js";
7
+ import { startRecordingProxy } from "../../sandbox/proxy.js";
8
+ import { isNodeCommand, resolveNativeStart, resolveNodeStart, } from "../../sandbox/startCmd.js";
9
+ const STARTUP_TIMEOUT_MS = 25_000;
10
+ /** Absolute path of the nock sandbox preload that ships with the package. */
11
+ function preloadPath() {
12
+ return fileURLToPath(new URL("../../../templates/ledger/preload.cjs", import.meta.url));
13
+ }
14
+ function freePort() {
15
+ return new Promise((resolve, reject) => {
16
+ const s = net.createServer();
17
+ s.unref();
18
+ s.on("error", reject);
19
+ s.listen(0, () => {
20
+ const p = s.address().port;
21
+ s.close(() => resolve(p));
22
+ });
23
+ });
24
+ }
25
+ async function waitForServer(baseUrl, exited, controlPath, timeoutMs) {
26
+ const deadline = Date.now() + timeoutMs;
27
+ const armed = async () => {
28
+ if (!fs.existsSync(controlPath))
29
+ return false;
30
+ try {
31
+ return fs
32
+ .readFileSync(controlPath, "utf8")
33
+ .split(/\r?\n/)
34
+ .some((l) => l.includes('"event":"armed"'));
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ };
40
+ while (Date.now() < deadline) {
41
+ const aborted = controlAborted(controlPath);
42
+ if (aborted)
43
+ return { up: false, aborted: true };
44
+ if (exited()) {
45
+ return { up: false, aborted: controlAborted(controlPath) };
46
+ }
47
+ if ((await armed()) && baseUrl) {
48
+ try {
49
+ const res = await fetch(`${baseUrl}/__pitstop_ledger_probe__`);
50
+ if (res)
51
+ return { up: true, aborted: false };
52
+ }
53
+ catch {
54
+ /* not up yet */
55
+ }
56
+ }
57
+ await new Promise((r) => setTimeout(r, 150));
58
+ }
59
+ return { up: false, aborted: controlAborted(controlPath) };
60
+ }
61
+ function controlAborted(controlPath) {
62
+ if (!fs.existsSync(controlPath))
63
+ return false;
64
+ try {
65
+ return fs
66
+ .readFileSync(controlPath, "utf8")
67
+ .split(/\r?\n/)
68
+ .some((l) => l.includes('"event":"abort"'));
69
+ }
70
+ catch {
71
+ return false;
72
+ }
73
+ }
74
+ export async function startHarness(repo, gatewayHosts) {
75
+ const lang = detectLanguage(repo);
76
+ const nodeMode = lang === "js";
77
+ // Java/Dart clients do not honor HTTP_PROXY by default — a proxy sandbox
78
+ // there cannot claim to intercept gateway traffic, so refuse honestly.
79
+ if (lang === "unknown") {
80
+ return {
81
+ harness: null,
82
+ aborted: true,
83
+ abortReason: "could not identify the repo language — refusing to run ledger mode",
84
+ };
85
+ }
86
+ if (!nodeMode && (lang === "java" || lang === "dart")) {
87
+ return {
88
+ harness: null,
89
+ aborted: true,
90
+ abortReason: `--ledger cannot guarantee interception for ${lang} apps: ${lang} HTTP clients do not honor ` +
91
+ "HTTP_PROXY by default, and the nock preload cannot load into a non-Node process. " +
92
+ "Run ledger mode on a Node/JS, Go, Python, Rust, or .NET app.",
93
+ };
94
+ }
95
+ const workDir = path.join(repo, ".pitstop", "ledger");
96
+ fs.mkdirSync(workDir, { recursive: true });
97
+ const ts = new Date().toISOString().replace(/[:.]/g, "-");
98
+ const runDir = path.join(workDir, `run-${ts}`);
99
+ fs.mkdirSync(runDir, { recursive: true });
100
+ const gatewayLogPath = path.join(runDir, "gateway.log.jsonl");
101
+ const controlPath = path.join(runDir, "control.jsonl");
102
+ const appOut = path.join(runDir, "app.out.log");
103
+ const port = await freePort();
104
+ const baseUrl = `http://127.0.0.1:${port}`;
105
+ let start = null;
106
+ try {
107
+ if (nodeMode) {
108
+ start = resolveNodeStart(repo);
109
+ if (!isNodeCommand(start.cmd)) {
110
+ return {
111
+ harness: null,
112
+ aborted: true,
113
+ abortReason: `start script runs "${start.cmd}", which is not a Node-based runtime — ` +
114
+ "outbound HTTP from it cannot be guaranteed to be intercepted; refusing to run ledger mode",
115
+ };
116
+ }
117
+ }
118
+ else {
119
+ start = resolveNativeStart(repo, lang, port);
120
+ }
121
+ }
122
+ catch (e) {
123
+ return {
124
+ harness: null,
125
+ aborted: false,
126
+ abortReason: e.message,
127
+ };
128
+ }
129
+ if (!start) {
130
+ return {
131
+ harness: null,
132
+ aborted: false,
133
+ abortReason: `could not determine a start command for a ${lang} repo — set PITSTOP_START ` +
134
+ `(e.g. "PITSTOP_START=uvicorn app.main:app") and re-run`,
135
+ };
136
+ }
137
+ // Non-Node sandbox: HTTP(S)_PROXY recording proxy that mocks the gateway
138
+ // hosts and writes receipts to the same gateway.log.jsonl contract.
139
+ let proxy = null;
140
+ if (!nodeMode) {
141
+ try {
142
+ proxy = await startRecordingProxy({
143
+ logPath: path.join(runDir, "outbound.jsonl"),
144
+ controlPath,
145
+ gatewayLogPath,
146
+ gatewayHosts,
147
+ canarySuffix: "pitstop.invalid",
148
+ });
149
+ }
150
+ catch (e) {
151
+ return {
152
+ harness: null,
153
+ aborted: true,
154
+ abortReason: `could not start the recording proxy: ${e.message}`,
155
+ };
156
+ }
157
+ }
158
+ else if (!fs.existsSync(preloadPath())) {
159
+ return {
160
+ harness: null,
161
+ aborted: true,
162
+ abortReason: `ledger preload missing at ${preloadPath()}; cannot guarantee interception`,
163
+ };
164
+ }
165
+ const baseEnv = {
166
+ ...process.env,
167
+ PORT: String(port),
168
+ PITSTOP_LEDGER_CONTROL: controlPath,
169
+ PITSTOP_LEDGER_GATEWAY_LOG: gatewayLogPath,
170
+ PITSTOP_LEDGER_GATEWAY_HOSTS: gatewayHosts.join(","),
171
+ // Fake credentials — requests are intercepted before they reach a real gateway.
172
+ RAZORPAY_KEY_ID: process.env.RAZORPAY_KEY_ID || "rzp_test_pitstop000000",
173
+ RAZORPAY_KEY_SECRET: process.env.RAZORPAY_KEY_SECRET || "pitstop_fake_secret",
174
+ STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY || "sk_test_pitstop_fake",
175
+ NODE_ENV: process.env.NODE_ENV || "test",
176
+ };
177
+ const env = nodeMode
178
+ ? {
179
+ ...baseEnv,
180
+ NODE_OPTIONS: [process.env.NODE_OPTIONS, `--require=${preloadPath()}`]
181
+ .filter(Boolean)
182
+ .join(" "),
183
+ }
184
+ : {
185
+ ...baseEnv,
186
+ HTTP_PROXY: `http://127.0.0.1:${proxy.port}`,
187
+ HTTPS_PROXY: `http://127.0.0.1:${proxy.port}`,
188
+ http_proxy: `http://127.0.0.1:${proxy.port}`,
189
+ https_proxy: `http://127.0.0.1:${proxy.port}`,
190
+ NO_PROXY: "localhost,127.0.0.1,::1",
191
+ no_proxy: "localhost,127.0.0.1,::1",
192
+ };
193
+ const child = execa(start.cmd, start.args, {
194
+ cwd: repo,
195
+ env,
196
+ stdio: ["ignore", "pipe", "pipe"],
197
+ windowsHide: true,
198
+ reject: false,
199
+ maxBuffer: 50 * 1024 * 1024,
200
+ });
201
+ // execa's subprocess is a promise (never rejects with `reject: false`);
202
+ // track completion so the poll loop can detect an early exit.
203
+ let exited = false;
204
+ child
205
+ .then(() => {
206
+ exited = true;
207
+ })
208
+ .catch(() => {
209
+ exited = true;
210
+ });
211
+ const outFd = fs.openSync(appOut, "a");
212
+ const pump = (chunk) => fs.writeSync(outFd, typeof chunk === "string" ? chunk : chunk.toString());
213
+ child.stdout?.on("data", pump);
214
+ child.stderr?.on("data", pump);
215
+ const { up, aborted } = await waitForServer(baseUrl, () => exited, controlPath, STARTUP_TIMEOUT_MS);
216
+ if (!up) {
217
+ const reason = (controlAborted(controlPath) ? readAbortReason(controlPath) : null) ??
218
+ `app did not come up within ${STARTUP_TIMEOUT_MS / 1000}s (start: "${start.cmd} ${start.args.join(" ")}")`;
219
+ try {
220
+ child.kill();
221
+ }
222
+ catch {
223
+ /* ignore */
224
+ }
225
+ if (proxy)
226
+ proxy.close().catch(() => { });
227
+ return { harness: null, aborted, abortReason: reason };
228
+ }
229
+ const harness = {
230
+ port,
231
+ baseUrl,
232
+ gatewayLogPath,
233
+ controlPath,
234
+ close: () => new Promise((resolve) => {
235
+ try {
236
+ child.kill();
237
+ }
238
+ catch {
239
+ /* ignore */
240
+ }
241
+ fs.closeSync(outFd);
242
+ if (proxy)
243
+ proxy.close().catch(() => { });
244
+ resolve();
245
+ }),
246
+ };
247
+ return { harness, aborted: false };
248
+ }
249
+ export function newEvidencePath(repo, ts) {
250
+ return path.join(repo, ".pitstop", `ledger-evidence-${ts}.json`);
251
+ }
252
+ export function readAbortReason(controlPath) {
253
+ try {
254
+ for (const line of fs.readFileSync(controlPath, "utf8").split(/\r?\n/)) {
255
+ try {
256
+ const e = JSON.parse(line);
257
+ if (e.event === "abort" && e.reason)
258
+ return e.reason;
259
+ }
260
+ catch {
261
+ /* skip */
262
+ }
263
+ }
264
+ }
265
+ catch {
266
+ /* ignore */
267
+ }
268
+ return null;
269
+ }
270
+ //# sourceMappingURL=harness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"harness.js","sourceRoot":"","sources":["../../../src/analyzers/ledger/harness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { LedgerResult } from "../types.js";
2
+ /**
3
+ * ledger.ts — orchestrates `pitstop scan --ledger`.
4
+ *
5
+ * This is invasive (it boots the app and fires live traffic at it), so it is
6
+ * ONLY ever invoked from the CLI when the user explicitly passes `--ledger`.
7
+ * Every outbound HTTP call from the app is routed through a nock sandbox before
8
+ * it can leave the process; the request/response pairs the "gateway" receives
9
+ * are recorded so evidence of a double-charge is real, not simulated.
10
+ */
11
+ export declare function runLedgerAnalyzer(repo: string): Promise<LedgerResult>;
12
+ export { discover } from "./discover.js";
13
+ export { runAttacks } from "./attacks.js";
14
+ export { analyzeEvidence } from "./evidence.js";