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,198 @@
1
+ import { execaSync } from "execa";
2
+ import { parse } from "@babel/parser";
3
+ /**
4
+ * exceptionSwallow — AST-scan added try/catch (JS/TS) or try/except (Python) blocks.
5
+ *
6
+ * SUSPICIOUS ONLY (never CONFIRMED — legitimate error handling is common):
7
+ * - the try/catch body is empty
8
+ * - the catch only logs (no re-raise / no real handling)
9
+ * - the catch unconditionally returns a default/success value
10
+ */
11
+ const PY_SCRIPT = `
12
+ import ast, json, sys
13
+
14
+ code = sys.stdin.read()
15
+ try:
16
+ tree = ast.parse(code)
17
+ except Exception:
18
+ print(json.dumps([]))
19
+ sys.exit(0)
20
+
21
+ LOG_NAMES = {"print", "logging", "logger", "log"}
22
+
23
+ def is_log(stmt):
24
+ if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Call):
25
+ f = stmt.value.func
26
+ name = f.id if isinstance(f, ast.Name) else (f.attr if isinstance(f, ast.Attribute) else None)
27
+ if name in LOG_NAMES or (isinstance(name, str) and name.startswith("log")):
28
+ return True
29
+ return False
30
+
31
+ def reason_for(body):
32
+ if not body:
33
+ return "empty catch swallows the error"
34
+ if all(is_log(s) for s in body):
35
+ return "catch only logs the error and does not re-raise or handle it"
36
+ if len(body) == 1 and isinstance(body[0], ast.Return) and isinstance(body[0].value, ast.Constant):
37
+ return "catch unconditionally returns a default/success value"
38
+ return None
39
+
40
+ out = []
41
+ for node in ast.walk(tree):
42
+ if isinstance(node, ast.Try):
43
+ for h in node.handlers:
44
+ r = reason_for(h.body)
45
+ if r:
46
+ out.append({"line": h.lineno, "reason": r})
47
+ break
48
+ else:
49
+ if not node.body:
50
+ out.append({"line": node.lineno, "reason": "empty try block"})
51
+ print(json.dumps(out))
52
+ `;
53
+ function isNewlyAdded(change, line) {
54
+ if (change.status === "added")
55
+ return true;
56
+ return change.added.some((l) => l.line === line);
57
+ }
58
+ function isLogStatement(stmt) {
59
+ if (!stmt || stmt.type !== "ExpressionStatement")
60
+ return false;
61
+ const expr = stmt.expression;
62
+ if (!expr || expr.type !== "CallExpression")
63
+ return false;
64
+ const callee = expr.callee;
65
+ let name = null;
66
+ if (callee.type === "Identifier")
67
+ name = callee.name;
68
+ else if (callee.type === "MemberExpression" && callee.property.type === "Identifier") {
69
+ name = callee.property.name;
70
+ }
71
+ if (!name)
72
+ return false;
73
+ if (/^(log|warn|error|info|debug)/i.test(name))
74
+ return true;
75
+ if (["console", "logger", "log", "logging", "print"].includes(name))
76
+ return true;
77
+ return false;
78
+ }
79
+ function classifyCatch(body) {
80
+ if (!body || body.length === 0)
81
+ return "empty catch swallows the error";
82
+ if (body.every(isLogStatement)) {
83
+ return "catch only logs the error and does not re-raise or handle it";
84
+ }
85
+ if (body.length === 1 && body[0].type === "ReturnStatement") {
86
+ const arg = body[0].argument;
87
+ if (arg &&
88
+ (arg.type === "Literal" ||
89
+ arg.type === "ArrayExpression" ||
90
+ arg.type === "ObjectExpression")) {
91
+ return "catch unconditionally returns a default/success value";
92
+ }
93
+ }
94
+ return null;
95
+ }
96
+ function walk(node, cb) {
97
+ if (!node || typeof node !== "object")
98
+ return;
99
+ cb(node);
100
+ for (const key of Object.keys(node)) {
101
+ const val = node[key];
102
+ if (Array.isArray(val))
103
+ val.forEach((v) => walk(v, cb));
104
+ else if (val && typeof val === "object" && typeof val.type === "string")
105
+ walk(val, cb);
106
+ }
107
+ }
108
+ function detectJs(change) {
109
+ const out = [];
110
+ if (!change.after)
111
+ return out;
112
+ let ast;
113
+ try {
114
+ ast = parse(change.after, {
115
+ sourceType: "module",
116
+ errorRecovery: true,
117
+ plugins: change.language === "ts"
118
+ ? ["typescript", "jsx"]
119
+ : ["jsx"],
120
+ });
121
+ }
122
+ catch {
123
+ return out;
124
+ }
125
+ walk(ast, (n) => {
126
+ if (n.type !== "TryStatement")
127
+ return;
128
+ const tryLine = n.loc?.start?.line;
129
+ const handler = n.handler;
130
+ const handlerLine = handler?.loc?.start?.line ?? tryLine;
131
+ if (!isNewlyAdded(change, tryLine) && !isNewlyAdded(change, handlerLine))
132
+ return;
133
+ let reason = null;
134
+ if (!n.block || n.block.body.length === 0) {
135
+ reason = "empty try block";
136
+ }
137
+ else if (handler) {
138
+ reason = classifyCatch(handler.body?.body ?? null);
139
+ }
140
+ else {
141
+ // try/finally with no catch — only suspicious if the try body is empty.
142
+ reason = null;
143
+ }
144
+ if (reason) {
145
+ out.push({
146
+ detector: "exceptionSwallow",
147
+ pattern: "error-swallowed",
148
+ confidence: "suspicious",
149
+ file: change.path,
150
+ line: handlerLine ?? tryLine,
151
+ evidence: `newly added try/catch: ${reason}`,
152
+ });
153
+ }
154
+ });
155
+ return out;
156
+ }
157
+ function detectPython(change) {
158
+ const out = [];
159
+ if (!change.after)
160
+ return out;
161
+ try {
162
+ const res = execaSync("python", ["-c", PY_SCRIPT], {
163
+ input: change.after,
164
+ encoding: "utf8",
165
+ windowsHide: true,
166
+ reject: false,
167
+ timeout: 15000,
168
+ });
169
+ const hits = JSON.parse(res.stdout);
170
+ for (const h of hits) {
171
+ if (isNewlyAdded(change, h.line)) {
172
+ out.push({
173
+ detector: "exceptionSwallow",
174
+ pattern: "error-swallowed",
175
+ confidence: "suspicious",
176
+ file: change.path,
177
+ line: h.line,
178
+ evidence: `newly added try/except: ${h.reason}`,
179
+ });
180
+ }
181
+ }
182
+ }
183
+ catch {
184
+ // python unavailable or parse error — skip silently.
185
+ }
186
+ return out;
187
+ }
188
+ export function detectExceptionSwallow(changes) {
189
+ const findings = [];
190
+ for (const c of changes) {
191
+ if (c.language === "python")
192
+ findings.push(...detectPython(c));
193
+ else if (c.language === "js" || c.language === "ts")
194
+ findings.push(...detectJs(c));
195
+ }
196
+ return findings;
197
+ }
198
+ //# sourceMappingURL=exceptionSwallow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exceptionSwallow.js","sourceRoot":"","sources":["../../../src/analyzers/integrity/exceptionSwallow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { FileChange, IntegrityFinding } from "./types.js";
2
+ export declare function detectExitCheat(changes: FileChange[]): IntegrityFinding[];
@@ -0,0 +1,31 @@
1
+ import { isAppCode } from "./helpers.js";
2
+ /**
3
+ * exitCheat — a `process.exit(0)` / `sys.exit(0)` / `os._exit(0)` added inside
4
+ * application code is the one unambiguous cheat: it can force a "success" exit
5
+ * to hide a failing run. CLI entrypoints and test-runner configs are excluded
6
+ * because `process.exit(0)` is legitimate there.
7
+ *
8
+ * CONFIRMED (unambiguous, auto-block) — but only for exit(0) with no context.
9
+ */
10
+ const EXIT_CHEAT_RE = /\b(?:process\.exit|sys\.exit|os\._exit)\s*\(\s*0\s*\)/;
11
+ export function detectExitCheat(changes) {
12
+ const findings = [];
13
+ for (const c of changes) {
14
+ if (!isAppCode(c.path))
15
+ continue;
16
+ for (const a of c.added) {
17
+ if (EXIT_CHEAT_RE.test(a.text)) {
18
+ findings.push({
19
+ detector: "exitCheat",
20
+ pattern: "process-exit-0-in-app-code",
21
+ confidence: "confirmed",
22
+ file: c.path,
23
+ line: a.line,
24
+ evidence: `added ${a.text.trim().split(/\s+/)[0].split("(")[0]}(0) in application code — forces a success exit to hide failure`,
25
+ });
26
+ }
27
+ }
28
+ }
29
+ return findings;
30
+ }
31
+ //# sourceMappingURL=exitCheat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exitCheat.js","sourceRoot":"","sources":["../../../src/analyzers/integrity/exitCheat.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC;;;;;;;GAOG;AACH,MAAM,aAAa,GAAG,uDAAuD,CAAC;AAE9E,MAAM,UAAU,eAAe,CAAC,OAAqB;IACnD,MAAM,QAAQ,GAAuB,EAAE,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,SAAS;QACjC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACxB,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC/B,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,WAAW;oBACrB,OAAO,EAAE,4BAA4B;oBACrC,UAAU,EAAE,WAAW;oBACvB,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,IAAI,EAAE,CAAC,CAAC,IAAI;oBACZ,QAAQ,EAAE,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,iEAAiE;iBAChI,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { languageOf } from "./helpers.js";
2
+ import type { FileChange } from "./types.js";
3
+ /** Get all changed files for the given ref range. */
4
+ export declare function getDiff(repo: string, from: string, to?: string): FileChange[];
5
+ /** Resolve default refs. `from` defaults to HEAD~1 (or HEAD if that's invalid). */
6
+ export declare function resolveRefs(repo: string, from?: string, to?: string): {
7
+ from: string;
8
+ to?: string;
9
+ };
10
+ export { languageOf };
@@ -0,0 +1,181 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { safeExec } from "../util.js";
4
+ import { languageOf, makeChange, linesToDiff } from "./helpers.js";
5
+ function gitOk(repo, args) {
6
+ return safeExec("git", args, repo).code === 0;
7
+ }
8
+ function normalizeEol(text) {
9
+ return text.replace(/\r\n/g, "\n");
10
+ }
11
+ function showAtRef(repo, ref, file) {
12
+ const r = safeExec("git", ["show", `${ref}:${file}`], repo);
13
+ if (r.code !== 0)
14
+ return undefined;
15
+ return normalizeEol(r.stdout);
16
+ }
17
+ function readWorking(repo, file) {
18
+ try {
19
+ return normalizeEol(fs.readFileSync(path.join(repo, file), "utf8"));
20
+ }
21
+ catch {
22
+ return undefined;
23
+ }
24
+ }
25
+ function stripPrefix(p) {
26
+ // git diff prints "a/foo" / "b/foo" (or "/dev/null").
27
+ if (p === "/dev/null")
28
+ return "/dev/null";
29
+ return p.replace(/^(a\/|b\/)/, "");
30
+ }
31
+ /** Parse a unified diff (-U0) into per-file added/removed lines + status. */
32
+ function parseUnified(text) {
33
+ const files = [];
34
+ let cur = null;
35
+ let oldLine = 0;
36
+ let newLine = 0;
37
+ const flush = () => {
38
+ if (cur)
39
+ files.push(cur);
40
+ cur = null;
41
+ };
42
+ for (const raw of text.split(/\r?\n/)) {
43
+ if (raw.startsWith("diff --git")) {
44
+ flush();
45
+ cur = { oldPath: "", newPath: "", status: "modified", added: [], removed: [] };
46
+ const m = raw.match(/diff --git a\/(.*) b\/(.*)$/);
47
+ if (m) {
48
+ cur.oldPath = stripPrefix("a/" + m[1]);
49
+ cur.newPath = stripPrefix("b/" + m[2]);
50
+ }
51
+ continue;
52
+ }
53
+ if (!cur)
54
+ continue;
55
+ if (raw.startsWith("new file")) {
56
+ cur.status = "added";
57
+ continue;
58
+ }
59
+ if (raw.startsWith("deleted file")) {
60
+ cur.status = "deleted";
61
+ continue;
62
+ }
63
+ if (raw.startsWith("rename from")) {
64
+ cur.oldPath = stripPrefix(raw.slice("rename from ".length).trim());
65
+ cur.status = "renamed";
66
+ continue;
67
+ }
68
+ if (raw.startsWith("rename to")) {
69
+ cur.newPath = stripPrefix(raw.slice("rename to ".length).trim());
70
+ continue;
71
+ }
72
+ if (raw.startsWith("--- ")) {
73
+ cur.oldPath = stripPrefix(raw.slice(4).trim());
74
+ continue;
75
+ }
76
+ if (raw.startsWith("+++ ")) {
77
+ cur.newPath = stripPrefix(raw.slice(4).trim());
78
+ continue;
79
+ }
80
+ if (raw.startsWith("@@")) {
81
+ const m = raw.match(/@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
82
+ if (m) {
83
+ oldLine = parseInt(m[1], 10);
84
+ newLine = parseInt(m[2], 10);
85
+ }
86
+ continue;
87
+ }
88
+ if (raw.startsWith("+") && !raw.startsWith("+++")) {
89
+ cur.added.push({ line: newLine++, text: raw.slice(1) });
90
+ }
91
+ else if (raw.startsWith("-") && !raw.startsWith("---")) {
92
+ cur.removed.push({ line: oldLine++, text: raw.slice(1) });
93
+ }
94
+ else if (raw.startsWith(" ")) {
95
+ oldLine++;
96
+ newLine++;
97
+ }
98
+ }
99
+ flush();
100
+ return files;
101
+ }
102
+ function mergeByNewPath(a, b) {
103
+ const byPath = new Map();
104
+ for (const f of [...a, ...b]) {
105
+ const key = f.newPath && f.newPath !== "/dev/null" ? f.newPath : f.oldPath;
106
+ const existing = byPath.get(key);
107
+ if (existing) {
108
+ existing.added.push(...f.added);
109
+ existing.removed.push(...f.removed);
110
+ if (f.status === "added" || f.status === "deleted")
111
+ existing.status = f.status;
112
+ }
113
+ else {
114
+ byPath.set(key, { ...f });
115
+ }
116
+ }
117
+ return [...byPath.values()];
118
+ }
119
+ /** Get all changed files for the given ref range. */
120
+ export function getDiff(repo, from, to) {
121
+ let parsed;
122
+ if (to) {
123
+ const r = safeExec("git", ["diff", "-U0", from, to], repo);
124
+ parsed = parseUnified(r.stdout);
125
+ }
126
+ else {
127
+ const unstaged = safeExec("git", ["diff", "-U0", from], repo).stdout;
128
+ const staged = safeExec("git", ["diff", "--cached", "-U0", from], repo).stdout;
129
+ parsed = mergeByNewPath(parseUnified(unstaged), parseUnified(staged));
130
+ // Untracked files count as additions against the working tree.
131
+ const others = safeExec("git", ["ls-files", "--others", "--exclude-standard"], repo).stdout;
132
+ for (const f of others.split(/\r?\n/).map((s) => s.trim()).filter(Boolean)) {
133
+ if (f.startsWith("node_modules/") || f.startsWith(".pitstop/") || f.startsWith("dist/")) {
134
+ continue;
135
+ }
136
+ const content = readWorking(repo, f);
137
+ if (content == null)
138
+ continue;
139
+ parsed.push({
140
+ oldPath: "/dev/null",
141
+ newPath: f,
142
+ status: "added",
143
+ added: linesToDiff(content),
144
+ removed: [],
145
+ });
146
+ }
147
+ }
148
+ const out = [];
149
+ for (const p of parsed) {
150
+ const newPath = p.newPath !== "/dev/null" ? p.newPath : p.oldPath;
151
+ const oldPath = p.oldPath !== "/dev/null" ? p.oldPath : p.newPath;
152
+ let before;
153
+ let after;
154
+ if (p.status === "added") {
155
+ before = undefined;
156
+ after = to ? showAtRef(repo, to, newPath) : readWorking(repo, newPath);
157
+ }
158
+ else if (p.status === "deleted") {
159
+ before = showAtRef(repo, from, oldPath);
160
+ after = undefined;
161
+ }
162
+ else {
163
+ before = showAtRef(repo, from, oldPath);
164
+ after = to ? showAtRef(repo, to, newPath) : readWorking(repo, newPath);
165
+ }
166
+ if (before == null && after == null)
167
+ continue;
168
+ out.push(makeChange(newPath, p.status, before, after, p.added, p.removed));
169
+ }
170
+ return out;
171
+ }
172
+ /** Resolve default refs. `from` defaults to HEAD~1 (or HEAD if that's invalid). */
173
+ export function resolveRefs(repo, from, to) {
174
+ let resolvedFrom = from ?? "";
175
+ if (!resolvedFrom) {
176
+ resolvedFrom = gitOk(repo, ["rev-parse", "--verify", "HEAD~1"]) ? "HEAD~1" : "HEAD";
177
+ }
178
+ return { from: resolvedFrom, to };
179
+ }
180
+ export { languageOf };
181
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../../src/analyzers/integrity/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { FileChange, IntegrityFinding } from "./types.js";
2
+ export declare function detectHardcodedMatch(changes: FileChange[]): IntegrityFinding[];
@@ -0,0 +1,93 @@
1
+ import { isTestFile } from "./helpers.js";
2
+ /**
3
+ * hardcodedMatch — cross-reference literals introduced in non-test (app) files
4
+ * against expected values asserted in the test files' diff.
5
+ *
6
+ * SUSPICIOUS ONLY (never CONFIRMED): matching a test's expected value is strong
7
+ * evidence the implementation was hardcoded to pass rather than correctly
8
+ * computed, but a human must confirm (e.g. a shared constant is a legit fixture).
9
+ */
10
+ const ASSERT_SINGLE_RE = /(?:toBe|toEqual|toStrictEqual|toMatch|toMatchObject)\s*\(\s*("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|-?\d+\.?\d*)\s*\)/g;
11
+ const ASSERT_TWO_RE = /(?:assertEqual|assertEquals|assert\.equal|assert\.strictEqual|assertDeepEqual|assert deepEqual|assertAlmostEqual|self\.assertEqual)\s*\([^,]*,\s*("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|-?\d+\.?\d*)\s*\)/g;
12
+ const ASSERT_PY_RE = /\bassert\s+[^=]*==\s*("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|-?\d+\.?\d*)/g;
13
+ const STR_RE = /'[^']*'|"[^"]*"/g;
14
+ const NUM_RE = /(?<!\w)(?:-?\d+\.\d+|-?\d+)(?!\w|\.)/g;
15
+ function normalize(raw) {
16
+ const t = raw.trim();
17
+ if ((t.startsWith('"') && t.endsWith('"')) || (t.startsWith("'") && t.endsWith("'"))) {
18
+ return { kind: "str", value: t.slice(1, -1) };
19
+ }
20
+ const n = Number(t);
21
+ if (!Number.isNaN(n))
22
+ return { kind: "num", value: n };
23
+ return null;
24
+ }
25
+ function trivial(n) {
26
+ if (n.kind === "num" && (n.value === 0 || n.value === 1))
27
+ return true;
28
+ if (n.kind === "str" && n.value === "")
29
+ return true;
30
+ return false;
31
+ }
32
+ export function detectHardcodedMatch(changes) {
33
+ const findings = [];
34
+ // App literals introduced in this change.
35
+ const appLiterals = [];
36
+ for (const c of changes) {
37
+ if (isTestFile(c.path))
38
+ continue;
39
+ for (const a of c.added) {
40
+ for (const m of a.text.matchAll(STR_RE)) {
41
+ const n = normalize(m[0]);
42
+ if (n && !trivial(n))
43
+ appLiterals.push({ norm: n, file: c.path, line: a.line });
44
+ }
45
+ for (const m of a.text.matchAll(NUM_RE)) {
46
+ const n = normalize(m[0]);
47
+ if (n && !trivial(n))
48
+ appLiterals.push({ norm: n, file: c.path, line: a.line });
49
+ }
50
+ }
51
+ }
52
+ // Expected values asserted in test files' diff.
53
+ const testExpected = [];
54
+ for (const c of changes) {
55
+ if (!isTestFile(c.path))
56
+ continue;
57
+ for (const a of c.added) {
58
+ for (const m of a.text.matchAll(ASSERT_SINGLE_RE)) {
59
+ const n = normalize(m[1]);
60
+ if (n)
61
+ testExpected.push({ norm: n, file: c.path, line: a.line });
62
+ }
63
+ for (const m of a.text.matchAll(ASSERT_TWO_RE)) {
64
+ const n = normalize(m[1]);
65
+ if (n)
66
+ testExpected.push({ norm: n, file: c.path, line: a.line });
67
+ }
68
+ for (const m of a.text.matchAll(ASSERT_PY_RE)) {
69
+ const n = normalize(m[1]);
70
+ if (n)
71
+ testExpected.push({ norm: n, file: c.path, line: a.line });
72
+ }
73
+ }
74
+ }
75
+ for (const app of appLiterals) {
76
+ for (const t of testExpected) {
77
+ if (app.norm.kind === t.norm.kind && app.norm.value === t.norm.value) {
78
+ findings.push({
79
+ detector: "hardcodedMatch",
80
+ pattern: "hardcoded-matches-test-expectation",
81
+ confidence: "suspicious",
82
+ file: app.file,
83
+ line: app.line,
84
+ evidence: `literal ${JSON.stringify(app.norm.value)} introduced in app code matches an expected value asserted in the test "${t.file}:${t.line}"`,
85
+ file2: t.file,
86
+ line2: t.line,
87
+ });
88
+ }
89
+ }
90
+ }
91
+ return findings;
92
+ }
93
+ //# sourceMappingURL=hardcodedMatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hardcodedMatch.js","sourceRoot":"","sources":["../../../src/analyzers/integrity/hardcodedMatch.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C;;;;;;;GAOG;AAEH,MAAM,gBAAgB,GACpB,qHAAqH,CAAC;AACxH,MAAM,aAAa,GACjB,0MAA0M,CAAC;AAC7M,MAAM,YAAY,GAChB,yEAAyE,CAAC;AAC5E,MAAM,MAAM,GAAG,kBAAkB,CAAC;AAClC,MAAM,MAAM,GAAG,uCAAuC,CAAC;AAIvD,SAAS,SAAS,CAAC,GAAW;IAC5B,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IACrB,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QACrF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAChD,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACpB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IACvD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,OAAO,CAAC,CAAO;IACtB,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACtE,IAAI,CAAC,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,KAAK,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACpD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAqB;IACxD,MAAM,QAAQ,GAAuB,EAAE,CAAC;IAExC,0CAA0C;IAC1C,MAAM,WAAW,GAAiD,EAAE,CAAC;IACrE,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,SAAS;QACjC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACxB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACxC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC1B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;oBAAE,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAClF,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACxC,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC1B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;oBAAE,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAClF,CAAC;QACH,CAAC;IACH,CAAC;IAED,gDAAgD;IAChD,MAAM,YAAY,GAAiD,EAAE,CAAC;IACtE,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;YACxB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAClD,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC1B,IAAI,CAAC;oBAAE,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACpE,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC/C,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC1B,IAAI,CAAC;oBAAE,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACpE,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC9C,MAAM,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBAC1B,IAAI,CAAC;oBAAE,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACpE,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;YAC7B,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACrE,QAAQ,CAAC,IAAI,CAAC;oBACZ,QAAQ,EAAE,gBAAgB;oBAC1B,OAAO,EAAE,oCAAoC;oBAC7C,UAAU,EAAE,YAAY;oBACxB,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,IAAI,EAAE,GAAG,CAAC,IAAI;oBACd,QAAQ,EAAE,WAAW,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,2EAA2E,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,GAAG;oBACjJ,KAAK,EAAE,CAAC,CAAC,IAAI;oBACb,KAAK,EAAE,CAAC,CAAC,IAAI;iBACd,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { FileChange, FileStatus, DiffLine } from "./types.js";
2
+ /** Map a file path to its language for analyzer selection. */
3
+ export declare function languageOf(path: string): "js" | "ts" | "python" | "unknown";
4
+ /** A test file: foo.test.js, foo.spec.tsx, __tests__/x, or test_*.py / *_test.py. */
5
+ export declare function isTestFile(path: string): boolean;
6
+ /**
7
+ * A "test runner config" that legitimately contains suppressions / exits and
8
+ * must not be flagged by suppressionCreep / exitCheat.
9
+ */
10
+ export declare function isTestRunnerConfig(path: string): boolean;
11
+ /**
12
+ * A legitimate CLI entrypoint (shebang / bin script) — `process.exit(0)` here is
13
+ * normal and must NOT be flagged as an exit cheat.
14
+ */
15
+ export declare function isCliEntrypoint(path: string): boolean;
16
+ /** Application code = not a test, not a test-runner config, not a CLI entrypoint. */
17
+ export declare function isAppCode(path: string): boolean;
18
+ /** Build a FileChange helper with added/removed lines and detected language. */
19
+ export declare function makeChange(path: string, status: FileStatus, before: string | undefined, after: string | undefined, added: DiffLine[], removed: DiffLine[]): FileChange;
20
+ /** Lines of a file version as DiffLine[] (used for fully-added files). */
21
+ export declare function linesToDiff(text: string): DiffLine[];
@@ -0,0 +1,66 @@
1
+ /** Map a file path to its language for analyzer selection. */
2
+ export function languageOf(path) {
3
+ const lower = path.toLowerCase();
4
+ if (lower.endsWith(".py") || lower.endsWith(".pyw"))
5
+ return "python";
6
+ if (lower.endsWith(".ts") ||
7
+ lower.endsWith(".tsx") ||
8
+ lower.endsWith(".mts") ||
9
+ lower.endsWith(".cts")) {
10
+ return "ts";
11
+ }
12
+ if (lower.endsWith(".js") ||
13
+ lower.endsWith(".jsx") ||
14
+ lower.endsWith(".mjs") ||
15
+ lower.endsWith(".cjs")) {
16
+ return "js";
17
+ }
18
+ return "unknown";
19
+ }
20
+ /** A test file: foo.test.js, foo.spec.tsx, __tests__/x, or test_*.py / *_test.py. */
21
+ export function isTestFile(path) {
22
+ const normalized = path.replace(/\\/g, "/");
23
+ if (/\.(test|spec)\.[jt]sx?$/.test(normalized))
24
+ return true;
25
+ if (/[\\/]__tests?__[\\/]/.test(normalized))
26
+ return true;
27
+ const base = normalized.split("/").pop() ?? "";
28
+ if (/^test_.*\.py$/.test(base) || /_test\.py$/.test(base))
29
+ return true;
30
+ return false;
31
+ }
32
+ /**
33
+ * A "test runner config" that legitimately contains suppressions / exits and
34
+ * must not be flagged by suppressionCreep / exitCheat.
35
+ */
36
+ export function isTestRunnerConfig(path) {
37
+ const normalized = path.replace(/\\/g, "/");
38
+ return /\.(config|conf)\.[jt]sx?$/.test(normalized) || /(^|\/)conftest\.py$/.test(normalized);
39
+ }
40
+ /**
41
+ * A legitimate CLI entrypoint (shebang / bin script) — `process.exit(0)` here is
42
+ * normal and must NOT be flagged as an exit cheat.
43
+ */
44
+ export function isCliEntrypoint(path) {
45
+ const normalized = path.replace(/\\/g, "/");
46
+ const base = (normalized.split("/").pop() ?? "").toLowerCase();
47
+ if (base === "cli.ts" || base === "cli.js" || base === "cli.mjs" || base === "cli.cjs") {
48
+ return true;
49
+ }
50
+ if (/[\\/]bin[\\/]/.test(normalized))
51
+ return true;
52
+ return false;
53
+ }
54
+ /** Application code = not a test, not a test-runner config, not a CLI entrypoint. */
55
+ export function isAppCode(path) {
56
+ return !isTestFile(path) && !isTestRunnerConfig(path) && !isCliEntrypoint(path);
57
+ }
58
+ /** Build a FileChange helper with added/removed lines and detected language. */
59
+ export function makeChange(path, status, before, after, added, removed) {
60
+ return { path, status, before, after, added, removed, language: languageOf(path) };
61
+ }
62
+ /** Lines of a file version as DiffLine[] (used for fully-added files). */
63
+ export function linesToDiff(text) {
64
+ return text.split(/\r?\n/).map((text, i) => ({ line: i + 1, text }));
65
+ }
66
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/analyzers/integrity/helpers.ts"],"names":[],"mappings":"AAEA,8DAA8D;AAC9D,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,QAAQ,CAAC;IACrE,IACE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;QACrB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QACtB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QACtB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EACtB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IACE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC;QACrB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QACtB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QACtB,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,EACtB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC5C,IAAI,yBAAyB,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5D,IAAI,sBAAsB,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IACzD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IAC/C,IAAI,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACvE,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC5C,OAAO,2BAA2B,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,qBAAqB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AAChG,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/D,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,eAAe,CAAC,IAAI,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;AAClF,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,UAAU,CACxB,IAAY,EACZ,MAAkB,EAClB,MAA0B,EAC1B,KAAyB,EACzB,KAAiB,EACjB,OAAmB;IAEnB,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;AACrF,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACvE,CAAC"}