@fourteensystems/prodcheck 0.3.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 (239) hide show
  1. package/README.md +252 -0
  2. package/bin/prodcheck.mjs +2 -0
  3. package/dist/cli/commands/baseline.d.ts +7 -0
  4. package/dist/cli/commands/baseline.d.ts.map +1 -0
  5. package/dist/cli/commands/baseline.js +22 -0
  6. package/dist/cli/commands/baseline.js.map +1 -0
  7. package/dist/cli/commands/ci.d.ts +14 -0
  8. package/dist/cli/commands/ci.d.ts.map +1 -0
  9. package/dist/cli/commands/ci.js +104 -0
  10. package/dist/cli/commands/ci.js.map +1 -0
  11. package/dist/cli/commands/explain.d.ts +2 -0
  12. package/dist/cli/commands/explain.d.ts.map +1 -0
  13. package/dist/cli/commands/explain.js +20 -0
  14. package/dist/cli/commands/explain.js.map +1 -0
  15. package/dist/cli/commands/init.d.ts +7 -0
  16. package/dist/cli/commands/init.d.ts.map +1 -0
  17. package/dist/cli/commands/init.js +127 -0
  18. package/dist/cli/commands/init.js.map +1 -0
  19. package/dist/cli/commands/rules.d.ts +2 -0
  20. package/dist/cli/commands/rules.d.ts.map +1 -0
  21. package/dist/cli/commands/rules.js +13 -0
  22. package/dist/cli/commands/rules.js.map +1 -0
  23. package/dist/cli/commands/scan.d.ts +10 -0
  24. package/dist/cli/commands/scan.d.ts.map +1 -0
  25. package/dist/cli/commands/scan.js +65 -0
  26. package/dist/cli/commands/scan.js.map +1 -0
  27. package/dist/cli/commands/waive.d.ts +8 -0
  28. package/dist/cli/commands/waive.d.ts.map +1 -0
  29. package/dist/cli/commands/waive.js +34 -0
  30. package/dist/cli/commands/waive.js.map +1 -0
  31. package/dist/cli/index.d.ts +2 -0
  32. package/dist/cli/index.d.ts.map +1 -0
  33. package/dist/cli/index.js +64 -0
  34. package/dist/cli/index.js.map +1 -0
  35. package/dist/engine/baseline.d.ts +11 -0
  36. package/dist/engine/baseline.d.ts.map +1 -0
  37. package/dist/engine/baseline.js +39 -0
  38. package/dist/engine/baseline.js.map +1 -0
  39. package/dist/engine/baseline.test.d.ts +2 -0
  40. package/dist/engine/baseline.test.d.ts.map +1 -0
  41. package/dist/engine/baseline.test.js +135 -0
  42. package/dist/engine/baseline.test.js.map +1 -0
  43. package/dist/engine/config.d.ts +8 -0
  44. package/dist/engine/config.d.ts.map +1 -0
  45. package/dist/engine/config.js +134 -0
  46. package/dist/engine/config.js.map +1 -0
  47. package/dist/engine/config.test.d.ts +2 -0
  48. package/dist/engine/config.test.d.ts.map +1 -0
  49. package/dist/engine/config.test.js +107 -0
  50. package/dist/engine/config.test.js.map +1 -0
  51. package/dist/engine/extensions/load.d.ts +11 -0
  52. package/dist/engine/extensions/load.d.ts.map +1 -0
  53. package/dist/engine/extensions/load.js +26 -0
  54. package/dist/engine/extensions/load.js.map +1 -0
  55. package/dist/engine/extensions/registry.d.ts +5 -0
  56. package/dist/engine/extensions/registry.d.ts.map +1 -0
  57. package/dist/engine/extensions/registry.js +11 -0
  58. package/dist/engine/extensions/registry.js.map +1 -0
  59. package/dist/engine/extensions/types.d.ts +51 -0
  60. package/dist/engine/extensions/types.d.ts.map +1 -0
  61. package/dist/engine/extensions/types.js +2 -0
  62. package/dist/engine/extensions/types.js.map +1 -0
  63. package/dist/engine/license.d.ts +40 -0
  64. package/dist/engine/license.d.ts.map +1 -0
  65. package/dist/engine/license.js +104 -0
  66. package/dist/engine/license.js.map +1 -0
  67. package/dist/engine/report.d.ts +5 -0
  68. package/dist/engine/report.d.ts.map +1 -0
  69. package/dist/engine/report.js +115 -0
  70. package/dist/engine/report.js.map +1 -0
  71. package/dist/engine/run.d.ts +11 -0
  72. package/dist/engine/run.d.ts.map +1 -0
  73. package/dist/engine/run.js +105 -0
  74. package/dist/engine/run.js.map +1 -0
  75. package/dist/engine/sarif.d.ts +3 -0
  76. package/dist/engine/sarif.d.ts.map +1 -0
  77. package/dist/engine/sarif.js +58 -0
  78. package/dist/engine/sarif.js.map +1 -0
  79. package/dist/engine/sarif.test.d.ts +2 -0
  80. package/dist/engine/sarif.test.d.ts.map +1 -0
  81. package/dist/engine/sarif.test.js +152 -0
  82. package/dist/engine/sarif.test.js.map +1 -0
  83. package/dist/engine/score.d.ts +13 -0
  84. package/dist/engine/score.d.ts.map +1 -0
  85. package/dist/engine/score.js +116 -0
  86. package/dist/engine/score.js.map +1 -0
  87. package/dist/engine/score.test.d.ts +2 -0
  88. package/dist/engine/score.test.d.ts.map +1 -0
  89. package/dist/engine/score.test.js +227 -0
  90. package/dist/engine/score.test.js.map +1 -0
  91. package/dist/engine/types.d.ts +123 -0
  92. package/dist/engine/types.d.ts.map +1 -0
  93. package/dist/engine/types.js +2 -0
  94. package/dist/engine/types.js.map +1 -0
  95. package/dist/engine/version.d.ts +5 -0
  96. package/dist/engine/version.d.ts.map +1 -0
  97. package/dist/engine/version.js +15 -0
  98. package/dist/engine/version.js.map +1 -0
  99. package/dist/engine/waivers.d.ts +9 -0
  100. package/dist/engine/waivers.d.ts.map +1 -0
  101. package/dist/engine/waivers.js +55 -0
  102. package/dist/engine/waivers.js.map +1 -0
  103. package/dist/engine/waivers.test.d.ts +2 -0
  104. package/dist/engine/waivers.test.d.ts.map +1 -0
  105. package/dist/engine/waivers.test.js +147 -0
  106. package/dist/engine/waivers.test.js.map +1 -0
  107. package/dist/index.d.ts +14 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/index.js +12 -0
  110. package/dist/index.js.map +1 -0
  111. package/dist/next/deps.d.ts +4 -0
  112. package/dist/next/deps.d.ts.map +1 -0
  113. package/dist/next/deps.js +118 -0
  114. package/dist/next/deps.js.map +1 -0
  115. package/dist/next/deps.test.d.ts +2 -0
  116. package/dist/next/deps.test.d.ts.map +1 -0
  117. package/dist/next/deps.test.js +249 -0
  118. package/dist/next/deps.test.js.map +1 -0
  119. package/dist/next/detect.d.ts +10 -0
  120. package/dist/next/detect.d.ts.map +1 -0
  121. package/dist/next/detect.js +57 -0
  122. package/dist/next/detect.js.map +1 -0
  123. package/dist/next/detect.test.d.ts +2 -0
  124. package/dist/next/detect.test.d.ts.map +1 -0
  125. package/dist/next/detect.test.js +74 -0
  126. package/dist/next/detect.test.js.map +1 -0
  127. package/dist/next/index.d.ts +5 -0
  128. package/dist/next/index.d.ts.map +1 -0
  129. package/dist/next/index.js +59 -0
  130. package/dist/next/index.js.map +1 -0
  131. package/dist/next/middleware.d.ts +3 -0
  132. package/dist/next/middleware.d.ts.map +1 -0
  133. package/dist/next/middleware.js +48 -0
  134. package/dist/next/middleware.js.map +1 -0
  135. package/dist/next/middleware.test.d.ts +2 -0
  136. package/dist/next/middleware.test.d.ts.map +1 -0
  137. package/dist/next/middleware.test.js +203 -0
  138. package/dist/next/middleware.test.js.map +1 -0
  139. package/dist/next/routes.d.ts +10 -0
  140. package/dist/next/routes.d.ts.map +1 -0
  141. package/dist/next/routes.js +172 -0
  142. package/dist/next/routes.js.map +1 -0
  143. package/dist/next/routes.test.d.ts +2 -0
  144. package/dist/next/routes.test.d.ts.map +1 -0
  145. package/dist/next/routes.test.js +175 -0
  146. package/dist/next/routes.test.js.map +1 -0
  147. package/dist/next/server-actions.d.ts +4 -0
  148. package/dist/next/server-actions.d.ts.map +1 -0
  149. package/dist/next/server-actions.js +107 -0
  150. package/dist/next/server-actions.js.map +1 -0
  151. package/dist/next/server-actions.test.d.ts +2 -0
  152. package/dist/next/server-actions.test.d.ts.map +1 -0
  153. package/dist/next/server-actions.test.js +138 -0
  154. package/dist/next/server-actions.test.js.map +1 -0
  155. package/dist/next/trpc.d.ts +3 -0
  156. package/dist/next/trpc.d.ts.map +1 -0
  157. package/dist/next/trpc.js +312 -0
  158. package/dist/next/trpc.js.map +1 -0
  159. package/dist/next/types.d.ts +144 -0
  160. package/dist/next/types.d.ts.map +1 -0
  161. package/dist/next/types.js +2 -0
  162. package/dist/next/types.js.map +1 -0
  163. package/dist/next/wrappers.d.ts +10 -0
  164. package/dist/next/wrappers.d.ts.map +1 -0
  165. package/dist/next/wrappers.js +536 -0
  166. package/dist/next/wrappers.js.map +1 -0
  167. package/dist/next/wrappers.test.d.ts +2 -0
  168. package/dist/next/wrappers.test.d.ts.map +1 -0
  169. package/dist/next/wrappers.test.js +361 -0
  170. package/dist/next/wrappers.test.js.map +1 -0
  171. package/dist/rules/auth-boundary-missing.d.ts +5 -0
  172. package/dist/rules/auth-boundary-missing.d.ts.map +1 -0
  173. package/dist/rules/auth-boundary-missing.js +463 -0
  174. package/dist/rules/auth-boundary-missing.js.map +1 -0
  175. package/dist/rules/auth-boundary-missing.test.d.ts +2 -0
  176. package/dist/rules/auth-boundary-missing.test.d.ts.map +1 -0
  177. package/dist/rules/auth-boundary-missing.test.js +492 -0
  178. package/dist/rules/auth-boundary-missing.test.js.map +1 -0
  179. package/dist/rules/index.d.ts +12 -0
  180. package/dist/rules/index.d.ts.map +1 -0
  181. package/dist/rules/index.js +95 -0
  182. package/dist/rules/index.js.map +1 -0
  183. package/dist/rules/input-validation-missing.d.ts +5 -0
  184. package/dist/rules/input-validation-missing.d.ts.map +1 -0
  185. package/dist/rules/input-validation-missing.js +272 -0
  186. package/dist/rules/input-validation-missing.js.map +1 -0
  187. package/dist/rules/input-validation-missing.test.d.ts +2 -0
  188. package/dist/rules/input-validation-missing.test.d.ts.map +1 -0
  189. package/dist/rules/input-validation-missing.test.js +449 -0
  190. package/dist/rules/input-validation-missing.test.js.map +1 -0
  191. package/dist/rules/rate-limit-missing.d.ts +5 -0
  192. package/dist/rules/rate-limit-missing.d.ts.map +1 -0
  193. package/dist/rules/rate-limit-missing.js +316 -0
  194. package/dist/rules/rate-limit-missing.js.map +1 -0
  195. package/dist/rules/rate-limit-missing.test.d.ts +2 -0
  196. package/dist/rules/rate-limit-missing.test.d.ts.map +1 -0
  197. package/dist/rules/rate-limit-missing.test.js +381 -0
  198. package/dist/rules/rate-limit-missing.test.js.map +1 -0
  199. package/dist/rules/tenancy-scope-missing.d.ts +5 -0
  200. package/dist/rules/tenancy-scope-missing.d.ts.map +1 -0
  201. package/dist/rules/tenancy-scope-missing.js +149 -0
  202. package/dist/rules/tenancy-scope-missing.js.map +1 -0
  203. package/dist/rules/wrapper-unrecognized.d.ts +5 -0
  204. package/dist/rules/wrapper-unrecognized.d.ts.map +1 -0
  205. package/dist/rules/wrapper-unrecognized.js +81 -0
  206. package/dist/rules/wrapper-unrecognized.js.map +1 -0
  207. package/dist/util/hof.d.ts +22 -0
  208. package/dist/util/hof.d.ts.map +1 -0
  209. package/dist/util/hof.js +99 -0
  210. package/dist/util/hof.js.map +1 -0
  211. package/dist/util/hof.test.d.ts +2 -0
  212. package/dist/util/hof.test.d.ts.map +1 -0
  213. package/dist/util/hof.test.js +79 -0
  214. package/dist/util/hof.test.js.map +1 -0
  215. package/dist/util/monorepo.d.ts +6 -0
  216. package/dist/util/monorepo.d.ts.map +1 -0
  217. package/dist/util/monorepo.js +29 -0
  218. package/dist/util/monorepo.js.map +1 -0
  219. package/dist/util/outbound-fetch.d.ts +14 -0
  220. package/dist/util/outbound-fetch.d.ts.map +1 -0
  221. package/dist/util/outbound-fetch.js +59 -0
  222. package/dist/util/outbound-fetch.js.map +1 -0
  223. package/dist/util/outbound-fetch.test.d.ts +2 -0
  224. package/dist/util/outbound-fetch.test.d.ts.map +1 -0
  225. package/dist/util/outbound-fetch.test.js +83 -0
  226. package/dist/util/outbound-fetch.test.js.map +1 -0
  227. package/dist/util/paths.d.ts +6 -0
  228. package/dist/util/paths.d.ts.map +1 -0
  229. package/dist/util/paths.js +18 -0
  230. package/dist/util/paths.js.map +1 -0
  231. package/dist/util/resolve.d.ts +30 -0
  232. package/dist/util/resolve.d.ts.map +1 -0
  233. package/dist/util/resolve.js +306 -0
  234. package/dist/util/resolve.js.map +1 -0
  235. package/dist/util/resolve.test.d.ts +2 -0
  236. package/dist/util/resolve.test.d.ts.map +1 -0
  237. package/dist/util/resolve.test.js +186 -0
  238. package/dist/util/resolve.test.js.map +1 -0
  239. package/package.json +56 -0
