@safegres/test 0.0.1 → 1.30.7

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 (483) hide show
  1. package/README.md +825 -412
  2. package/adapters.d.ts +8 -0
  3. package/adapters.js +18 -0
  4. package/ast/helpers.d.ts +16 -0
  5. package/ast/helpers.js +40 -0
  6. package/ast/parse.d.ts +31 -0
  7. package/ast/parse.js +87 -0
  8. package/ast/walk.d.ts +27 -0
  9. package/ast/walk.js +79 -0
  10. package/callgraph/baseline.d.ts +28 -0
  11. package/callgraph/baseline.js +55 -0
  12. package/callgraph/extract.d.ts +93 -0
  13. package/callgraph/extract.js +340 -0
  14. package/callgraph/graph.d.ts +82 -0
  15. package/callgraph/graph.js +276 -0
  16. package/checks/anti-patterns.d.ts +53 -0
  17. package/checks/anti-patterns.js +247 -0
  18. package/checks/column-grants.d.ts +20 -0
  19. package/checks/column-grants.js +74 -0
  20. package/checks/coverage.d.ts +43 -0
  21. package/checks/coverage.js +110 -0
  22. package/checks/definer-function.d.ts +91 -0
  23. package/checks/definer-function.js +458 -0
  24. package/checks/definer-view.d.ts +167 -0
  25. package/checks/definer-view.js +392 -0
  26. package/checks/indexes.d.ts +82 -0
  27. package/checks/indexes.js +263 -0
  28. package/checks/lattice.d.ts +179 -0
  29. package/checks/lattice.js +487 -0
  30. package/checks/object-acls.d.ts +32 -0
  31. package/checks/object-acls.js +129 -0
  32. package/checks/policy-index.d.ts +78 -0
  33. package/checks/policy-index.js +349 -0
  34. package/checks/revocable-grants.d.ts +140 -0
  35. package/checks/revocable-grants.js +377 -0
  36. package/checks/rls-flags.d.ts +25 -0
  37. package/checks/rls-flags.js +82 -0
  38. package/checks/role-reach.d.ts +329 -0
  39. package/checks/role-reach.js +277 -0
  40. package/checks/role-trust.d.ts +45 -0
  41. package/checks/role-trust.js +91 -0
  42. package/checks/set-role.d.ts +21 -0
  43. package/checks/set-role.js +66 -0
  44. package/checks/stats.d.ts +52 -0
  45. package/checks/stats.js +192 -0
  46. package/checks/view-exposure.d.ts +68 -0
  47. package/checks/view-exposure.js +236 -0
  48. package/checks/view-writes.d.ts +88 -0
  49. package/checks/view-writes.js +350 -0
  50. package/ci/baseline.d.ts +104 -0
  51. package/ci/baseline.js +111 -0
  52. package/ci/github.d.ts +47 -0
  53. package/ci/github.js +197 -0
  54. package/cli/audit.d.ts +3 -0
  55. package/cli/audit.js +560 -0
  56. package/cli/baseline.d.ts +3 -0
  57. package/cli/baseline.js +104 -0
  58. package/cli/commands.d.ts +2 -0
  59. package/cli/commands.js +60 -0
  60. package/cli/doctor.d.ts +3 -0
  61. package/cli/doctor.js +72 -0
  62. package/cli/eval.d.ts +3 -0
  63. package/cli/eval.js +153 -0
  64. package/cli/print-config.d.ts +3 -0
  65. package/cli/print-config.js +54 -0
  66. package/cli/shared.d.ts +43 -0
  67. package/cli/shared.js +153 -0
  68. package/cli.d.ts +2 -0
  69. package/cli.js +69 -0
  70. package/commands/audit.d.ts +65 -0
  71. package/commands/audit.js +779 -0
  72. package/commands/doctor.d.ts +19 -0
  73. package/commands/doctor.js +181 -0
  74. package/commands/eval.d.ts +67 -0
  75. package/commands/eval.js +93 -0
  76. package/config/fingerprint.d.ts +40 -0
  77. package/config/fingerprint.js +71 -0
  78. package/config/loader.d.ts +44 -0
  79. package/config/loader.js +210 -0
  80. package/config/presets.d.ts +95 -0
  81. package/config/presets.js +327 -0
  82. package/config/resolve.d.ts +47 -0
  83. package/config/resolve.js +192 -0
  84. package/config/schema.d.ts +54 -0
  85. package/config/schema.js +269 -0
  86. package/config/types.d.ts +562 -0
  87. package/config/validate.d.ts +14 -0
  88. package/config/validate.js +125 -0
  89. package/corpus/README.md +83 -0
  90. package/corpus/bootstrap.sql +14 -0
  91. package/corpus/cases/01-anon-write-grant/case.json +26 -0
  92. package/corpus/cases/01-anon-write-grant/schema.sql +19 -0
  93. package/corpus/cases/02-grant-to-public/case.json +25 -0
  94. package/corpus/cases/02-grant-to-public/schema.sql +18 -0
  95. package/corpus/cases/03-rls-off-reachable-by-anon/case.json +30 -0
  96. package/corpus/cases/03-rls-off-reachable-by-anon/schema.sql +13 -0
  97. package/corpus/cases/04-permissive-write-policy/case.json +30 -0
  98. package/corpus/cases/04-permissive-write-policy/schema.sql +21 -0
  99. package/corpus/cases/05-public-read-policy/case.json +25 -0
  100. package/corpus/cases/05-public-read-policy/schema.sql +16 -0
  101. package/corpus/cases/06-update-without-with-check/case.json +25 -0
  102. package/corpus/cases/06-update-without-with-check/schema.sql +19 -0
  103. package/corpus/cases/07-rls-enabled-no-policies/case.json +29 -0
  104. package/corpus/cases/07-rls-enabled-no-policies/schema.sql +14 -0
  105. package/corpus/cases/08-select-grant-no-policy/case.json +25 -0
  106. package/corpus/cases/08-select-grant-no-policy/schema.sql +20 -0
  107. package/corpus/cases/09-write-grant-no-policy/case.json +24 -0
  108. package/corpus/cases/09-write-grant-no-policy/schema.sql +18 -0
  109. package/corpus/cases/10-rls-not-forced/case.json +24 -0
  110. package/corpus/cases/10-rls-not-forced/schema.sql +18 -0
  111. package/corpus/cases/11-session-user-in-policy/case.json +24 -0
  112. package/corpus/cases/11-session-user-in-policy/schema.sql +18 -0
  113. package/corpus/cases/12-volatile-function-in-policy/case.json +24 -0
  114. package/corpus/cases/12-volatile-function-in-policy/schema.sql +21 -0
  115. package/corpus/cases/13-security-definer-policy-wrapper/case.json +24 -0
  116. package/corpus/cases/13-security-definer-policy-wrapper/schema.sql +21 -0
  117. package/corpus/cases/14-dead-policy/case.json +24 -0
  118. package/corpus/cases/14-dead-policy/schema.sql +18 -0
  119. package/corpus/cases/15-unreachable-grant/case.json +24 -0
  120. package/corpus/cases/15-unreachable-grant/schema.sql +18 -0
  121. package/corpus/cases/16-anon-permissive-write-policy/case.json +34 -0
  122. package/corpus/cases/16-anon-permissive-write-policy/schema.sql +17 -0
  123. package/corpus/cases/17-foreign-key-without-index/case.json +24 -0
  124. package/corpus/cases/17-foreign-key-without-index/schema.sql +18 -0
  125. package/corpus/cases/18-policy-column-unindexed/case.json +24 -0
  126. package/corpus/cases/18-policy-column-unindexed/schema.sql +23 -0
  127. package/corpus/cases/19-policy-column-cast/case.json +24 -0
  128. package/corpus/cases/19-policy-column-cast/schema.sql +19 -0
  129. package/corpus/cases/20-stable-function-per-row/case.json +24 -0
  130. package/corpus/cases/20-stable-function-per-row/schema.sql +25 -0
  131. package/corpus/cases/21-table-without-primary-key/case.json +24 -0
  132. package/corpus/cases/21-table-without-primary-key/schema.sql +14 -0
  133. package/corpus/cases/22-redundant-index/case.json +24 -0
  134. package/corpus/cases/22-redundant-index/schema.sql +16 -0
  135. package/corpus/cases/23-tsvector-without-index/case.json +24 -0
  136. package/corpus/cases/23-tsvector-without-index/schema.sql +13 -0
  137. package/corpus/cases/24-anon-set-role-escalation/case.json +29 -0
  138. package/corpus/cases/24-anon-set-role-escalation/schema.sql +30 -0
  139. package/corpus/cases/25-definer-view-bypass/case.json +31 -0
  140. package/corpus/cases/25-definer-view-bypass/schema.sql +36 -0
  141. package/corpus/cases/26-invoker-view-no-bypass/case.json +31 -0
  142. package/corpus/cases/26-invoker-view-no-bypass/schema.sql +44 -0
  143. package/corpus/cases/27-definer-view-write/case.json +36 -0
  144. package/corpus/cases/27-definer-view-write/schema.sql +40 -0
  145. package/corpus/cases/28-invoker-view-no-write/case.json +31 -0
  146. package/corpus/cases/28-invoker-view-no-write/schema.sql +37 -0
  147. package/corpus/cases/29-rewrite-rule-bypass/case.json +36 -0
  148. package/corpus/cases/29-rewrite-rule-bypass/schema.sql +52 -0
  149. package/corpus/cases/30-instead-nothing-rule/case.json +31 -0
  150. package/corpus/cases/30-instead-nothing-rule/schema.sql +44 -0
  151. package/corpus/cases/31-matview-snapshot/case.json +32 -0
  152. package/corpus/cases/31-matview-snapshot/schema.sql +36 -0
  153. package/corpus/cases/32-matview-no-rls-no-bypass/case.json +30 -0
  154. package/corpus/cases/32-matview-no-rls-no-bypass/schema.sql +18 -0
  155. package/corpus/cases/33-leaky-filter-view/case.json +36 -0
  156. package/corpus/cases/33-leaky-filter-view/schema.sql +29 -0
  157. package/corpus/cases/34-barrier-view-no-leak/case.json +32 -0
  158. package/corpus/cases/34-barrier-view-no-leak/schema.sql +29 -0
  159. package/corpus/cases/35-column-grant-projection/case.json +27 -0
  160. package/corpus/cases/35-column-grant-projection/schema.sql +21 -0
  161. package/corpus/cases/35-policy-function-not-leakproof/case.json +24 -0
  162. package/corpus/cases/35-policy-function-not-leakproof/schema.sql +27 -0
  163. package/corpus/cases/36-column-grant-rls-mediated/case.json +27 -0
  164. package/corpus/cases/36-column-grant-rls-mediated/schema.sql +32 -0
  165. package/corpus/cases/36-policy-function-leakproof/case.json +24 -0
  166. package/corpus/cases/36-policy-function-leakproof/schema.sql +22 -0
  167. package/corpus/cases/37-sort-column-unindexed/case.json +24 -0
  168. package/corpus/cases/37-sort-column-unindexed/schema.sql +14 -0
  169. package/corpus/cases/37-unaudited-base-relation/case.json +30 -0
  170. package/corpus/cases/37-unaudited-base-relation/schema.sql +45 -0
  171. package/corpus/cases/38-foreign-key-with-index/case.json +22 -0
  172. package/corpus/cases/38-foreign-key-with-index/schema.sql +17 -0
  173. package/corpus/cases/38-invoker-view-unaudited-base/case.json +30 -0
  174. package/corpus/cases/38-invoker-view-unaudited-base/schema.sql +39 -0
  175. package/corpus/cases/39-definer-view-column-subset/case.json +30 -0
  176. package/corpus/cases/39-definer-view-column-subset/schema.sql +29 -0
  177. package/corpus/cases/39-policy-cast-with-expression-index/case.json +23 -0
  178. package/corpus/cases/39-policy-cast-with-expression-index/schema.sql +19 -0
  179. package/corpus/cases/40-definer-view-column-overreach/case.json +27 -0
  180. package/corpus/cases/40-definer-view-column-overreach/schema.sql +29 -0
  181. package/corpus/cases/40-stable-function-hoisted/case.json +23 -0
  182. package/corpus/cases/40-stable-function-hoisted/schema.sql +25 -0
  183. package/corpus/cases/41-index-prefix-not-redundant/case.json +21 -0
  184. package/corpus/cases/41-index-prefix-not-redundant/schema.sql +15 -0
  185. package/corpus/cases/41-unreadable-view-body/case.json +33 -0
  186. package/corpus/cases/41-unreadable-view-body/schema.sql +28 -0
  187. package/corpus/cases/42-invoker-view-unreadable-body/case.json +30 -0
  188. package/corpus/cases/42-invoker-view-unreadable-body/schema.sql +27 -0
  189. package/corpus/cases/42-search-column-indexed/case.json +21 -0
  190. package/corpus/cases/42-search-column-indexed/schema.sql +12 -0
  191. package/corpus/cases/43-anon-sequence-grant/case.json +27 -0
  192. package/corpus/cases/43-anon-sequence-grant/schema.sql +23 -0
  193. package/corpus/cases/44-serial-sequence-usage/case.json +27 -0
  194. package/corpus/cases/44-serial-sequence-usage/schema.sql +25 -0
  195. package/corpus/cases/45-anon-foreign-table/case.json +27 -0
  196. package/corpus/cases/45-anon-foreign-table/schema.sql +23 -0
  197. package/corpus/cases/46-unchecked-writable-view/case.json +41 -0
  198. package/corpus/cases/46-unchecked-writable-view/schema.sql +37 -0
  199. package/corpus/cases/47-checked-writable-view/case.json +37 -0
  200. package/corpus/cases/47-checked-writable-view/schema.sql +38 -0
  201. package/corpus/cases/48-definer-function-reach/case.json +38 -0
  202. package/corpus/cases/48-definer-function-reach/schema.sql +41 -0
  203. package/corpus/cases/49-invoker-function-no-reach/case.json +32 -0
  204. package/corpus/cases/49-invoker-function-no-reach/schema.sql +38 -0
  205. package/corpus/cases/50-instead-of-trigger-write/case.json +38 -0
  206. package/corpus/cases/50-instead-of-trigger-write/schema.sql +58 -0
  207. package/corpus/cases/51-invoker-trigger-no-write/case.json +33 -0
  208. package/corpus/cases/51-invoker-trigger-no-write/schema.sql +54 -0
  209. package/corpus/cases/52-revocable-execute-grant/case.json +30 -0
  210. package/corpus/cases/52-revocable-execute-grant/schema.sql +45 -0
  211. package/corpus/cases/53-policy-predicate-retains-execute/case.json +23 -0
  212. package/corpus/cases/53-policy-predicate-retains-execute/schema.sql +49 -0
  213. package/corpus/index.d.ts +79 -0
  214. package/corpus/index.js +125 -0
  215. package/esm/adapters.d.ts +8 -0
  216. package/esm/adapters.js +6 -0
  217. package/esm/ast/helpers.d.ts +16 -0
  218. package/esm/ast/helpers.js +36 -0
  219. package/esm/ast/parse.d.ts +31 -0
  220. package/esm/ast/parse.js +82 -0
  221. package/esm/ast/walk.d.ts +27 -0
  222. package/esm/ast/walk.js +72 -0
  223. package/esm/callgraph/baseline.d.ts +28 -0
  224. package/esm/callgraph/baseline.js +48 -0
  225. package/esm/callgraph/extract.d.ts +93 -0
  226. package/esm/callgraph/extract.js +333 -0
  227. package/esm/callgraph/graph.d.ts +82 -0
  228. package/esm/callgraph/graph.js +273 -0
  229. package/esm/checks/anti-patterns.d.ts +53 -0
  230. package/esm/checks/anti-patterns.js +240 -0
  231. package/esm/checks/column-grants.d.ts +20 -0
  232. package/esm/checks/column-grants.js +71 -0
  233. package/esm/checks/coverage.d.ts +43 -0
  234. package/esm/checks/coverage.js +104 -0
  235. package/esm/checks/definer-function.d.ts +91 -0
  236. package/esm/checks/definer-function.js +452 -0
  237. package/esm/checks/definer-view.d.ts +167 -0
  238. package/esm/checks/definer-view.js +382 -0
  239. package/esm/checks/indexes.d.ts +82 -0
  240. package/esm/checks/indexes.js +256 -0
  241. package/esm/checks/lattice.d.ts +179 -0
  242. package/esm/checks/lattice.js +475 -0
  243. package/esm/checks/object-acls.d.ts +32 -0
  244. package/esm/checks/object-acls.js +125 -0
  245. package/esm/checks/policy-index.d.ts +78 -0
  246. package/esm/checks/policy-index.js +342 -0
  247. package/esm/checks/revocable-grants.d.ts +140 -0
  248. package/esm/checks/revocable-grants.js +373 -0
  249. package/esm/checks/rls-flags.d.ts +25 -0
  250. package/esm/checks/rls-flags.js +77 -0
  251. package/esm/checks/role-reach.d.ts +329 -0
  252. package/esm/checks/role-reach.js +270 -0
  253. package/esm/checks/role-trust.d.ts +45 -0
  254. package/esm/checks/role-trust.js +86 -0
  255. package/esm/checks/set-role.d.ts +21 -0
  256. package/esm/checks/set-role.js +63 -0
  257. package/esm/checks/stats.d.ts +52 -0
  258. package/esm/checks/stats.js +184 -0
  259. package/esm/checks/view-exposure.d.ts +68 -0
  260. package/esm/checks/view-exposure.js +231 -0
  261. package/esm/checks/view-writes.d.ts +88 -0
  262. package/esm/checks/view-writes.js +344 -0
  263. package/esm/ci/baseline.d.ts +104 -0
  264. package/esm/ci/baseline.js +107 -0
  265. package/esm/ci/github.d.ts +47 -0
  266. package/esm/ci/github.js +161 -0
  267. package/esm/cli/audit.d.ts +3 -0
  268. package/esm/cli/audit.js +525 -0
  269. package/esm/cli/baseline.d.ts +3 -0
  270. package/esm/cli/baseline.js +102 -0
  271. package/esm/cli/commands.d.ts +2 -0
  272. package/esm/cli/commands.js +53 -0
  273. package/esm/cli/doctor.d.ts +3 -0
  274. package/esm/cli/doctor.js +67 -0
  275. package/esm/cli/eval.d.ts +3 -0
  276. package/esm/cli/eval.js +115 -0
  277. package/esm/cli/print-config.d.ts +3 -0
  278. package/esm/cli/print-config.js +52 -0
  279. package/esm/cli/shared.d.ts +43 -0
  280. package/esm/cli/shared.js +112 -0
  281. package/esm/cli.d.ts +2 -0
  282. package/esm/cli.js +67 -0
  283. package/esm/commands/audit.d.ts +65 -0
  284. package/esm/commands/audit.js +776 -0
  285. package/esm/commands/doctor.d.ts +19 -0
  286. package/esm/commands/doctor.js +178 -0
  287. package/esm/commands/eval.d.ts +67 -0
  288. package/esm/commands/eval.js +90 -0
  289. package/esm/config/fingerprint.d.ts +40 -0
  290. package/esm/config/fingerprint.js +68 -0
  291. package/esm/config/loader.d.ts +44 -0
  292. package/esm/config/loader.js +172 -0
  293. package/esm/config/presets.d.ts +95 -0
  294. package/esm/config/presets.js +324 -0
  295. package/esm/config/resolve.d.ts +47 -0
  296. package/esm/config/resolve.js +180 -0
  297. package/esm/config/schema.d.ts +54 -0
  298. package/esm/config/schema.js +265 -0
  299. package/esm/config/types.d.ts +562 -0
  300. package/esm/config/validate.d.ts +14 -0
  301. package/esm/config/validate.js +122 -0
  302. package/esm/corpus/index.d.ts +79 -0
  303. package/esm/corpus/index.js +85 -0
  304. package/esm/exposure/adapters.d.ts +152 -0
  305. package/esm/exposure/adapters.js +463 -0
  306. package/esm/exposure/planes.d.ts +59 -0
  307. package/esm/exposure/planes.js +144 -0
  308. package/esm/exposure/reach.d.ts +78 -0
  309. package/esm/exposure/reach.js +105 -0
  310. package/esm/exposure/routines.d.ts +36 -0
  311. package/esm/exposure/routines.js +54 -0
  312. package/esm/index.d.ts +96 -0
  313. package/esm/index.js +57 -6
  314. package/esm/lint/index.d.ts +11 -0
  315. package/esm/lint/index.js +10 -0
  316. package/esm/perf/baseline.d.ts +40 -0
  317. package/esm/perf/baseline.js +83 -0
  318. package/esm/perf/explain.d.ts +46 -0
  319. package/esm/perf/explain.js +201 -0
  320. package/esm/pg/acl.d.ts +72 -0
  321. package/esm/pg/acl.js +186 -0
  322. package/esm/pg/behaviors.d.ts +114 -0
  323. package/esm/pg/behaviors.js +206 -0
  324. package/esm/pg/exposure.d.ts +74 -0
  325. package/esm/pg/exposure.js +198 -0
  326. package/esm/pg/functions.d.ts +52 -0
  327. package/esm/pg/functions.js +128 -0
  328. package/esm/pg/indexes.d.ts +192 -0
  329. package/esm/pg/indexes.js +395 -0
  330. package/esm/pg/introspect.d.ts +134 -0
  331. package/esm/pg/introspect.js +274 -0
  332. package/esm/pg/objects.d.ts +37 -0
  333. package/esm/pg/objects.js +108 -0
  334. package/esm/pg/paths.d.ts +114 -0
  335. package/esm/pg/paths.js +159 -0
  336. package/esm/pg/proc.d.ts +23 -0
  337. package/esm/pg/proc.js +55 -0
  338. package/esm/pg/reach-inputs.d.ts +47 -0
  339. package/esm/pg/reach-inputs.js +133 -0
  340. package/esm/pg/roles.d.ts +28 -0
  341. package/esm/pg/roles.js +50 -0
  342. package/esm/pg/stats.d.ts +65 -0
  343. package/esm/pg/stats.js +129 -0
  344. package/esm/pgpm-test.d.ts +22 -0
  345. package/esm/pgpm-test.js +39 -0
  346. package/esm/report/callgraph.d.ts +9 -0
  347. package/esm/report/callgraph.js +74 -0
  348. package/esm/report/compare.d.ts +143 -0
  349. package/esm/report/compare.js +195 -0
  350. package/esm/report/github.d.ts +74 -0
  351. package/esm/report/github.js +290 -0
  352. package/esm/report/json.d.ts +4 -0
  353. package/esm/report/json.js +3 -0
  354. package/esm/report/markdown.d.ts +25 -0
  355. package/esm/report/markdown.js +352 -0
  356. package/esm/report/pretty.d.ts +14 -0
  357. package/esm/report/pretty.js +318 -0
  358. package/esm/report/sarif.d.ts +40 -0
  359. package/esm/report/sarif.js +189 -0
  360. package/esm/report/view.d.ts +82 -0
  361. package/esm/report/view.js +126 -0
  362. package/esm/rules/registry.d.ts +66 -0
  363. package/esm/rules/registry.js +562 -0
  364. package/esm/score/score.d.ts +84 -0
  365. package/esm/score/score.js +233 -0
  366. package/esm/score/scorecards.d.ts +68 -0
  367. package/esm/score/scorecards.js +157 -0
  368. package/esm/types.d.ts +292 -0
  369. package/esm/types.js +26 -0
  370. package/esm/version.d.ts +8 -0
  371. package/esm/version.js +9 -0
  372. package/exposure/adapters.d.ts +152 -0
  373. package/exposure/adapters.js +468 -0
  374. package/exposure/planes.d.ts +59 -0
  375. package/exposure/planes.js +151 -0
  376. package/exposure/reach.d.ts +78 -0
  377. package/exposure/reach.js +109 -0
  378. package/exposure/routines.d.ts +36 -0
  379. package/exposure/routines.js +57 -0
  380. package/index.d.ts +96 -6
  381. package/index.js +229 -6
  382. package/lint/index.d.ts +11 -0
  383. package/lint/index.js +19 -0
  384. package/package.json +54 -48
  385. package/perf/baseline.d.ts +40 -0
  386. package/perf/baseline.js +92 -0
  387. package/perf/explain.d.ts +46 -0
  388. package/perf/explain.js +204 -0
  389. package/pg/acl.d.ts +72 -0
  390. package/pg/acl.js +191 -0
  391. package/pg/behaviors.d.ts +114 -0
  392. package/pg/behaviors.js +217 -0
  393. package/pg/exposure.d.ts +74 -0
  394. package/pg/exposure.js +205 -0
  395. package/pg/functions.d.ts +52 -0
  396. package/pg/functions.js +131 -0
  397. package/pg/indexes.d.ts +192 -0
  398. package/pg/indexes.js +400 -0
  399. package/pg/introspect.d.ts +134 -0
  400. package/pg/introspect.js +280 -0
  401. package/pg/objects.d.ts +37 -0
  402. package/pg/objects.js +111 -0
  403. package/pg/paths.d.ts +114 -0
  404. package/pg/paths.js +166 -0
  405. package/pg/proc.d.ts +23 -0
  406. package/pg/proc.js +58 -0
  407. package/pg/reach-inputs.d.ts +47 -0
  408. package/pg/reach-inputs.js +136 -0
  409. package/pg/roles.d.ts +28 -0
  410. package/pg/roles.js +54 -0
  411. package/pg/stats.d.ts +65 -0
  412. package/pg/stats.js +133 -0
  413. package/pgpm-test.d.ts +22 -0
  414. package/pgpm-test.js +42 -0
  415. package/report/callgraph.d.ts +9 -0
  416. package/report/callgraph.js +81 -0
  417. package/report/compare.d.ts +143 -0
  418. package/report/compare.js +205 -0
  419. package/report/github.d.ts +74 -0
  420. package/report/github.js +333 -0
  421. package/report/json.d.ts +4 -0
  422. package/report/json.js +6 -0
  423. package/report/markdown.d.ts +25 -0
  424. package/report/markdown.js +355 -0
  425. package/report/pretty.d.ts +14 -0
  426. package/report/pretty.js +324 -0
  427. package/report/sarif.d.ts +40 -0
  428. package/report/sarif.js +193 -0
  429. package/report/view.d.ts +82 -0
  430. package/report/view.js +132 -0
  431. package/rules/registry.d.ts +66 -0
  432. package/rules/registry.js +569 -0
  433. package/schema/safegres.schema.json +1119 -0
  434. package/score/score.d.ts +84 -0
  435. package/score/score.js +238 -0
  436. package/score/scorecards.d.ts +68 -0
  437. package/score/scorecards.js +162 -0
  438. package/types.d.ts +292 -0
  439. package/types.js +32 -0
  440. package/version.d.ts +8 -0
  441. package/version.js +12 -0
  442. package/LICENSE +0 -23
  443. package/admin.d.ts +0 -26
  444. package/admin.js +0 -182
  445. package/connect.d.ts +0 -19
  446. package/connect.js +0 -83
  447. package/dist/README.md +0 -557
  448. package/dist/package.json +0 -72
  449. package/esm/admin.js +0 -178
  450. package/esm/connect.js +0 -78
  451. package/esm/manager.js +0 -136
  452. package/esm/roles.js +0 -32
  453. package/esm/seed/adapters.js +0 -23
  454. package/esm/seed/csv.js +0 -44
  455. package/esm/seed/index.js +0 -16
  456. package/esm/seed/json.js +0 -18
  457. package/esm/seed/launchql.js +0 -19
  458. package/esm/seed/sqitch.js +0 -17
  459. package/esm/stream.js +0 -43
  460. package/esm/test-client.js +0 -150
  461. package/manager.d.ts +0 -25
  462. package/manager.js +0 -140
  463. package/roles.d.ts +0 -17
  464. package/roles.js +0 -38
  465. package/seed/adapters.d.ts +0 -4
  466. package/seed/adapters.js +0 -28
  467. package/seed/csv.d.ts +0 -9
  468. package/seed/csv.js +0 -49
  469. package/seed/index.d.ts +0 -16
  470. package/seed/index.js +0 -33
  471. package/seed/json.d.ts +0 -6
  472. package/seed/json.js +0 -21
  473. package/seed/launchql.d.ts +0 -2
  474. package/seed/launchql.js +0 -22
  475. package/seed/sqitch.d.ts +0 -2
  476. package/seed/sqitch.js +0 -20
  477. package/seed/types.d.ts +0 -13
  478. package/stream.d.ts +0 -2
  479. package/stream.js +0 -46
  480. package/test-client.d.ts +0 -49
  481. package/test-client.js +0 -154
  482. /package/{seed → config}/types.js +0 -0
  483. /package/esm/{seed → config}/types.js +0 -0
