hullproof 0.0.0-stage → 0.5.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 (363) hide show
  1. package/LICENSE +222 -0
  2. package/NOTICE +32 -0
  3. package/README.md +313 -2
  4. package/checks/db.firebase-rules-not-open.json +24 -0
  5. package/checks/db.rls-every-table.json +25 -0
  6. package/checks/log.no-credential-in-logs.json +30 -0
  7. package/checks/secrets.no-env-file-committed.json +31 -0
  8. package/checks/secrets.no-secret-in-public-env.json +37 -0
  9. package/checks/storage.firebase-rules-not-open.json +22 -0
  10. package/checks/supply.no-untrusted-event-in-run.json +23 -0
  11. package/checks/web.cors-no-reflected-origin.json +31 -0
  12. package/detectors/ai.json +25 -0
  13. package/detectors/auth.json +27 -0
  14. package/detectors/ci.json +19 -0
  15. package/detectors/containers.json +8 -0
  16. package/detectors/data_stores.json +38 -0
  17. package/detectors/frameworks.json +94 -0
  18. package/detectors/hosting.json +44 -0
  19. package/detectors/iac.json +43 -0
  20. package/detectors/languages.json +48 -0
  21. package/detectors/mobile.json +22 -0
  22. package/detectors/package_managers.json +39 -0
  23. package/detectors/payments.json +19 -0
  24. package/detectors/runtimes.json +17 -0
  25. package/dist/checks/code/admin-role.js +46 -0
  26. package/dist/checks/code/owner-filter.js +91 -0
  27. package/dist/checks/code/shared.js +84 -0
  28. package/dist/checks/code/webhook-verify.js +77 -0
  29. package/dist/checks/context.js +64 -0
  30. package/dist/checks/map/config-checks.js +74 -0
  31. package/dist/checks/map/entry-checks.js +97 -0
  32. package/dist/checks/map/shared.js +44 -0
  33. package/dist/checks/ops.js +307 -0
  34. package/dist/checks/pro-map.js +1 -0
  35. package/dist/checks/run.js +92 -0
  36. package/dist/checks/types.js +1 -0
  37. package/dist/ci/comment.js +44 -0
  38. package/dist/ci/sarif-check.js +157 -0
  39. package/dist/ci/sarif.js +61 -0
  40. package/dist/cli.js +358 -0
  41. package/dist/commands/ci.js +115 -0
  42. package/dist/commands/git.js +58 -0
  43. package/dist/commands/hooks.js +30 -0
  44. package/dist/commands/hunt-pro.js +1 -0
  45. package/dist/commands/hunt.js +86 -0
  46. package/dist/commands/map.js +33 -0
  47. package/dist/commands/mcp.js +17 -0
  48. package/dist/commands/pack.js +48 -0
  49. package/dist/commands/prove-pro.js +1 -0
  50. package/dist/commands/prove.js +173 -0
  51. package/dist/commands/report-gate.js +92 -0
  52. package/dist/commands/scan.js +158 -0
  53. package/dist/confirm/agreement.js +18 -0
  54. package/dist/confirm/chain.js +84 -0
  55. package/dist/confirm/confirm.js +112 -0
  56. package/dist/confirm/honesty.js +75 -0
  57. package/dist/confirm/lead.js +92 -0
  58. package/dist/confirm/merge.js +88 -0
  59. package/dist/confirm/query.js +145 -0
  60. package/dist/confirm/rule.js +86 -0
  61. package/dist/confirm/store.js +42 -0
  62. package/dist/confirm/summary.js +6 -0
  63. package/dist/confirm/synthesis.js +28 -0
  64. package/dist/discover/detectors.js +50 -0
  65. package/dist/discover/discover.js +270 -0
  66. package/dist/discover/extract.js +282 -0
  67. package/dist/discover/git.js +40 -0
  68. package/dist/discover/kit-files.js +47 -0
  69. package/dist/discover/walk.js +110 -0
  70. package/dist/env.js +57 -0
  71. package/dist/gate/gate.js +173 -0
  72. package/dist/hooks/install.js +153 -0
  73. package/dist/hooks/run.js +112 -0
  74. package/dist/map/analyze.js +201 -0
  75. package/dist/map/build.js +253 -0
  76. package/dist/map/candidates.js +49 -0
  77. package/dist/map/cblocks.js +77 -0
  78. package/dist/map/config.js +291 -0
  79. package/dist/map/dotnet.js +242 -0
  80. package/dist/map/entry.js +78 -0
  81. package/dist/map/expo.js +190 -0
  82. package/dist/map/go.js +292 -0
  83. package/dist/map/js-express.js +143 -0
  84. package/dist/map/js-misc.js +295 -0
  85. package/dist/map/js-next.js +292 -0
  86. package/dist/map/laravel.js +389 -0
  87. package/dist/map/lex-more.js +305 -0
  88. package/dist/map/lex.js +425 -0
  89. package/dist/map/light-index.js +191 -0
  90. package/dist/map/light-profiles.js +324 -0
  91. package/dist/map/light.js +574 -0
  92. package/dist/map/local-vocab.js +27 -0
  93. package/dist/map/mobile.js +326 -0
  94. package/dist/map/py-django.js +281 -0
  95. package/dist/map/py.js +391 -0
  96. package/dist/map/rails.js +391 -0
  97. package/dist/map/repo.js +178 -0
  98. package/dist/map/rust.js +312 -0
  99. package/dist/map/spring.js +207 -0
  100. package/dist/map/taint.js +96 -0
  101. package/dist/map/touches.js +355 -0
  102. package/dist/map/vocab.js +88 -0
  103. package/dist/mapping/controls.js +34 -0
  104. package/dist/mapping/dependency-policy.js +44 -0
  105. package/dist/mapping/map.js +44 -0
  106. package/dist/mapping/maps/gitleaks.json +12 -0
  107. package/dist/mapping/maps/osv-scanner.json +7 -0
  108. package/dist/mapping/maps/trivy.json +13 -0
  109. package/dist/mcp/confine.js +59 -0
  110. package/dist/mcp/protocol.js +140 -0
  111. package/dist/mcp/schema.js +43 -0
  112. package/dist/mcp/server.js +37 -0
  113. package/dist/mcp/tools.js +238 -0
  114. package/dist/packs/coverage.js +79 -0
  115. package/dist/packs/manifest.js +36 -0
  116. package/dist/packs/validate.js +271 -0
  117. package/dist/prove/container.js +276 -0
  118. package/dist/prove/exec-main.js +20 -0
  119. package/dist/prove/executor.js +149 -0
  120. package/dist/prove/local.js +131 -0
  121. package/dist/prove/probes.js +65 -0
  122. package/dist/prove/recipe.js +278 -0
  123. package/dist/prove/run.js +136 -0
  124. package/dist/prove/runner.js +1 -0
  125. package/dist/prove/safety.js +126 -0
  126. package/dist/prove/spec.js +357 -0
  127. package/dist/prove/store.js +65 -0
  128. package/dist/report/build.js +297 -0
  129. package/dist/report/entry-points.js +75 -0
  130. package/dist/report/hunt-section.js +71 -0
  131. package/dist/report/kit.js +41 -0
  132. package/dist/report/load.js +35 -0
  133. package/dist/report/messages.js +273 -0
  134. package/dist/report/prove-section.js +65 -0
  135. package/dist/report/report.js +156 -0
  136. package/dist/report/surface.js +21 -0
  137. package/dist/report/test-code.js +51 -0
  138. package/dist/report/tiers.js +44 -0
  139. package/dist/scanners/common.js +162 -0
  140. package/dist/scanners/gitleaks.js +90 -0
  141. package/dist/scanners/index.js +113 -0
  142. package/dist/scanners/osv-scanner.js +77 -0
  143. package/dist/scanners/run-targets.js +34 -0
  144. package/dist/scanners/secret-noise.js +89 -0
  145. package/dist/scanners/semgrep.js +142 -0
  146. package/dist/scanners/trivy.js +65 -0
  147. package/dist/scanners/types.js +1 -0
  148. package/dist/types/check.js +1 -0
  149. package/dist/types/codemap.js +1 -0
  150. package/dist/types/findings.js +5 -0
  151. package/dist/types/hunt.js +1 -0
  152. package/dist/types/index.js +1 -0
  153. package/dist/types/prove.js +1 -0
  154. package/dist/types/results.js +2 -0
  155. package/dist/types/scan.js +1 -0
  156. package/dist/types/stack.js +5 -0
  157. package/dist/util.js +71 -0
  158. package/dist/version.js +2 -0
  159. package/package.json +46 -4
  160. package/packs/android/fixture-result.json +43 -0
  161. package/packs/android/pack.json +56 -0
  162. package/packs/dotnet/fixture-result.json +62 -0
  163. package/packs/dotnet/pack.json +88 -0
  164. package/packs/expo/answer-key.json +51 -0
  165. package/packs/expo/fixture-result.json +62 -0
  166. package/packs/expo/pack.json +79 -0
  167. package/packs/firebase/answer-key.json +27 -0
  168. package/packs/firebase/fixture-result.json +43 -0
  169. package/packs/firebase/pack.json +58 -0
  170. package/packs/go/answer-key.json +101 -0
  171. package/packs/go/fixture-result.json +64 -0
  172. package/packs/go/pack.json +99 -0
  173. package/packs/ios/fixture-result.json +43 -0
  174. package/packs/ios/pack.json +55 -0
  175. package/packs/laravel/answer-key.json +84 -0
  176. package/packs/laravel/fixture-result.json +60 -0
  177. package/packs/laravel/pack.json +91 -0
  178. package/packs/node-ts/answer-key.json +220 -0
  179. package/packs/node-ts/fixture-result.json +114 -0
  180. package/packs/node-ts/pack.json +134 -0
  181. package/packs/python/answer-key.json +195 -0
  182. package/packs/python/fixture-result.json +119 -0
  183. package/packs/python/pack.json +121 -0
  184. package/packs/rails/answer-key.json +84 -0
  185. package/packs/rails/fixture-result.json +60 -0
  186. package/packs/rails/pack.json +78 -0
  187. package/packs/rust/fixture-result.json +43 -0
  188. package/packs/rust/pack.json +69 -0
  189. package/packs/spring/fixture-result.json +63 -0
  190. package/packs/spring/pack.json +103 -0
  191. package/packs/supabase/answer-key.json +52 -0
  192. package/packs/supabase/fixture-result.json +58 -0
  193. package/packs/supabase/pack.json +60 -0
  194. package/rules/gitleaks.toml +256 -0
  195. package/rules/semgrep/api-route-no-auth.yaml +31 -0
  196. package/rules/semgrep/cors-reflect-origin.yaml +48 -0
  197. package/rules/semgrep/eval-or-new-function.yaml +185 -0
  198. package/rules/semgrep/log-session-token.yaml +48 -0
  199. package/rules/semgrep/math-random-tokens.yaml +24 -0
  200. package/rules/semgrep/next-public-secret-name.yaml +32 -0
  201. package/rules/semgrep/postgrest-filter-injection.yaml +78 -0
  202. package/rules/semgrep/raw-sql-concat.yaml +42 -0
  203. package/rules/semgrep/read-by-id-no-owner.yaml +49 -0
  204. package/rules/semgrep/server-action-no-auth.yaml +26 -0
  205. package/rules/semgrep/sql-variants.yaml +99 -0
  206. package/rules/semgrep/ssrf-unguarded-url.yaml +137 -0
  207. package/rules/semgrep/subprocess-injection.yaml +163 -0
  208. package/rules/semgrep/taint-go.yaml +333 -0
  209. package/rules/semgrep/taint-java.yaml +317 -0
  210. package/rules/semgrep/taint-javascript.yaml +412 -0
  211. package/rules/semgrep/taint-php.yaml +361 -0
  212. package/rules/semgrep/taint-python.yaml +414 -0
  213. package/rules/semgrep/tool-endpoint-admin-client.yaml +86 -0
  214. package/rules/semgrep/user-metadata-authz.yaml +22 -0
  215. package/rules/semgrep/verify-result-unused.yaml +62 -0
  216. package/rules/semgrep/webhook-parse-before-verify.yaml +29 -0
  217. package/rules/test-artifact-ids.json +4 -0
  218. package/rules/tiers.json +427 -0
  219. package/schemas/check-results.schema.json +55 -0
  220. package/schemas/check.schema.json +41 -0
  221. package/schemas/codemap.schema.json +216 -0
  222. package/schemas/findings.schema.json +1048 -0
  223. package/schemas/hunt-output.schema.json +46 -0
  224. package/schemas/hunt-result.schema.json +58 -0
  225. package/schemas/results.schema.json +17 -0
  226. package/schemas/scan-hits.schema.json +32 -0
  227. package/schemas/scan-meta.schema.json +25 -0
  228. package/schemas/stack.schema.json +93 -0
  229. package/src/checks/README.md +32 -0
  230. package/src/checks/code/admin-role.ts +49 -0
  231. package/src/checks/code/owner-filter.ts +95 -0
  232. package/src/checks/code/shared.ts +66 -0
  233. package/src/checks/code/webhook-verify.ts +75 -0
  234. package/src/checks/context.ts +72 -0
  235. package/src/checks/map/config-checks.ts +79 -0
  236. package/src/checks/map/entry-checks.ts +116 -0
  237. package/src/checks/map/shared.ts +63 -0
  238. package/src/checks/ops.ts +280 -0
  239. package/src/checks/pro-map.ts +4 -0
  240. package/src/checks/run.ts +80 -0
  241. package/src/checks/types.ts +12 -0
  242. package/src/ci/comment.ts +52 -0
  243. package/src/ci/sarif-check.ts +100 -0
  244. package/src/ci/sarif.ts +72 -0
  245. package/src/cli.ts +273 -0
  246. package/src/commands/ci.ts +104 -0
  247. package/src/commands/git.ts +59 -0
  248. package/src/commands/hooks.ts +27 -0
  249. package/src/commands/hunt-pro.ts +4 -0
  250. package/src/commands/hunt.ts +68 -0
  251. package/src/commands/map.ts +27 -0
  252. package/src/commands/mcp.ts +18 -0
  253. package/src/commands/pack.ts +34 -0
  254. package/src/commands/prove-pro.ts +4 -0
  255. package/src/commands/prove.ts +126 -0
  256. package/src/commands/report-gate.ts +75 -0
  257. package/src/commands/scan.ts +156 -0
  258. package/src/confirm/agreement.ts +23 -0
  259. package/src/confirm/chain.ts +67 -0
  260. package/src/confirm/confirm.ts +107 -0
  261. package/src/confirm/honesty.ts +65 -0
  262. package/src/confirm/lead.ts +76 -0
  263. package/src/confirm/merge.ts +89 -0
  264. package/src/confirm/query.ts +126 -0
  265. package/src/confirm/rule.ts +76 -0
  266. package/src/confirm/store.ts +42 -0
  267. package/src/confirm/summary.ts +9 -0
  268. package/src/confirm/synthesis.ts +33 -0
  269. package/src/discover/detectors.ts +71 -0
  270. package/src/discover/discover.ts +253 -0
  271. package/src/discover/extract.ts +235 -0
  272. package/src/discover/git.ts +31 -0
  273. package/src/discover/kit-files.ts +37 -0
  274. package/src/discover/walk.ts +88 -0
  275. package/src/env.ts +51 -0
  276. package/src/gate/gate.ts +169 -0
  277. package/src/hooks/install.ts +142 -0
  278. package/src/hooks/run.ts +121 -0
  279. package/src/map/analyze.ts +219 -0
  280. package/src/map/build.ts +206 -0
  281. package/src/map/candidates.ts +47 -0
  282. package/src/map/cblocks.ts +55 -0
  283. package/src/map/config.ts +207 -0
  284. package/src/map/dotnet.ts +205 -0
  285. package/src/map/entry.ts +80 -0
  286. package/src/map/expo.ts +171 -0
  287. package/src/map/go.ts +271 -0
  288. package/src/map/js-express.ts +135 -0
  289. package/src/map/js-misc.ts +221 -0
  290. package/src/map/js-next.ts +282 -0
  291. package/src/map/laravel.ts +327 -0
  292. package/src/map/lex-more.ts +152 -0
  293. package/src/map/lex.ts +274 -0
  294. package/src/map/light-index.ts +134 -0
  295. package/src/map/light-profiles.ts +363 -0
  296. package/src/map/light.ts +484 -0
  297. package/src/map/local-vocab.ts +30 -0
  298. package/src/map/mobile.ts +263 -0
  299. package/src/map/py-django.ts +239 -0
  300. package/src/map/py.ts +329 -0
  301. package/src/map/rails.ts +291 -0
  302. package/src/map/repo.ts +150 -0
  303. package/src/map/rust.ts +256 -0
  304. package/src/map/spring.ts +185 -0
  305. package/src/map/taint.ts +86 -0
  306. package/src/map/touches.ts +296 -0
  307. package/src/map/vocab.ts +106 -0
  308. package/src/mapping/controls.ts +47 -0
  309. package/src/mapping/dependency-policy.ts +55 -0
  310. package/src/mapping/map.ts +59 -0
  311. package/src/mapping/maps/gitleaks.json +12 -0
  312. package/src/mapping/maps/osv-scanner.json +7 -0
  313. package/src/mapping/maps/trivy.json +13 -0
  314. package/src/mcp/confine.ts +44 -0
  315. package/src/mcp/protocol.ts +144 -0
  316. package/src/mcp/schema.ts +35 -0
  317. package/src/mcp/server.ts +32 -0
  318. package/src/mcp/tools.ts +235 -0
  319. package/src/packs/coverage.ts +98 -0
  320. package/src/packs/manifest.ts +97 -0
  321. package/src/packs/validate.ts +204 -0
  322. package/src/prove/container.ts +237 -0
  323. package/src/prove/exec-main.ts +16 -0
  324. package/src/prove/executor.ts +130 -0
  325. package/src/prove/local.ts +100 -0
  326. package/src/prove/probes.ts +68 -0
  327. package/src/prove/recipe.ts +215 -0
  328. package/src/prove/run.ts +124 -0
  329. package/src/prove/runner.ts +28 -0
  330. package/src/prove/safety.ts +98 -0
  331. package/src/prove/spec.ts +256 -0
  332. package/src/prove/store.ts +48 -0
  333. package/src/report/build.ts +304 -0
  334. package/src/report/entry-points.ts +78 -0
  335. package/src/report/hunt-section.ts +67 -0
  336. package/src/report/kit.ts +59 -0
  337. package/src/report/load.ts +49 -0
  338. package/src/report/messages.ts +270 -0
  339. package/src/report/prove-section.ts +61 -0
  340. package/src/report/report.ts +160 -0
  341. package/src/report/surface.ts +32 -0
  342. package/src/report/test-code.ts +56 -0
  343. package/src/report/tiers.ts +50 -0
  344. package/src/scanners/common.ts +149 -0
  345. package/src/scanners/gitleaks.ts +89 -0
  346. package/src/scanners/index.ts +103 -0
  347. package/src/scanners/osv-scanner.ts +90 -0
  348. package/src/scanners/run-targets.ts +35 -0
  349. package/src/scanners/secret-noise.ts +80 -0
  350. package/src/scanners/semgrep.ts +139 -0
  351. package/src/scanners/trivy.ts +66 -0
  352. package/src/scanners/types.ts +98 -0
  353. package/src/types/check.ts +52 -0
  354. package/src/types/codemap.ts +142 -0
  355. package/src/types/findings.ts +107 -0
  356. package/src/types/hunt.ts +97 -0
  357. package/src/types/index.ts +10 -0
  358. package/src/types/prove.ts +83 -0
  359. package/src/types/results.ts +15 -0
  360. package/src/types/scan.ts +63 -0
  361. package/src/types/stack.ts +61 -0
  362. package/src/util.ts +59 -0
  363. package/src/version.ts +2 -0