@@ -0,0 +1,272 @@
1
+ import { readFileSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { detectOutboundFetcher } from "../util/outbound-fetch.js";
4
+ export const RULE_ID = "INPUT-VALIDATION-MISSING";
5
+ const SEVERITY_RANK = { critical: 4, high: 3, med: 2, low: 1 };
6
+ const SEVERITY_UP = { low: "med", med: "high", high: "high", critical: "critical" };
7
+ function bumpSeverityIfPublicIntent(severity, isPublicIntent) {
8
+ if (!isPublicIntent)
9
+ return severity;
10
+ return SEVERITY_UP[severity] ?? severity;
11
+ }
12
+ function severityFromConfidence(confidence, maxSeverity) {
13
+ const max = maxSeverity;
14
+ const maxRank = SEVERITY_RANK[max] ?? 3;
15
+ // high confidence → use max severity (typically high)
16
+ // med confidence → cap at med
17
+ const computed = confidence === "high" ? max : "med";
18
+ const computedRank = SEVERITY_RANK[computed] ?? 2;
19
+ return computedRank > maxRank ? max : computed;
20
+ }
21
+ export function run(index, config) {
22
+ const findings = [];
23
+ const maxSeverity = config.rules[RULE_ID]?.severity ?? "high";
24
+ // Check mutation route handlers
25
+ for (const route of index.routes.mutationRoutes) {
26
+ const result = checkEndpoint(route, index);
27
+ if (result) {
28
+ let { confidence, confidenceRationale: rationale, evidence } = result;
29
+ let tags = ["input-validation", "data-integrity"];
30
+ // public-intent endpoints: bump severity (public + unvalidated = worse)
31
+ if (route.publicIntent) {
32
+ if (confidence === "med")
33
+ confidence = "high";
34
+ rationale += " — endpoint declared intentionally public (higher exposure)";
35
+ evidence.push(`public-intent: "${route.publicIntent.reason}"`);
36
+ tags = ["input-validation", "data-integrity", "public-intent"];
37
+ // Combined SSRF note when outbound fetch detected
38
+ let src;
39
+ try {
40
+ src = readFileSync(path.resolve(index.rootDir, route.file), "utf-8");
41
+ }
42
+ catch { }
43
+ if (src) {
44
+ const fetcher = detectOutboundFetcher(src);
45
+ if (fetcher.isRisky) {
46
+ evidence.push("Public endpoint performs outbound fetch; missing validation increases SSRF risk");
47
+ tags.push("ssrf-surface");
48
+ }
49
+ }
50
+ }
51
+ findings.push({
52
+ ruleId: RULE_ID,
53
+ severity: bumpSeverityIfPublicIntent(severityFromConfidence(confidence, maxSeverity), !!route.publicIntent),
54
+ confidence,
55
+ message: "Endpoint reads user input and performs writes without schema validation",
56
+ file: route.file,
57
+ line: result.line,
58
+ snippet: result.snippet,
59
+ evidence,
60
+ confidenceRationale: rationale,
61
+ remediation: [
62
+ "Validate request body with a schema library before passing to DB/API calls",
63
+ "Example: `const data = schema.parse(await request.json())`",
64
+ "Recommended: zod, valibot, yup, or joi",
65
+ ],
66
+ tags,
67
+ });
68
+ }
69
+ }
70
+ // Check mutation server actions
71
+ for (const action of index.serverActions.mutationActions) {
72
+ const result = checkEndpoint(action, index);
73
+ if (result) {
74
+ findings.push({
75
+ ruleId: RULE_ID,
76
+ severity: severityFromConfidence(result.confidence, maxSeverity),
77
+ confidence: result.confidence,
78
+ message: "Server action performs writes without schema validation on input",
79
+ file: action.file,
80
+ line: result.line,
81
+ snippet: result.snippet,
82
+ evidence: result.evidence,
83
+ confidenceRationale: result.confidenceRationale,
84
+ remediation: [
85
+ "Validate action input with a schema library before passing to DB/API calls",
86
+ "Example: `const data = schema.parse(formData)`",
87
+ "Recommended: zod, valibot, yup, or joi",
88
+ ],
89
+ tags: ["input-validation", "data-integrity"],
90
+ });
91
+ }
92
+ }
93
+ return findings;
94
+ }
95
+ function checkEndpoint(endpoint, index) {
96
+ let src;
97
+ try {
98
+ src = readFileSync(path.resolve(index.rootDir, endpoint.file), "utf-8");
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ // Must read user input
104
+ if (!readsUserInput(src))
105
+ return null;
106
+ // Must have a write (DB or Stripe)
107
+ if (!endpoint.signals.hasDbWriteEvidence && !endpoint.signals.hasStripeWriteEvidence)
108
+ return null;
109
+ // Check for validation patterns — if present, no finding
110
+ // Strip comment lines to avoid false negatives from commented-out validation
111
+ if (hasSchemaValidation(stripCommentLines(src)))
112
+ return null;
113
+ // Build evidence
114
+ const evidence = [];
115
+ if (readsJson(src))
116
+ evidence.push("Reads request.json() / req.json()");
117
+ if (readsFormData(src))
118
+ evidence.push("Reads request.formData()");
119
+ if (/req\.body/.test(src))
120
+ evidence.push("Reads req.body");
121
+ for (const detail of endpoint.signals.mutationDetails) {
122
+ if (detail !== "reads request body") {
123
+ evidence.push(detail);
124
+ }
125
+ }
126
+ evidence.push("No schema validation detected (z.parse, safeParse, validate, etc.)");
127
+ // Confidence: high if clear DB write + body read + no validation
128
+ // med if only general mutation evidence
129
+ let confidence = endpoint.signals.hasDbWriteEvidence ? "high" : "med";
130
+ let rationale = confidence === "high"
131
+ ? "Direct DB write with unvalidated user input — no schema parsing detected"
132
+ : "Mutation endpoint with unvalidated input — no schema parsing detected";
133
+ // Webhook-verified routes: signature verification provides some payload integrity
134
+ // Downgrade — still flag because signatures don't validate schema structure
135
+ if (hasWebhookSignature(src)) {
136
+ confidence = "med";
137
+ rationale = "Webhook signature verified but no schema validation — payload structure not enforced";
138
+ evidence.push("webhook signature verification present (provides integrity, not schema validation)");
139
+ }
140
+ // Find the line of the first body read
141
+ const line = findInputReadLine(src);
142
+ return { confidence, line, evidence, confidenceRationale: rationale };
143
+ }
144
+ // --- Detection patterns ---
145
+ function readsUserInput(src) {
146
+ return readsJson(src) || readsFormData(src) || /req\.body\b/.test(src);
147
+ }
148
+ function readsJson(src) {
149
+ return /(?:request|req)\.json\s*\(/.test(src);
150
+ }
151
+ function readsFormData(src) {
152
+ return /(?:request|req)\.formData\s*\(/.test(src);
153
+ }
154
+ /**
155
+ * Detect schema validation patterns.
156
+ * Starts with Zod (.parse, .safeParse, z.object) and expands to common libs.
157
+ */
158
+ function hasSchemaValidation(src) {
159
+ // Zod: z.object(), schema.parse(), schema.safeParse()
160
+ if (/\bz\.\s*(?:object|string|number|array|enum|union|tuple|record|literal|nativeEnum|coerce)\s*\(/.test(src))
161
+ return true;
162
+ // .parse() but NOT JSON.parse, URL.parse, path.parse, Date.parse, parseInt
163
+ if (/\.parse\s*\(/.test(src) && !isOnlyBuiltinParse(src))
164
+ return true;
165
+ if (/\.safeParse\s*\(/.test(src))
166
+ return true;
167
+ // Valibot: v.parse(), v.safeParse(), parse(schema, ...)
168
+ if (/\bv\.\s*(?:parse|safeParse)\s*\(/.test(src))
169
+ return true;
170
+ // Yup: schema.validate(), schema.validateSync()
171
+ if (/\.validate\s*\(/.test(src) && !isOnlyBuiltinValidate(src))
172
+ return true;
173
+ if (/\.validateSync\s*\(/.test(src))
174
+ return true;
175
+ // Joi: schema.validate()
176
+ // (already covered by .validate above)
177
+ // ArkType: type(...), already uses .parse
178
+ // (covered by .parse above)
179
+ // TypeBox + Ajv: Value.Check, ajv.validate — both use .validate
180
+ // (covered above)
181
+ // Next.js server action pattern: zod + useFormState
182
+ // createSafeActionClient (next-safe-action)
183
+ if (/createSafeActionClient|actionClient/.test(src))
184
+ return true;
185
+ // tRPC input validation (z.object in .input())
186
+ if (/\.input\s*\(\s*z\./.test(src))
187
+ return true;
188
+ return false;
189
+ }
190
+ /**
191
+ * Returns true if ALL .parse() calls in the source are from built-in objects
192
+ * (JSON.parse, URL.parse, path.parse, Date.parse, etc.) — not schema validation.
193
+ */
194
+ function isOnlyBuiltinParse(src) {
195
+ const allParseMatches = [...src.matchAll(/(\w+)\.parse\s*\(/g)];
196
+ // No named callers found but .parse() exists → likely chained (e.g. getSchema().parse())
197
+ // Treat as schema validation (safe default)
198
+ if (allParseMatches.length === 0)
199
+ return false;
200
+ return allParseMatches.every((m) => BUILTIN_PARSE_CALLERS.has(m[1]));
201
+ }
202
+ const BUILTIN_PARSE_CALLERS = new Set([
203
+ "JSON", "URL", "path", "Date", "Number", "BigInt",
204
+ "Buffer", "querystring", "qs", "cookie", "cookieStore",
205
+ ]);
206
+ /**
207
+ * Returns true if ALL .validate() calls are from built-in/non-schema objects.
208
+ */
209
+ function isOnlyBuiltinValidate(src) {
210
+ const allMatches = [...src.matchAll(/(\w+)\.validate\s*\(/g)];
211
+ if (allMatches.length === 0)
212
+ return true;
213
+ return allMatches.every((m) => BUILTIN_VALIDATE_CALLERS.has(m[1]));
214
+ }
215
+ const BUILTIN_VALIDATE_CALLERS = new Set([
216
+ "document", "form", "email", "url",
217
+ ]);
218
+ /**
219
+ * Remove full-line comments to avoid false negatives.
220
+ * Only strips lines where first non-whitespace is // or lines inside block comments.
221
+ * Deliberately simple — doesn't handle inline comments to avoid breaking strings.
222
+ */
223
+ function stripCommentLines(src) {
224
+ let inBlock = false;
225
+ return src.split("\n").filter((line) => {
226
+ const trimmed = line.trimStart();
227
+ if (inBlock) {
228
+ if (trimmed.includes("*/"))
229
+ inBlock = false;
230
+ return false;
231
+ }
232
+ if (trimmed.startsWith("/*")) {
233
+ if (!trimmed.includes("*/"))
234
+ inBlock = true;
235
+ return false;
236
+ }
237
+ if (trimmed.startsWith("//"))
238
+ return false;
239
+ return true;
240
+ }).join("\n");
241
+ }
242
+ /**
243
+ * Detect webhook signature verification patterns.
244
+ * Presence indicates payload integrity is verified (but not schema structure).
245
+ */
246
+ function hasWebhookSignature(src) {
247
+ if (/constructEvent\s*\(/.test(src))
248
+ return true;
249
+ if (/createHmac\s*\(/.test(src) && /signature/i.test(src))
250
+ return true;
251
+ if (/timingSafeEqual\s*\(/.test(src))
252
+ return true;
253
+ if (/verifySignature\s*\(/.test(src))
254
+ return true;
255
+ if (/verifyWebhook\s*\(/i.test(src))
256
+ return true;
257
+ if (/\.verify\s*\(/.test(src) && /webhook/i.test(src))
258
+ return true;
259
+ return false;
260
+ }
261
+ function findInputReadLine(src) {
262
+ const lines = src.split("\n");
263
+ for (let i = 0; i < lines.length; i++) {
264
+ if (/(?:request|req)\.json\s*\(/.test(lines[i]) ||
265
+ /(?:request|req)\.formData\s*\(/.test(lines[i]) ||
266
+ /req\.body\b/.test(lines[i])) {
267
+ return i + 1;
268
+ }
269
+ }
270
+ return undefined;
271
+ }
272
+ //# sourceMappingURL=input-validation-missing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-validation-missing.js","sourceRoot":"","sources":["../../src/rules/input-validation-missing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=input-validation-missing.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-validation-missing.test.d.ts","sourceRoot":"","sources":["../../src/rules/input-validation-missing.test.ts"],"names":[],"mappings":""}