@@ -0,0 +1,84 @@
1
+ import type { Grade, ScoringConfig } from '../config/types';
2
+ import type { Finding, Severity } from '../types';
3
+ export interface ScoreDeduction {
4
+ code: string;
5
+ count: number;
6
+ points: number;
7
+ /**
8
+ * What the audit would score if this rule were its only problem — the same
9
+ * curve applied to this rule's points alone. Grades the rule rather than
10
+ * the database.
11
+ */
12
+ score: number;
13
+ grade: Grade;
14
+ /**
15
+ * Score the audit would *gain* by taking this rule to zero. Not
16
+ * interchangeable with `score`: because the curve is exponential a rule's
17
+ * payoff depends on how much other debt exists, so the same fix is worth
18
+ * more once the rest is clean.
19
+ */
20
+ potential: number;
21
+ /**
22
+ * The rule contributes no points by construction — every finding is
23
+ * `info`-weighted or fail-closed. Reported so a run cannot imply that
24
+ * fixing them would move the score; it cannot.
25
+ */
26
+ unscored?: boolean;
27
+ /**
28
+ * The rule's raw points exceeded `maxRuleDensity` and were cut to it. The
29
+ * finding count is unchanged: what is capped is the rule's influence on the
30
+ * score, not the size of the problem it reports.
31
+ */
32
+ capped?: boolean;
33
+ /**
34
+ * Distinct units of repair behind `count`, when a rule declares one
35
+ * (`RuleMeta.dedupBy`). Points are charged per unit, so a rule that emits
36
+ * one finding per (relation × function) pair costs what fixing it costs.
37
+ */
38
+ units?: number;
39
+ }
40
+ export interface Score {
41
+ /** 0-100. */
42
+ value: number;
43
+ grade: Grade;
44
+ /** Scoring model identifier ('density' or 'weighted'). */
45
+ model: string;
46
+ /**
47
+ * Every rule that produced a scorable finding, largest deduction first,
48
+ * with its own score, grade and payoff-to-fix. Rules that contribute no
49
+ * points (info-weighted, fail-closed) are listed last and flagged
50
+ * `unscored` rather than omitted — a report that silently drops them
51
+ * implies that fixing them would help, and it would not.
52
+ */
53
+ deductions: ScoreDeduction[];
54
+ /** Density model: risk points per exposed table. */
55
+ density?: number;
56
+ /** Density model: the exposed-table denominator. */
57
+ exposedTables?: number;
58
+ /** True when the score was capped because no exposure surface was known. */
59
+ cappedByUnknownExposure?: boolean;
60
+ }
61
+ /** Context the audit supplies for exposure-aware scoring. */
62
+ export interface ScoreContext {
63
+ /** Number of tables on the exposed surface (the density denominator). */
64
+ exposedTables?: number;
65
+ /** Whether an exposure surface was configured/resolved. */
66
+ exposureKnown?: boolean;
67
+ /**
68
+ * The caller has already decided which findings count (a scorecard's
69
+ * selector). Skips the built-in exposure/acknowledgement/meta filtering,
70
+ * which would otherwise silently overrule a selector that asked for
71
+ * unexposed findings.
72
+ */
73
+ prefiltered?: boolean;
74
+ }
75
+ export declare const DEFAULT_WEIGHTS: Record<Severity, number>;
76
+ export declare const DEFAULT_GRADE_BANDS: Record<Exclude<Grade, 'F'>, number>;
77
+ /**
78
+ * Compute the audit score. The score is config-driven by design: disabling
79
+ * or retuning rules changes the findings and therefore the score, and only
80
+ * findings on the exposed surface count (see `Finding.exposed`).
81
+ */
82
+ export declare function computeScore(findings: Finding[], config?: ScoringConfig, context?: ScoreContext): Score;
83
+ /** True when `grade` is at least as good as `minimum`. */
84
+ export declare function meetsGrade(grade: Grade, minimum: Grade): boolean;
package/score/score.js ADDED
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_GRADE_BANDS = exports.DEFAULT_WEIGHTS = void 0;
4
+ exports.computeScore = computeScore;
5
+ exports.meetsGrade = meetsGrade;
6
+ const registry_1 = require("../rules/registry");
7
+ const types_1 = require("../types");
8
+ exports.DEFAULT_WEIGHTS = {
9
+ critical: 25,
10
+ high: 10,
11
+ medium: 4,
12
+ low: 1,
13
+ info: 0
14
+ };
15
+ exports.DEFAULT_GRADE_BANDS = {
16
+ 'A+': 97,
17
+ A: 90,
18
+ B: 80,
19
+ C: 65,
20
+ D: 50
21
+ };
22
+ const DEFAULT_MAX_DEDUCTION_PER_RULE = 40;
23
+ const DEFAULT_MAX_RULE_DENSITY = 0.5;
24
+ const DEFAULT_DENSITY_K = 0.17;
25
+ const DEFAULT_UNKNOWN_EXPOSURE_CAP = 80;
26
+ const DEFAULT_FAIL_CLOSED_WEIGHT = 0;
27
+ /**
28
+ * Compute the audit score. The score is config-driven by design: disabling
29
+ * or retuning rules changes the findings and therefore the score, and only
30
+ * findings on the exposed surface count (see `Finding.exposed`).
31
+ */
32
+ function computeScore(findings, config = {}, context = {}) {
33
+ const model = config.model ?? 'density';
34
+ return model === 'weighted'
35
+ ? computeWeightedScore(findings, config, context)
36
+ : computeDensityScore(findings, config, context);
37
+ }
38
+ /**
39
+ * Density scoring: severity-weighted *scorable* findings per exposed table
40
+ * with exponential falloff — does not saturate on large schemas.
41
+ *
42
+ * score = 100 · exp(−k · riskPoints / exposedTables)
43
+ *
44
+ * Scorable findings are those on the exposed surface (`exposed !== false`)
45
+ * that are not fail-closed (fail-closed weights are multiplied by
46
+ * `failClosedWeight`, default 0 — denied-at-runtime hygiene doesn't reduce
47
+ * the score). With the default k, one critical per ~10 exposed tables lands
48
+ * around a C; one critical per table is an F.
49
+ *
50
+ * Two things keep a rule's *shape* out of the arithmetic. Points are charged
51
+ * per unit of repair, not per finding, for rules that declare one — a rule
52
+ * emitting a row per (relation × function) pair costs what fixing it costs.
53
+ * And each rule's total is capped at a fraction of the points that would fail
54
+ * an audit alone (`maxRuleDensity`), so no single rule can decide the grade.
55
+ */
56
+ function computeDensityScore(findings, config, context) {
57
+ const weights = { ...exports.DEFAULT_WEIGHTS, ...(config.weights ?? {}) };
58
+ const perRule = config.perRuleWeights ?? {};
59
+ const bands = { ...exports.DEFAULT_GRADE_BANDS, ...(config.gradeBands ?? {}) };
60
+ const floor = config.floorOnCritical === undefined ? 'C' : config.floorOnCritical;
61
+ const k = config.densityK ?? DEFAULT_DENSITY_K;
62
+ const failClosedWeight = config.failClosedWeight ?? DEFAULT_FAIL_CLOSED_WEIGHT;
63
+ // Meta findings (e.g. W1) are advisories about the audit itself — the
64
+ // unknown-exposure cap is their penalty, not weighted points.
65
+ const scorable = context.prefiltered
66
+ ? findings
67
+ : findings.filter((f) => f.exposed !== false && !f.acknowledged && f.category !== 'meta');
68
+ const exposedTables = Math.max(1, context.exposedTables ?? 1);
69
+ const byRule = new Map();
70
+ for (const f of scorable) {
71
+ let weight = perRule[f.code] ?? weights[f.severity] ?? 0;
72
+ if (f.direction === 'fail-closed')
73
+ weight *= failClosedWeight;
74
+ const entry = byRule.get(f.code) ?? { count: 0, points: 0, units: new Set() };
75
+ entry.count += 1;
76
+ // A rule with a declared unit of repair is charged once per unit: the
77
+ // second finding against the same function is the same fix, and letting
78
+ // it charge again grades fan-out rather than risk.
79
+ const unit = repairUnit(f);
80
+ if (unit === undefined || !entry.units.has(unit)) {
81
+ entry.points += Math.max(0, weight);
82
+ if (unit !== undefined)
83
+ entry.units.add(unit);
84
+ }
85
+ byRule.set(f.code, entry);
86
+ }
87
+ // No single rule may contribute more than `maxRuleDensity` of the points
88
+ // that would take the score to F alone — at the default half, one rule can
89
+ // cost two grade bands but an F still takes breadth. Solved from the curve
90
+ // rather than fixed, so retuning `k` or the bands moves the cap with them.
91
+ const maxRuleDensity = config.maxRuleDensity ?? DEFAULT_MAX_RULE_DENSITY;
92
+ const pointsToF = (Math.log(100 / (bands.D || 50)) / k) * exposedTables;
93
+ const ruleCap = maxRuleDensity === false ? Infinity : maxRuleDensity * pointsToF;
94
+ const capped = new Map();
95
+ for (const [code, tally] of byRule) {
96
+ capped.set(code, {
97
+ count: tally.count,
98
+ points: Math.min(tally.points, ruleCap),
99
+ raw: tally.points,
100
+ ...(tally.units.size > 0 ? { units: tally.units.size } : {})
101
+ });
102
+ }
103
+ const riskPoints = [...capped.values()].reduce((sum, r) => sum + r.points, 0);
104
+ const density = riskPoints / exposedTables;
105
+ const curve = (points) => round1(100 * Math.exp((-k * points) / exposedTables));
106
+ let value = round1(100 * Math.exp(-k * density));
107
+ const deductions = [...capped.entries()]
108
+ .map(([code, { count, points, raw, units }]) => ({
109
+ code,
110
+ count,
111
+ points,
112
+ score: curve(points),
113
+ grade: gradeFor(curve(points), bands),
114
+ potential: round1(curve(riskPoints - points) - value),
115
+ ...(units !== undefined && units !== count ? { units } : {}),
116
+ ...(raw > points ? { capped: true } : {}),
117
+ ...(points === 0 ? { unscored: true } : {})
118
+ }))
119
+ .sort(byPayoff);
120
+ const unknownCap = config.unknownExposureCap ?? DEFAULT_UNKNOWN_EXPOSURE_CAP;
121
+ const cappedByUnknown = context.exposureKnown === false && unknownCap !== false && value > unknownCap;
122
+ if (cappedByUnknown)
123
+ value = unknownCap;
124
+ let grade = gradeFor(value, bands);
125
+ if (floor
126
+ && scorable.some((f) => f.direction !== 'fail-closed' && (0, types_1.meetsThreshold)(f.severity, 'critical'))) {
127
+ grade = worseOf(grade, floor);
128
+ }
129
+ return {
130
+ value,
131
+ grade,
132
+ model: 'density',
133
+ deductions,
134
+ density: Math.round(density * 1000) / 1000,
135
+ exposedTables,
136
+ ...(cappedByUnknown ? { cappedByUnknownExposure: true } : {})
137
+ };
138
+ }
139
+ /**
140
+ * The finding's unit of repair, from the rule's declared `dedupBy` path.
141
+ * `undefined` when the rule declares none — one finding, one unit.
142
+ */
143
+ function repairUnit(finding) {
144
+ const path = registry_1.RULES_BY_CODE.get(finding.code)?.dedupBy;
145
+ if (!path)
146
+ return undefined;
147
+ let value = finding;
148
+ for (const key of path.split('.')) {
149
+ if (value === null || typeof value !== 'object')
150
+ return undefined;
151
+ value = value[key];
152
+ }
153
+ return typeof value === 'string' ? value : undefined;
154
+ }
155
+ /**
156
+ * Legacy weighted-deduction scoring (Lighthouse-style): each finding deducts
157
+ * points by severity (or per-rule override), capped per rule so one noisy
158
+ * rule can't zero the score alone. Any critical finding floors the grade
159
+ * (default 'C'). Saturates on large schemas — prefer the density model.
160
+ */
161
+ function computeWeightedScore(findings, config, context) {
162
+ const weights = { ...exports.DEFAULT_WEIGHTS, ...(config.weights ?? {}) };
163
+ const perRule = config.perRuleWeights ?? {};
164
+ const cap = config.maxDeductionPerRule ?? DEFAULT_MAX_DEDUCTION_PER_RULE;
165
+ const bands = { ...exports.DEFAULT_GRADE_BANDS, ...(config.gradeBands ?? {}) };
166
+ const floor = config.floorOnCritical === undefined ? 'C' : config.floorOnCritical;
167
+ const scorable = context.prefiltered
168
+ ? findings
169
+ : findings.filter((f) => f.exposed !== false && !f.acknowledged && f.category !== 'meta');
170
+ const byRule = new Map();
171
+ for (const f of scorable) {
172
+ const weight = perRule[f.code] ?? weights[f.severity] ?? 0;
173
+ const entry = byRule.get(f.code) ?? { count: 0, points: 0 };
174
+ entry.count += 1;
175
+ entry.points += weight;
176
+ byRule.set(f.code, entry);
177
+ }
178
+ const byRuleCapped = [...byRule.entries()].map(([code, { count, points }]) => [code, { count, points: Math.min(points, cap) }]);
179
+ const total = byRuleCapped.reduce((sum, [, r]) => sum + r.points, 0);
180
+ let value = Math.max(0, round1(100 - total));
181
+ const remainder = (points) => Math.max(0, round1(100 - (total - points)));
182
+ const deductions = byRuleCapped
183
+ .map(([code, { count, points }]) => ({
184
+ code,
185
+ count,
186
+ points,
187
+ score: Math.max(0, round1(100 - points)),
188
+ grade: gradeFor(Math.max(0, round1(100 - points)), bands),
189
+ potential: round1(remainder(points) - value),
190
+ ...(points === 0 ? { unscored: true } : {})
191
+ }))
192
+ .sort(byPayoff);
193
+ const unknownCap = config.unknownExposureCap ?? DEFAULT_UNKNOWN_EXPOSURE_CAP;
194
+ const capped = context.exposureKnown === false && unknownCap !== false && value > unknownCap;
195
+ if (capped)
196
+ value = unknownCap;
197
+ let grade = gradeFor(value, bands);
198
+ if (floor && scorable.some((f) => (0, types_1.meetsThreshold)(f.severity, 'critical'))) {
199
+ grade = worseOf(grade, floor);
200
+ }
201
+ return {
202
+ value,
203
+ grade,
204
+ model: 'weighted',
205
+ deductions,
206
+ ...(capped ? { cappedByUnknownExposure: true } : {})
207
+ };
208
+ }
209
+ function round1(n) {
210
+ return Math.round(n * 10) / 10;
211
+ }
212
+ /**
213
+ * Scored rules first, largest deduction at the top; unscored rules last, in
214
+ * code order. Points and payoff rank identically under a monotone curve, so
215
+ * this keeps the existing "top deductions" output stable.
216
+ */
217
+ function byPayoff(a, b) {
218
+ if (!!a.unscored !== !!b.unscored)
219
+ return a.unscored ? 1 : -1;
220
+ return b.points - a.points || a.code.localeCompare(b.code);
221
+ }
222
+ const GRADE_ORDER = ['A+', 'A', 'B', 'C', 'D', 'F'];
223
+ function gradeFor(value, bands) {
224
+ for (const grade of GRADE_ORDER) {
225
+ if (grade === 'F')
226
+ break;
227
+ if (value >= bands[grade])
228
+ return grade;
229
+ }
230
+ return 'F';
231
+ }
232
+ function worseOf(a, b) {
233
+ return GRADE_ORDER.indexOf(a) >= GRADE_ORDER.indexOf(b) ? a : b;
234
+ }
235
+ /** True when `grade` is at least as good as `minimum`. */
236
+ function meetsGrade(grade, minimum) {
237
+ return GRADE_ORDER.indexOf(grade) <= GRADE_ORDER.indexOf(minimum);
238
+ }
@@ -0,0 +1,68 @@
1
+ import type { ScorecardConfig, ScorecardSelector } from '../config/types';
2
+ import { type Finding, type Summary } from '../types';
3
+ import { type Score, type ScoreContext } from './score';
4
+ /**
5
+ * One named score over a selected slice of the findings.
6
+ *
7
+ * The headline score answers one question well and every other question
8
+ * badly: a platform team gating on what `anonymous` reaches and an
9
+ * application team gating on house style are not arguing about the same
10
+ * number, and forcing them to is how a single number becomes something
11
+ * nobody trusts. A scorecard is that question, written down — a selector
12
+ * over the findings plus the weighting to grade them with.
13
+ */
14
+ export interface ScorecardReport {
15
+ /** Config key, e.g. `anon-surface`. */
16
+ name: string;
17
+ /** Human-readable heading for reports. */
18
+ title?: string;
19
+ /**
20
+ * Why this scorecard exists — what decision it informs. Rendered with the
21
+ * score, because a number whose question is unstated invites the reader to
22
+ * supply their own.
23
+ */
24
+ description?: string;
25
+ /** The findings it selected, graded by its own weighting. */
26
+ score: Score;
27
+ /** How many findings the selector matched. */
28
+ findings: number;
29
+ /** Severity counts over the selected findings. */
30
+ summary: Summary;
31
+ /** The resolved selector, so a report explains its own arithmetic. */
32
+ select: ScorecardSelector;
33
+ /**
34
+ * Reserved scorecards safegres computes itself: `default` is the headline
35
+ * (`report.score`, verbatim) and `raw` is every finding at its declared
36
+ * severity with no exposure filter. Neither can be redefined into
37
+ * something flattering.
38
+ */
39
+ reserved?: boolean;
40
+ }
41
+ export interface ScorecardContext extends ScoreContext {
42
+ /** Relations on the exposed surface — the default density denominator. */
43
+ exposedTables?: number;
44
+ /** Every relation in the audited schemas — the `all` denominator. */
45
+ totalTables?: number;
46
+ }
47
+ /**
48
+ * The two scorecards every run carries, whatever the config says.
49
+ *
50
+ * `raw` is the honest one: no exposure filter, no acknowledgements, no
51
+ * preset severity retuning, fail-closed findings weighted like anything
52
+ * else. It is not flattering and it is not meant to be — it is the number
53
+ * that is comparable between two databases, and the one a preset cannot
54
+ * talk down.
55
+ */
56
+ export declare const RESERVED_SCORECARDS: Record<string, ScorecardConfig>;
57
+ /**
58
+ * Grade each configured scorecard. `default` is not recomputed — it is the
59
+ * headline score passed in, so a scorecard block can never move the number
60
+ * on the badge by redefining what `default` means.
61
+ */
62
+ export declare function computeScorecards(findings: Finding[], headline: Score, cards: Record<string, ScorecardConfig>, context: ScorecardContext): ScorecardReport[];
63
+ /**
64
+ * The findings a scorecard grades. Every clause is a narrowing filter, so an
65
+ * empty selector selects what the headline does: exposed, non-acknowledged,
66
+ * security findings.
67
+ */
68
+ export declare function selectFindings(findings: Finding[], select: ScorecardSelector): Finding[];
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RESERVED_SCORECARDS = void 0;
4
+ exports.computeScorecards = computeScorecards;
5
+ exports.selectFindings = selectFindings;
6
+ const registry_1 = require("../rules/registry");
7
+ const types_1 = require("../types");
8
+ const score_1 = require("./score");
9
+ /**
10
+ * The two scorecards every run carries, whatever the config says.
11
+ *
12
+ * `raw` is the honest one: no exposure filter, no acknowledgements, no
13
+ * preset severity retuning, fail-closed findings weighted like anything
14
+ * else. It is not flattering and it is not meant to be — it is the number
15
+ * that is comparable between two databases, and the one a preset cannot
16
+ * talk down.
17
+ */
18
+ exports.RESERVED_SCORECARDS = {
19
+ default: {
20
+ title: 'Safegres score',
21
+ description: 'The headline: exposed, fail-open findings at their configured severities.'
22
+ },
23
+ raw: {
24
+ title: 'Raw score',
25
+ description: 'Every finding at its declared severity, exposure ignored — comparable across databases.',
26
+ select: {
27
+ severities: 'declared',
28
+ exposure: 'all',
29
+ acknowledged: 'include',
30
+ denominator: 'all'
31
+ },
32
+ failClosedWeight: 1,
33
+ maxRuleDensity: false
34
+ }
35
+ };
36
+ /**
37
+ * Grade each configured scorecard. `default` is not recomputed — it is the
38
+ * headline score passed in, so a scorecard block can never move the number
39
+ * on the badge by redefining what `default` means.
40
+ */
41
+ function computeScorecards(findings, headline, cards, context) {
42
+ const out = [];
43
+ for (const [name, card] of Object.entries(cards)) {
44
+ const reserved = name in exports.RESERVED_SCORECARDS;
45
+ const select = { ...(exports.RESERVED_SCORECARDS[name]?.select ?? {}), ...(card.select ?? {}) };
46
+ if (name === 'default') {
47
+ out.push({
48
+ name,
49
+ ...titleOf(name, card),
50
+ score: headline,
51
+ findings: findings.filter((f) => f.exposed !== false && !f.acknowledged).length,
52
+ summary: (0, types_1.summarize)(findings.filter((f) => f.exposed !== false && !f.acknowledged)),
53
+ select,
54
+ reserved: true
55
+ });
56
+ continue;
57
+ }
58
+ const selected = selectFindings(findings, select);
59
+ const denominator = select.denominator === 'all'
60
+ ? (context.totalTables ?? context.exposedTables)
61
+ : context.exposedTables;
62
+ out.push({
63
+ name,
64
+ ...titleOf(name, card),
65
+ score: (0, score_1.computeScore)(selected, card, {
66
+ ...context,
67
+ exposedTables: denominator,
68
+ // The selector has already decided what counts; scoring must not
69
+ // filter again, or `exposure: 'all'` would be silently overruled.
70
+ prefiltered: true
71
+ }),
72
+ findings: selected.length,
73
+ summary: (0, types_1.summarize)(selected),
74
+ select,
75
+ ...(reserved ? { reserved: true } : {})
76
+ });
77
+ }
78
+ return out;
79
+ }
80
+ function titleOf(name, card) {
81
+ const base = exports.RESERVED_SCORECARDS[name] ?? {};
82
+ const title = card.title ?? base.title;
83
+ const description = card.description ?? base.description;
84
+ return { ...(title ? { title } : {}), ...(description ? { description } : {}) };
85
+ }
86
+ /**
87
+ * The findings a scorecard grades. Every clause is a narrowing filter, so an
88
+ * empty selector selects what the headline does: exposed, non-acknowledged,
89
+ * security findings.
90
+ */
91
+ function selectFindings(findings, select) {
92
+ const dimension = select.dimension ?? 'security';
93
+ const rules = select.rules?.map(matcher);
94
+ const exclude = select.exclude?.map(matcher);
95
+ const roles = select.roles ? new Set(select.roles) : undefined;
96
+ const planes = select.planes ? new Set(select.planes) : undefined;
97
+ const schemas = select.schemas ? new Set(select.schemas) : undefined;
98
+ const severity = select.minSeverity ? SEVERITY_RANK[select.minSeverity] : undefined;
99
+ const selected = findings.filter((f) => {
100
+ if (f.category === 'meta')
101
+ return false;
102
+ if (dimension !== 'all' && (f.dimension ?? 'security') !== dimension)
103
+ return false;
104
+ if (select.exposure !== 'all' && f.exposed === false)
105
+ return false;
106
+ if (select.acknowledged !== 'include' && f.acknowledged)
107
+ return false;
108
+ if (select.direction && select.direction !== 'any' && f.direction !== select.direction) {
109
+ return false;
110
+ }
111
+ if (rules && !rules.some((m) => m(f.code)))
112
+ return false;
113
+ if (exclude?.some((m) => m(f.code)))
114
+ return false;
115
+ if (schemas && (f.schema === undefined || !schemas.has(f.schema)))
116
+ return false;
117
+ if (roles && !touchesRole(f, roles))
118
+ return false;
119
+ if (planes && !f.planes?.some((p) => planes.has(p)))
120
+ return false;
121
+ if (severity !== undefined && SEVERITY_RANK[f.severity] < severity)
122
+ return false;
123
+ return true;
124
+ });
125
+ // Declared severities answer "how bad is this really", independent of what
126
+ // a preset chose to quiet down. Copies, never mutation: the findings in
127
+ // the report keep the severity the config resolved.
128
+ return select.severities === 'declared'
129
+ ? selected.map((f) => {
130
+ const declared = registry_1.RULES_BY_CODE.get(f.code)?.defaultSeverity;
131
+ return declared === undefined || declared === f.severity ? f : { ...f, severity: declared };
132
+ })
133
+ : selected;
134
+ }
135
+ const SEVERITY_RANK = {
136
+ info: 0,
137
+ low: 1,
138
+ medium: 2,
139
+ high: 3,
140
+ critical: 4
141
+ };
142
+ /**
143
+ * A role clause asks "is this finding about one of these roles" — which the
144
+ * finding answers in one of three places: the graded `role` column, a
145
+ * grantee list, or the reachability context a lattice rule attached.
146
+ */
147
+ function touchesRole(finding, roles) {
148
+ if (finding.role && roles.has(finding.role))
149
+ return true;
150
+ const context = finding.context;
151
+ if (context?.role && roles.has(context.role))
152
+ return true;
153
+ return Array.isArray(context?.roles)
154
+ && context.roles.some((r) => typeof r === 'string' && roles.has(r));
155
+ }
156
+ /** Rule code matcher: exact, or a `C*` prefix wildcard. */
157
+ function matcher(pattern) {
158
+ if (!pattern.endsWith('*'))
159
+ return (code) => code === pattern;
160
+ const prefix = pattern.slice(0, -1);
161
+ return (code) => code.startsWith(prefix);
162
+ }