@@ -0,0 +1,52 @@
1
+ // Executable checks: the definition format (checks/*.json) and the result format (checks.json).
2
+ // JSON schemas: schemas/check.schema.json, schemas/check-results.schema.json. Documented in docs/INTERFACES.md.
3
+ import type { StackCategory } from './stack.ts';
4
+ import type { ResultWord } from './results.ts';
5
+
6
+ export type CheckOp =
7
+ | 'file_exists' | 'file_exists_for_each' | 'file_absent'
8
+ | 'regex_present' | 'regex_absent' | 'json_value'
9
+ | 'sql_every_table_has' | 'workflow_uses_pinned_sha' | 'workflow_permissions' | 'version_in_table'
10
+ | 'code'; // a check written as TypeScript, see CodeCheckDefinition
11
+
12
+ // One declarative check definition (checks/*.json). Data only, no code. The interpreter in src/checks/ runs it.
13
+ export interface CheckDefinition {
14
+ id: string; // "check.<area>.<name>", for example "check.supply.lockfile-committed"
15
+ sec_ids: string[];
16
+ // Runs only when every named stack category has an item whose id is listed, or any item when the value is "*".
17
+ applies_when: Partial<Record<StackCategory, string[] | '*'>>;
18
+ op: CheckOp;
19
+ params: Record<string, unknown>; // op specific, documented per op in docs/INTERFACES.md by the checks author
20
+ on_true: ResultWord;
21
+ on_false: ResultWord;
22
+ fixtures: { pass: string; fail: string }; // folders under test/fixtures/checks/
23
+ }
24
+
25
+ // A check written as TypeScript when no declarative op can say it: op "code", no params, and module names the file in
26
+ // src/checks/code/ that exports it. The five code checks keep this metadata in their module (src/checks/types.ts CodeCheck).
27
+ export interface CodeCheckDefinition extends Omit<CheckDefinition, 'op' | 'params'> {
28
+ op: 'code';
29
+ module: string; // for example "src/checks/code/webhook-verify.ts"
30
+ }
31
+
32
+ export interface CheckEvidence { file: string; line?: number; key?: string; note: string }
33
+
34
+ // The search that supports an absence result (same idea as absence_search in the findings record).
35
+ export interface AbsenceSearch { pattern: string; files_searched: number; file_globs: string[]; matches: number }
36
+
37
+ export interface CheckResult {
38
+ check_id: string;
39
+ sec_ids: string[];
40
+ result: ResultWord;
41
+ evidence: CheckEvidence[]; // sorted by file, then line
42
+ absence_search: AbsenceSearch | null;
43
+ message_key: string; // key into the engine's plain English text, normally the check id
44
+ }
45
+
46
+ // checks.json
47
+ export interface CheckResultsFile {
48
+ schema_version: '1.0.0';
49
+ commit: string | null;
50
+ results: CheckResult[]; // sorted by check_id
51
+ not_run: { check_id: string; reason: string }[]; // sorted by check_id
52
+ }
@@ -0,0 +1,142 @@
1
+ // codemap.json, written by `hullproof map` and by `scan`. JSON schema: schemas/codemap.schema.json. Design: docs/PHASE-2-DESIGN.md.
2
+ import type { Confidence } from './stack.ts';
3
+
4
+ export type EntryKind =
5
+ | 'http_route' | 'webhook' | 'server_action' | 'page' | 'ai_tool' | 'job' | 'graphql_resolver' | 'rpc'
6
+ | 'message_consumer' | 'websocket' | 'deep_link' | 'ipc';
7
+
8
+ export type ProtectionKind = 'session_read' | 'role_check' | 'owner_filter' | 'signature_verify' | 'rate_limit' | 'input_validation' | 'audit_log';
9
+ export type Scope = 'handler' | 'wrapper' | 'router' | 'middleware' | 'global';
10
+
11
+ export interface Protection {
12
+ kind: ProtectionKind;
13
+ scope: Scope;
14
+ file: string;
15
+ line: number;
16
+ via: string; // the call, decorator or name that matched
17
+ enforced: boolean | null; // true: a failed check stops the request; false: it carries on; null: cannot tell
18
+ after_parse?: boolean; // signature_verify only: the body is parsed before the signature is checked
19
+ confidence: Confidence;
20
+ }
21
+
22
+ export type InputSource = 'body' | 'query' | 'params' | 'headers' | 'cookies' | 'form' | 'file';
23
+ export interface InputRead { source: InputSource; line: number }
24
+
25
+ export type StoreKind = 'sql' | 'prisma' | 'drizzle' | 'mongo' | 'supabase' | 'firebase' | 'redis' | 'orm' | 'filesystem' | 'object_storage';
26
+ export interface DataTouch {
27
+ store: StoreKind;
28
+ op: 'read' | 'write' | 'delete' | 'unknown';
29
+ target: string | null; // table or model name when known
30
+ line: number;
31
+ by_id: boolean; // filters on an id column with a value from the request
32
+ limit_from_request: boolean; // a limit or page size comes from the request
33
+ request_derived: boolean; // the query text or its arguments contain a request derived value
34
+ owner_scoped: boolean; // the where clause names an owner, user, tenant or account column and the value comes from the session
35
+ raw_sql: boolean;
36
+ sets: string[]; // privileged columns (role, plan, is_admin ...) a write sets from a request value, sorted
37
+ mass_assign: boolean; // a whole request object goes into a create or update
38
+ confidence: Confidence;
39
+ }
40
+
41
+ export type OutboundKind = 'http' | 'ai_model' | 'email' | 'sms' | 'payment' | 'process' | 'redirect' | 'queue';
42
+ export interface Outbound {
43
+ kind: OutboundKind;
44
+ line: number;
45
+ target: string; // the target expression, clipped to 120 characters
46
+ request_derived: boolean;
47
+ guarded: boolean; // an allowlist or host check in the same function
48
+ confidence: Confidence;
49
+ }
50
+
51
+ export interface EntryPoint {
52
+ id: string;
53
+ kind: EntryKind;
54
+ framework: string;
55
+ file: string;
56
+ line: number;
57
+ end_line: number;
58
+ method: string | null;
59
+ path: string | null;
60
+ name: string | null;
61
+ handler: { file: string; line: number } | null; // where the handler body is, when it is registered by reference
62
+ confidence: Confidence;
63
+ test_code: boolean;
64
+ tags: string[]; // sorted: admin, auth_endpoint, public, state_changing, upload, webhook
65
+ protections: Protection[];
66
+ reads: InputRead[];
67
+ data: DataTouch[];
68
+ outbound: Outbound[];
69
+ }
70
+
71
+ export interface Gate {
72
+ id: string;
73
+ kind: 'middleware' | 'guard' | 'router_dependency' | 'global_pipe' | 'mount';
74
+ framework: string;
75
+ file: string;
76
+ line: number;
77
+ paths: string[]; // path prefixes covered; ['*'] for everything
78
+ methods: string[]; // [] for all
79
+ protections: Protection[];
80
+ confidence: Confidence;
81
+ }
82
+
83
+ export type CoverageStatus = 'mapped' | 'partial' | 'not_mapped';
84
+ export interface Coverage {
85
+ language: string;
86
+ files: number;
87
+ extractor: string | null;
88
+ status: CoverageStatus;
89
+ entry_points: number;
90
+ note: string;
91
+ }
92
+
93
+ export interface SupabasePolicy { name: string; op: string; roles: string[]; using: string | null; check: string | null; line: number; permissive: boolean }
94
+ export interface SupabaseGrant { role: string; privileges: string[]; columns: string[] | null; line: number }
95
+ export interface SupabaseTable {
96
+ name: string; file: string; line: number; rls_enabled: boolean | null; columns: string[];
97
+ policies: SupabasePolicy[]; grants: SupabaseGrant[];
98
+ }
99
+ export interface FirebaseRule {
100
+ service: 'firestore' | 'storage' | 'realtime';
101
+ file: string; line: number; path: string; ops: string[]; condition: string;
102
+ uses_auth: boolean; owner_bound: boolean; always_true: boolean; owner_wildcard: boolean;
103
+ }
104
+
105
+ // ---- native mobile and Rust configuration (Phase 6f)
106
+ export interface ConfigSecret { file: string; line: number; key: string; where: 'android manifest' | 'android strings' | 'ios plist' }
107
+ export interface AndroidFilter { actions: string[]; categories: string[]; schemes: string[]; hosts: string[]; paths: string[] }
108
+ export interface AndroidComponent {
109
+ kind: 'activity' | 'service' | 'receiver' | 'provider'; name: string; line: number;
110
+ exported: boolean; exported_explicit: boolean; permission: string | null; launcher: boolean; filters: AndroidFilter[];
111
+ }
112
+ export interface AndroidManifest {
113
+ file: string; package: string | null; line: number; debuggable: boolean | null; allow_backup: boolean | null; uses_cleartext_traffic: boolean | null;
114
+ network_security_config: string | null; target_sdk: number | null; permissions: string[]; components: AndroidComponent[];
115
+ }
116
+ export interface NetworkSecurity { file: string; line: number; cleartext_base: boolean | null; cleartext_domains: string[]; trusts_user_certs: boolean; pinned_domains: number }
117
+ export interface AndroidConfig { manifests: AndroidManifest[]; network_security: NetworkSecurity[] }
118
+ export interface IosPlist { file: string; ats_arbitrary_loads: boolean | null; ats_line: number | null; url_schemes: string[]; associated_domains: string[]; usage_descriptions: string[] }
119
+ export interface IosConfig { plists: IosPlist[] }
120
+ export interface CargoDependency { name: string; source: 'registry' | 'git' | 'path'; version: string | null; line: number }
121
+ export interface CargoManifest {
122
+ file: string; line: number; name: string | null; edition: string | null; dependencies: CargoDependency[];
123
+ release_debug: boolean | null; release_overflow_checks: boolean | null; unsafe_code_lint: string | null; has_lock: boolean; binary: boolean;
124
+ }
125
+ export interface CargoConfig { manifests: CargoManifest[] }
126
+
127
+ export interface AuthHelperCandidate { name: string; entry_points: number; file: string; line: number }
128
+ export interface LocalVocabRecord { auth_helpers: string[]; role_helpers: string[] }
129
+
130
+ export interface CodeMap {
131
+ schema_version: '1.0.0';
132
+ engine_version: string;
133
+ commit: string | null;
134
+ coverage: Coverage[];
135
+ entry_points: EntryPoint[];
136
+ gates: Gate[];
137
+ config: {
138
+ supabase: { tables: SupabaseTable[] } | null; firebase: { rules: FirebaseRule[] } | null; android: AndroidConfig | null; ios: IosConfig | null; cargo: CargoConfig | null;
139
+ secrets: ConfigSecret[]; local_vocab: LocalVocabRecord; auth_helper_candidates: AuthHelperCandidate[];
140
+ };
141
+ counts: { entry_points: number; by_kind: Record<string, number>; by_framework: Record<string, number> };
142
+ }
@@ -0,0 +1,107 @@
1
+ // The findings record (the JSON inside the hullproof-findings block of report.findings.md), schema 1.1.0.
2
+ // The schema file is schemas/findings.schema.json, a copy of the kit's docs/hullproof/findings.schema.json; the kit file is the
3
+ // source of truth and the copy is checked by test/schemas.test.ts. Field limits (lengths, counts, patterns) live in the schema;
4
+ // cross field rules live in the kit's validate-findings.mjs. The engine writes schema_version "1.1.0".
5
+
6
+ export type Severity = 'BLOCKER' | 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW';
7
+ export type FailClass = 'finding' | 'missing_control' | 'hardening_note';
8
+ export type RouteStateWord = 'NEEDS DASHBOARD' | 'NEEDS BUILD' | 'NEEDS DYNAMIC TEST' | 'ASK OWNER' | 'ATTESTATION' | 'UNKNOWN';
9
+
10
+ export interface FileLineNote { file: string; line: number; note: string }
11
+
12
+ export interface Boundary { principal: string; input: string; intended_control: string; crossed: string; affected: string; result: string }
13
+ export interface TraceStep extends FileLineNote { kind: 'entrypoint' | 'handoff' | 'sink' }
14
+
15
+ // New in 1.1.0. A fail record carrying this may be class "finding" without a trace: the rule hit is the evidence.
16
+ export interface MachineEvidence {
17
+ tool: string; // "semgrep", "gitleaks", "trivy", "hullproof-check" and so on
18
+ rule_id: string; // scanner rule id or engine check id
19
+ tool_version: string;
20
+ config_sha256: string | null;
21
+ }
22
+
23
+ export interface FailRecord {
24
+ fingerprint: string; // "<SEC id|GAP|OBS>:<file or ->:<slug>"
25
+ kind: 'fail';
26
+ sec_ids: string[];
27
+ title: string;
28
+ class: FailClass;
29
+ gap_in_standard: boolean;
30
+ severity: Severity;
31
+ confidence: 'VERIFIED' | 'SUSPECTED';
32
+ demonstrated: boolean;
33
+ root_cause: string;
34
+ raised_by: string[];
35
+ impact_statement: string;
36
+ remediation: { strategy: string; confirm: string };
37
+ evidence: FileLineNote[];
38
+ boundary?: Boundary;
39
+ trace?: TraceStep[];
40
+ absence_search?: { command: string; files_searched: number; count: number; practice: 'absent' | 'present_unsafe' };
41
+ preventing_layer?: FileLineNote[];
42
+ paths_enumerated?: { count: number; method: string };
43
+ expiry_condition?: string;
44
+ owner_evidence?: { file: string; hash: string; hash_kind: 'git_blob' | 'sha256'; date: string; commit: string; note: string }[];
45
+ lowering?: { from: Severity; to: Severity; evidence: FileLineNote[] };
46
+ verified_by?: { agent: 'hullproof-finding-verifier'; mode: 'finding'; outcome: 'UPHELD' | 'ADJUSTED' | 'INSUFFICIENT'; own_severity?: Severity; own_class?: FailClass; call: number };
47
+ machine_evidence?: MachineEvidence;
48
+ }
49
+
50
+ export interface RoutedRecord {
51
+ fingerprint: string;
52
+ kind: 'routed';
53
+ sec_ids: string[];
54
+ title: string;
55
+ state: RouteStateWord;
56
+ reason: string;
57
+ owner_action: string;
58
+ check_card: string | null;
59
+ raised_by: string[];
60
+ boundary: Boundary;
61
+ trace?: TraceStep[];
62
+ verified_by?: { agent: 'hullproof-finding-verifier'; mode: 'handoff'; outcome: 'UPHELD' | 'ADJUSTED' | 'INSUFFICIENT'; call: number };
63
+ }
64
+
65
+ export interface RefutedRecord {
66
+ fingerprint: string;
67
+ kind: 'refuted';
68
+ sec_ids: string[];
69
+ title: string;
70
+ claim: string;
71
+ reason: string;
72
+ preventing_layer: FileLineNote[];
73
+ raised_by: string[];
74
+ verified_by: { agent: 'hullproof-finding-verifier'; mode: 'finding' | 'handoff'; outcome: 'REFUTED'; call: number };
75
+ }
76
+
77
+ export interface ObservationRecord {
78
+ fingerprint: string;
79
+ kind: 'observation';
80
+ sec_ids: string[];
81
+ title: string;
82
+ where: FileLineNote[];
83
+ note: string;
84
+ raised_by: string[];
85
+ }
86
+
87
+ export type FindingRecord = FailRecord | RoutedRecord | RefutedRecord | ObservationRecord;
88
+
89
+ export interface FindingsReport {
90
+ commit: string;
91
+ tree: string;
92
+ tree_clean: boolean;
93
+ stage: 'LAUNCH' | 'GROWTH' | 'SCALE';
94
+ profile: 'quick' | 'standard' | 'deep';
95
+ scope: 'full' | 'partial';
96
+ scope_note?: string;
97
+ run_status: 'complete' | 'incomplete';
98
+ incomplete_reason?: 'budget_exhausted' | 'agent_failure' | 'owner_stopped' | 'deterministic_only'; // deterministic_only: schema 1.1.0, engine run, no verifier by design
99
+ agent_calls: number;
100
+ budget: number | null;
101
+ }
102
+
103
+ export interface FindingsFile {
104
+ schema_version: '1.0.0' | '1.1.0';
105
+ report: FindingsReport;
106
+ records: FindingRecord[]; // sorted by fingerprint
107
+ }
@@ -0,0 +1,97 @@
1
+ // Types of the hunt files under <out>/<commit>/hunt/. Documented in docs/INTERFACES.md. The confirmation engine that reads them is
2
+ // open; the step that calls a model and writes the answers is not part of this repository.
3
+ export type Json = null | boolean | number | string | Json[] | { [k: string]: Json };
4
+
5
+ export interface LeadStep { file: string; line: number; note: string }
6
+
7
+ // A condition of the map query language (src/confirm/query.ts).
8
+ export type Cond =
9
+ | { all: Cond[] } | { any: Cond[] } | { not: Cond }
10
+ | { field: string; op: 'eq' | 'ne' | 'in' | 'contains' | 'startsWith' | 'exists' | 'gt' | 'lt'; value?: Json }
11
+ | { some: string; where?: Cond } | { none: string; where?: Cond };
12
+ export interface Query { where: Cond }
13
+
14
+ export type EvidenceProposal =
15
+ | { kind: 'map_query'; query: Query }
16
+ | { kind: 'semgrep_rule'; rule_yaml: string; control_code: string; control_extension: string }
17
+ | { kind: 'generated_test' }
18
+ | { kind: 'none'; rejected?: string };
19
+
20
+ export interface Lead {
21
+ lead_id: string;
22
+ entry_point: string;
23
+ requirement_ids: string[]; // sorted, unique
24
+ claim: string;
25
+ path: LeadStep[]; // entry point first, sink last
26
+ quoted_code: string; // the code at the sink, as it stands in the file
27
+ evidence_proposal: EvidenceProposal;
28
+ claim_truncated?: true; // the model's claim was longer than the limit and was shortened
29
+ }
30
+
31
+ // One saved answer: one call for one entry point in one run. Written as soon as it arrives.
32
+ export interface HuntAnswer {
33
+ schema_version: '1.0.0';
34
+ run: number;
35
+ entry_id: string;
36
+ status: 'complete' | 'incomplete';
37
+ stop: string | null; // why an incomplete call stopped: refusal, safety, rate_limit, timeout, length, ...
38
+ detail: string | null;
39
+ provider: string;
40
+ model: string;
41
+ context_sha256: string;
42
+ truncated: boolean;
43
+ usage: { input_tokens: number; output_tokens: number };
44
+ output: Json; // the model's JSON as received; null for an incomplete call
45
+ }
46
+
47
+ export interface HuntPlan {
48
+ schema_version: '1.0.0';
49
+ commit: string | null;
50
+ runs: number;
51
+ entries: string[]; // entry point ids in hunting order
52
+ }
53
+
54
+ export type Confirmation =
55
+ | { status: 'confirmed'; method: 'map_query' | 'semgrep_rule' | 'sandbox_test'; run: number; detail: string; tool: string; rule_id: string; tool_version: string; config_sha256: string }
56
+ | { status: 'not_confirmed'; method: 'map_query' | 'semgrep_rule' | 'generated_test' | 'none'; detail: string; reason?: 'unknown_protection'; helpers?: string[] }
57
+ | { status: 'not_run'; method: 'semgrep_rule'; detail: string };
58
+
59
+ export interface LeadOut {
60
+ key: string;
61
+ claim_truncated?: true;
62
+ entry_point: string;
63
+ requirement_ids: string[];
64
+ claim: string;
65
+ path: LeadStep[];
66
+ sink: { file: string; line: number };
67
+ runs_raised: number;
68
+ runs: number[];
69
+ confirmation: Confirmation;
70
+ }
71
+
72
+ export interface HuntResult {
73
+ schema_version: '1.0.0';
74
+ engine_version: string;
75
+ commit: string | null;
76
+ runs: number;
77
+ entries_hunted: number;
78
+ calls: { complete: number; incomplete: number; missing: number };
79
+ incomplete: { entry_id: string; run: number; stop: string; detail: string }[];
80
+ malformed: { count: number; items: { run: number; entry_id: string; lead_id: string; reason: string }[] };
81
+ hallucinations: { count: number; items: { run: number; entry_id: string; lead_id: string; reason: string }[] };
82
+ confirmed: LeadOut[];
83
+ leads: LeadOut[]; // everything not confirmed, most runs first
84
+ usage: { calls: number; input_tokens: number; output_tokens: number };
85
+ }
86
+
87
+ export interface HuntSynthesis {
88
+ schema_version: '1.0.0';
89
+ status: 'complete' | 'incomplete';
90
+ stop: string | null;
91
+ detail: string | null;
92
+ provider: string;
93
+ model: string;
94
+ summary: string;
95
+ explanations: { key: string; text: string }[];
96
+ usage: { input_tokens: number; output_tokens: number };
97
+ }
@@ -0,0 +1,10 @@
1
+ // Shared types for every engine module. Import with `import type { ... } from '../types/index.ts'`.
2
+ export type * from './stack.ts';
3
+ export type * from './scan.ts';
4
+ export type * from './check.ts';
5
+ export type * from './results.ts';
6
+ export type * from './findings.ts';
7
+ export { STACK_CATEGORIES } from './stack.ts';
8
+ export type * from './codemap.ts';
9
+ export type * from './hunt.ts';
10
+ export type * from './prove.ts';
@@ -0,0 +1,83 @@
1
+ // Types of the proof sandbox (Phase 4): the test spec language, the recipe that starts an app, and the files under <out>/<commit>/prove/.
2
+ // Documented in docs/INTERFACES.md and docs/PHASE-4-DESIGN.md.
3
+ import type { Json } from './hunt.ts';
4
+
5
+ export type Method = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
6
+ export type Phase = 'setup' | 'control' | 'attack';
7
+
8
+ export type Assertion =
9
+ | { kind: 'status'; op: 'eq' | 'ne' | 'in'; value: number | number[] }
10
+ | { kind: 'json'; path: string; op: 'eq' | 'ne' | 'contains' | 'exists' | 'absent'; value?: string | number | boolean | null }
11
+ | { kind: 'text'; op: 'contains' | 'absent'; value: string }
12
+ | { kind: 'header'; name: string; op: 'eq' | 'contains' | 'exists' | 'absent'; value?: string };
13
+
14
+ export interface Capture { name: string; from: 'json' | 'header' | 'status'; path?: string }
15
+
16
+ export interface SpecStep {
17
+ id: string; phase: Phase; actor: string; method: Method; path: string;
18
+ headers?: Record<string, string>; body?: { [k: string]: Json } | null; captures?: Capture[]; assert: Assertion[];
19
+ }
20
+
21
+ export interface Spec {
22
+ schema_version: '1.0.0';
23
+ spec_id: string; title: string; claim: string;
24
+ lead_key: string | null; // the hunt lead this spec tests, when it came from one
25
+ target: { entry_point: string; file: string; line: number };
26
+ requirement_ids: string[];
27
+ actors: Record<string, { headers?: Record<string, string> }>;
28
+ steps: SpecStep[];
29
+ expect: { outcome: 'flaw_present' }; // the attack steps' assertions describe the flaw; they all hold when it is there
30
+ }
31
+
32
+ export interface StepRecord {
33
+ id: string; phase: Phase; actor: string;
34
+ error: string | null; // the request could not be made (connection, timeout, bad variable)
35
+ request: { method: Method; path: string; headers: Record<string, string>; body: Json };
36
+ response: { status: number; headers: Record<string, string>; json: Json | undefined; text: string | undefined; truncated: boolean } | null;
37
+ captures: Record<string, Json>;
38
+ assertions: { ok: boolean; desc: string }[];
39
+ }
40
+
41
+ export type RunOutcome = 'proven' | 'not_proven' | 'invalid' | 'error';
42
+ export type SpecOutcome = RunOutcome | 'unstable' | 'not_provable';
43
+
44
+ // One run of one spec, as stored under prove/runs/<spec_id>/run-N.json. The hash covers everything but itself.
45
+ export interface RunRecord {
46
+ schema_version: '1.0.0'; spec_id: string; spec_sha256: string; run: number;
47
+ outcome: RunOutcome; detail: string; steps: StepRecord[]; transcript_sha256: string;
48
+ }
49
+
50
+ export interface ProveRunSummary { run: number; outcome: RunOutcome; transcript_sha256: string; detail: string }
51
+ export interface ProveSpecResult {
52
+ spec_id: string; spec_sha256: string; lead_key: string | null; title: string; claim: string;
53
+ entry_point: string; requirement_ids: string[]; sink: { file: string; line: number };
54
+ outcome: SpecOutcome; reason: string; runs: ProveRunSummary[];
55
+ }
56
+
57
+ export type NotProvableCode =
58
+ | 'no_package_json' | 'unsupported_stack' | 'hosted_service' | 'unsupported_datastore' | 'no_lockfile' | 'needs_install_scripts'
59
+ | 'no_start_command' | 'port_unknown' | 'db_path_hardcoded' | 'no_seed' | 'no_runtime' | 'refused_unsafe_backend' | 'build_failed' | 'start_failed' | 'health_failed' | 'time_limit' | 'too_large';
60
+ export interface NotProvable { code: NotProvableCode; reason: string }
61
+
62
+ export interface ProveMeta { schema_version: '1.0.0'; commit: string | null; runner: string; runs_required: number; recipe_sha256: string | null; not_provable: NotProvable | null; left_behind: number; copy_excluded: string[] }
63
+ export interface ProveResult {
64
+ schema_version: '1.0.0'; engine_version: string; commit: string | null; runner: string; runs_required: number;
65
+ recipe_sha256: string | null; not_provable: NotProvable | null;
66
+ specs: ProveSpecResult[];
67
+ dropped: { file: string; reason: string }[]; // spec files that failed validation and were not run
68
+ left_behind: number; // containers, images or networks still there after teardown (0 when clean)
69
+ copy_excluded: string[]; // what was left out of the copy that went into the sandbox
70
+ counts: { specs: number; proven: number; not_proven: number; invalid: number; unstable: number; error: number; not_provable: number; dropped: number };
71
+ }
72
+
73
+ export interface Recipe {
74
+ schema_version: '1.0.0'; stack: 'node-express' | 'python-flask'; base_image: string;
75
+ install_scripts?: boolean; // true only when the owner passed --allow-install-scripts: install scripts run (a Python package may build from source) in the build container
76
+ install: string[] | null; // argv of the build phase install, null when nothing is installed
77
+ vendored_modules: boolean; // node_modules of the repository is part of the copy (no install, no registry); always false for Python
78
+ seed: string | null; start: string; port: number; port_from_env: boolean; health_path: string;
79
+ data_store: { kind: 'none' } | { kind: 'sqlite'; env: string[] } | { kind: 'postgres'; migrations: string[]; seed_sql: string | null };
80
+ env: Record<string, string>; // dummy values only; {DATA} stands for the writable data folder
81
+ notes: string[];
82
+ }
83
+ export type RecipeResult = { ok: true; recipe: Recipe } | ({ ok: false } & NotProvable);
@@ -0,0 +1,15 @@
1
+ // results.json: the per requirement result list the kit gate (hullproof-gate.mjs, loadResults) already reads.
2
+
3
+ export type RouteState = 'NEEDS DASHBOARD' | 'NEEDS BUILD' | 'NEEDS DYNAMIC TEST' | 'ASK OWNER' | 'ATTESTATION' | 'UNKNOWN';
4
+
5
+ // The result words of STANDARD.md. "PASS (static)" settles only repo authority controls; the gate enforces that.
6
+ export type ResultWord = 'FAIL' | 'PASS (static)' | 'NOT APPLICABLE' | `NOT ASSESSED: ${RouteState}`;
7
+
8
+ export interface ResultRow {
9
+ id: string; // SEC-XXX-000
10
+ result: ResultWord;
11
+ notes: string; // NOT APPLICABLE needs a reason here; plain text, no secret values
12
+ }
13
+
14
+ // results.json is a JSON array of ResultRow sorted by id.
15
+ export type ResultsFile = ResultRow[];
@@ -0,0 +1,63 @@
1
+ // Scanner adapters and their normalised output. JSON schemas: schemas/scan-hits.schema.json, schemas/scan-meta.schema.json.
2
+ // Written by the adapters in src/scanners/. Documented in docs/INTERFACES.md.
3
+ import type { Stack } from './stack.ts';
4
+
5
+ export type ScannerId = 'semgrep' | 'osv-scanner' | 'gitleaks' | 'trivy' | 'checkov';
6
+
7
+ // One normalised scanner hit. No secret value, no tool message text, no absolute path, no timing.
8
+ export interface Hit {
9
+ tool: ScannerId;
10
+ rule_id: string; // the tool's own rule or check id, as the tool prints it
11
+ file: string; // repository relative POSIX path
12
+ line: number; // 1 based; 0 only when the tool gives no line (for example a lockfile level advisory)
13
+ sec_ids: string[]; // requirement IDs from the rule metadata or engine/maps/<tool>.json; [] means unmapped
14
+ severity_hint: string | null; // the tool's own severity word, kept for the report; never used by the gate
15
+ message_key: string; // key into the engine's plain English text, normally `${tool}:${rule_id}`
16
+ noise?: string; // secret hits only: why the value is an example, not a live secret (src/scanners/secret-noise.ts); absent when it must hold
17
+ }
18
+
19
+ // scan/<tool>.json
20
+ export interface HitsFile {
21
+ schema_version: '1.0.0';
22
+ tool: ScannerId;
23
+ hits: Hit[]; // sorted by file, line, tool, rule_id
24
+ }
25
+
26
+ // scan/<tool>.meta.json. The reproducibility record.
27
+ export interface ToolMeta {
28
+ schema_version: '1.0.0';
29
+ tool: ScannerId;
30
+ status: 'ran' | 'skipped: not installed' | 'skipped: not applicable' | 'skipped: network not allowed' | 'failed';
31
+ version: string | null;
32
+ tested_version_range: string;
33
+ version_tested: boolean; // false when the installed version is outside tested_version_range
34
+ argv: string[]; // exact command, no shell; [] when skipped
35
+ config_sha256: string | null;
36
+ commit: string | null;
37
+ files_scanned: number | null;
38
+ files_tracked: number | null;
39
+ exit_code: number | null;
40
+ db_date: string | null; // OSV Scanner advisory data date, else null
41
+ not_assessed_sec_ids: string[]; // IDs that stay NOT ASSESSED because this tool did not run
42
+ }
43
+
44
+ export interface RunContext {
45
+ repo: string; // absolute path of the repository being scanned
46
+ out: string; // absolute path of the output folder
47
+ stack: Stack;
48
+ allowNetwork: boolean;
49
+ edition: 'free' | 'pro';
50
+ kit?: string | null; // the kit folder the scan resolved; absent or null: the path order in src/mapping/controls.ts
51
+ history?: boolean; // set by the scan step after its git configuration check; lets `gitleaks git` run
52
+ }
53
+
54
+ export interface ScannerAdapter {
55
+ id: ScannerId;
56
+ versionCommand: string[];
57
+ testedVersions: string;
58
+ needsNetwork: boolean;
59
+ applies(stack: Stack): boolean;
60
+ command(ctx: RunContext): string[];
61
+ configFiles(ctx: RunContext): string[];
62
+ parse(stdout: string, ctx: RunContext): Hit[];
63
+ }
@@ -0,0 +1,61 @@
1
+ // stack.json, written by `hullproof discover`. JSON schema: schemas/stack.schema.json. Documented in docs/INTERFACES.md.
2
+
3
+ export type Confidence = 'high' | 'medium' | 'low';
4
+
5
+ export const STACK_CATEGORIES = [
6
+ 'languages', 'runtimes', 'frameworks', 'package_managers', 'data_stores', 'auth', 'payments', 'ai',
7
+ 'hosting', 'iac', 'containers', 'ci', 'mobile',
8
+ ] as const;
9
+ export type StackCategory = typeof STACK_CATEGORIES[number];
10
+
11
+ // One proof that an item is present. Exactly one of line or key is set.
12
+ // line: 1 based line in file. key: a JSON key path such as "dependencies.express", or "(present)" when the file itself
13
+ // is the signal (vercel.json, Dockerfile), or "(extension)" when the file extension is the signal.
14
+ export interface Evidence {
15
+ file: string; // repository relative POSIX path
16
+ line?: number;
17
+ key?: string;
18
+ rule: string; // detector rule id, for example "frameworks.express.dep.npm"
19
+ confidence: Confidence;
20
+ }
21
+
22
+ export interface StackItem {
23
+ id: string;
24
+ confidence: Confidence; // the best confidence among all evidence
25
+ evidence: Evidence[]; // at most 5, best confidence first, then file, then line
26
+ evidence_count: number; // all evidence found, including entries not listed
27
+ versions?: string[]; // declared versions or ranges from manifests, as written
28
+ file_count?: number; // languages only: files with this language's extensions
29
+ kind?: string; // hosting only: "platform" or "cloud"
30
+ clouds?: string[]; // iac only: providers found in those files
31
+ lockfiles?: string[]; // package_managers only
32
+ manifests?: string[]; // package_managers only
33
+ }
34
+
35
+ export interface Skipped { path: string; reason: 'gitignore' | 'dependency folder' | 'build output' | 'tool folder' | 'symlink' | 'unchanged kit files' }
36
+ export interface Unknown { file: string; note: string }
37
+
38
+ export interface Stack {
39
+ schema_version: '1.0.0';
40
+ engine_version: string;
41
+ detectors_sha256: string; // sha256 over the sorted detector files
42
+ commit: string | null; // HEAD commit read from .git without running git; null when not a git repository
43
+ confirmed_by_user: boolean; // false until `--confirm` exists (later phase)
44
+ files: { listed: number; scanned_for_imports: number };
45
+ limits: { max_files: number; max_bytes_per_file: number; file_cap_reached: boolean; truncated_files: string[] };
46
+ skipped: Skipped[];
47
+ languages: StackItem[];
48
+ runtimes: StackItem[];
49
+ frameworks: StackItem[];
50
+ package_managers: StackItem[];
51
+ data_stores: StackItem[];
52
+ auth: StackItem[];
53
+ payments: StackItem[];
54
+ ai: StackItem[];
55
+ hosting: StackItem[];
56
+ iac: StackItem[];
57
+ containers: StackItem[];
58
+ ci: StackItem[];
59
+ mobile: StackItem[];
60
+ unknown: Unknown[];
61
+ }