@safegres/test 0.0.2 → 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,78 @@
1
+ /**
2
+ * API reach: which relations a generated API can actually address.
3
+ *
4
+ * A plane made of schemas answers "is this relation in the API's schemas?",
5
+ * which is as much precision as a schema list can carry. It over-counts in a
6
+ * way that matters: a generated API exposes types and fields, not schemas, and
7
+ * a schema routinely contains relations the API deliberately does not surface
8
+ * — join tables, denormalised shadows, machine-only back-pointers.
9
+ *
10
+ * Where the author has *declared* that, the declaration is better evidence
11
+ * than anything else safegres can collect. `reltuples` and `idx_scan` are
12
+ * measurements, and safegres grades ephemeral CI databases that have never
13
+ * held data, so both read zero at exactly the moment they would have to mean
14
+ * something. A behavior tag reads the same in CI as in production.
15
+ *
16
+ * Two properties keep this honest:
17
+ *
18
+ * - **Only an explicit denial counts.** Presets grant most behaviors by
19
+ * default, so the absence of `+list` says nothing whatsoever. Silence is
20
+ * never read as denial.
21
+ * - **Unreachable means unreachable by *every* route.** A relation the API
22
+ * cannot address at the root can still be addressed by traversing a relation
23
+ * field from one that can, so this is a graph reachability problem and not a
24
+ * per-table test. Subtracting a relation that is in fact addressable would
25
+ * silently drop real findings out of the score, which is the one failure
26
+ * mode worth designing against.
27
+ */
28
+ import type { BehaviorSnapshot } from '../pg/behaviors';
29
+ /**
30
+ * Abilities that between them cover every root entry point to a relation.
31
+ * All four must be denied before the relation is absent from the API root: a
32
+ * table that cannot be read but can still be inserted into is present.
33
+ */
34
+ export declare const ROOT_ABILITIES: string[];
35
+ /** The reverse (child-listing) field exists under any one of these. */
36
+ export declare const BACKWARD_ABILITIES: string[];
37
+ /** The forward (parent) field is a single record. */
38
+ export declare const FORWARD_ABILITIES: string[];
39
+ /** One foreign key, as reach cares about it. */
40
+ export interface ReachEdge {
41
+ /** Referencing relation, `schema.table`. */
42
+ from: string;
43
+ /** Referenced relation, `schema.table`. */
44
+ to: string;
45
+ constraint: string;
46
+ }
47
+ export interface ReachInputs {
48
+ /** Every relation in scope, `schema.table`. */
49
+ relations: string[];
50
+ edges: ReachEdge[];
51
+ behaviors: BehaviorSnapshot;
52
+ }
53
+ export interface UnreachableRelation {
54
+ schema: string;
55
+ table: string;
56
+ /** Why it is unreachable, in one human-readable clause. */
57
+ reason: string;
58
+ }
59
+ export interface ApiReach {
60
+ /** Relations no field of the generated API can address. */
61
+ unreachable: UnreachableRelation[];
62
+ /**
63
+ * `schema.table.constraint` for every foreign key whose reverse relation is
64
+ * declared absent. A hidden reverse relation is not the same claim as an
65
+ * unreachable table — it is one path, not all of them — so it is reported
66
+ * separately and consumed by the X1 access-path signals.
67
+ */
68
+ hiddenBackwardRelations: string[];
69
+ }
70
+ /**
71
+ * Compute what the generated API can address.
72
+ *
73
+ * The traversal starts from every relation the API roots — anything not
74
+ * explicitly denied all of {@link ROOT_ABILITIES} — and walks relation fields
75
+ * in both directions. What it never visits is a relation that is denied at the
76
+ * root *and* has no surviving field pointing at it from anywhere reachable.
77
+ */
78
+ export declare function computeApiReach(inputs: ReachInputs): ApiReach;
@@ -0,0 +1,105 @@
1
+ /**
2
+ * API reach: which relations a generated API can actually address.
3
+ *
4
+ * A plane made of schemas answers "is this relation in the API's schemas?",
5
+ * which is as much precision as a schema list can carry. It over-counts in a
6
+ * way that matters: a generated API exposes types and fields, not schemas, and
7
+ * a schema routinely contains relations the API deliberately does not surface
8
+ * — join tables, denormalised shadows, machine-only back-pointers.
9
+ *
10
+ * Where the author has *declared* that, the declaration is better evidence
11
+ * than anything else safegres can collect. `reltuples` and `idx_scan` are
12
+ * measurements, and safegres grades ephemeral CI databases that have never
13
+ * held data, so both read zero at exactly the moment they would have to mean
14
+ * something. A behavior tag reads the same in CI as in production.
15
+ *
16
+ * Two properties keep this honest:
17
+ *
18
+ * - **Only an explicit denial counts.** Presets grant most behaviors by
19
+ * default, so the absence of `+list` says nothing whatsoever. Silence is
20
+ * never read as denial.
21
+ * - **Unreachable means unreachable by *every* route.** A relation the API
22
+ * cannot address at the root can still be addressed by traversing a relation
23
+ * field from one that can, so this is a graph reachability problem and not a
24
+ * per-table test. Subtracting a relation that is in fact addressable would
25
+ * silently drop real findings out of the score, which is the one failure
26
+ * mode worth designing against.
27
+ */
28
+ import { deniesAll, directionalBehavior } from '../pg/behaviors';
29
+ /**
30
+ * Abilities that between them cover every root entry point to a relation.
31
+ * All four must be denied before the relation is absent from the API root: a
32
+ * table that cannot be read but can still be inserted into is present.
33
+ */
34
+ export const ROOT_ABILITIES = ['select', 'insert', 'update', 'delete'];
35
+ /** The reverse (child-listing) field exists under any one of these. */
36
+ export const BACKWARD_ABILITIES = ['list', 'connection', 'single'];
37
+ /** The forward (parent) field is a single record. */
38
+ export const FORWARD_ABILITIES = ['single'];
39
+ function splitRelation(relation) {
40
+ const dot = relation.indexOf('.');
41
+ return { schema: relation.slice(0, dot), table: relation.slice(dot + 1) };
42
+ }
43
+ /**
44
+ * Compute what the generated API can address.
45
+ *
46
+ * The traversal starts from every relation the API roots — anything not
47
+ * explicitly denied all of {@link ROOT_ABILITIES} — and walks relation fields
48
+ * in both directions. What it never visits is a relation that is denied at the
49
+ * root *and* has no surviving field pointing at it from anywhere reachable.
50
+ */
51
+ export function computeApiReach(inputs) {
52
+ const { relations, edges, behaviors } = inputs;
53
+ const inScope = new Set(relations);
54
+ const rooted = relations.filter((relation) => !deniesAll(behaviors.tables.get(relation), ROOT_ABILITIES));
55
+ const hiddenBackwardRelations = [];
56
+ const adjacency = new Map();
57
+ const addEdge = (from, to) => {
58
+ const list = adjacency.get(from);
59
+ if (list)
60
+ list.push(to);
61
+ else
62
+ adjacency.set(from, [to]);
63
+ };
64
+ for (const edge of edges) {
65
+ const key = `${edge.from}.${edge.constraint}`;
66
+ const directions = behaviors.constraintDirections.get(key);
67
+ const backwardHidden = deniesAll(directionalBehavior(directions, 'backward'), BACKWARD_ABILITIES);
68
+ if (backwardHidden)
69
+ hiddenBackwardRelations.push(key);
70
+ // The reverse field lives on the referenced table and reaches the
71
+ // referencing one.
72
+ if (!backwardHidden && inScope.has(edge.to))
73
+ addEdge(edge.to, edge.from);
74
+ const forwardHidden = deniesAll(directionalBehavior(directions, 'forward'), FORWARD_ABILITIES);
75
+ if (!forwardHidden && inScope.has(edge.from))
76
+ addEdge(edge.from, edge.to);
77
+ }
78
+ const reached = new Set();
79
+ const queue = [...rooted];
80
+ for (const relation of rooted)
81
+ reached.add(relation);
82
+ while (queue.length > 0) {
83
+ const current = queue.pop();
84
+ for (const next of adjacency.get(current) ?? []) {
85
+ if (!inScope.has(next) || reached.has(next))
86
+ continue;
87
+ reached.add(next);
88
+ queue.push(next);
89
+ }
90
+ }
91
+ const unreachable = [];
92
+ for (const relation of relations) {
93
+ if (reached.has(relation))
94
+ continue;
95
+ const inbound = edges.filter((e) => e.to === relation || e.from === relation).length;
96
+ unreachable.push({
97
+ ...splitRelation(relation),
98
+ reason: `a behavior denies ${ROOT_ABILITIES.join(', ')} on the relation`
99
+ + (inbound > 0
100
+ ? ', and every relation field that could reach it is denied too'
101
+ : ', and no relation field points at it')
102
+ });
103
+ }
104
+ return { unreachable, hiddenBackwardRelations: hiddenBackwardRelations.sort() };
105
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Routine exposure: which functions an untrusted role can actually call.
3
+ *
4
+ * The exposure surface answers "which relations can the API address?", and
5
+ * every finding was graded by that question — including the ones that are not
6
+ * about a relation at all. A definer function lives in a private schema, so by
7
+ * relation reach it is unexposed; but `anonymous` holds `EXECUTE` on it, so it
8
+ * is one call away from whatever its owner can touch. Grading it by its schema
9
+ * is not conservative, it is wrong in the dangerous direction.
10
+ *
11
+ * The question a routine finding is graded by is therefore the callability
12
+ * one: can an untrusted role reach the function itself — `USAGE` on its
13
+ * schema, and `EXECUTE` on the function, directly, by inheritance, or through
14
+ * Postgres's default `EXECUTE TO PUBLIC`.
15
+ */
16
+ import type { RoleGraph } from '../checks/lattice';
17
+ import { type SchemaAclInfo } from '../pg/acl';
18
+ import type { FunctionSnapshot } from '../pg/functions';
19
+ export interface RoutineReachOptions {
20
+ /** Roles a request can arrive as — the API edge, plus its anonymous roles. */
21
+ roles: string[];
22
+ graph: RoleGraph;
23
+ schemaAcls: Map<string, SchemaAclInfo>;
24
+ /**
25
+ * Schemas on the API surface. A trigger function is not directly callable
26
+ * whatever its ACL says, so it is judged the way its firing is: reachable
27
+ * when a write that fires it is.
28
+ */
29
+ exposedSchemas: Set<string>;
30
+ }
31
+ /**
32
+ * Every `schema.name` an untrusted role can execute. Overloads collapse: the
33
+ * finding names a function, not a signature, and a role that can call one
34
+ * overload can reach the body the rule is talking about.
35
+ */
36
+ export declare function resolveRoutineReach(functions: FunctionSnapshot[], options: RoutineReachOptions): Set<string>;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Routine exposure: which functions an untrusted role can actually call.
3
+ *
4
+ * The exposure surface answers "which relations can the API address?", and
5
+ * every finding was graded by that question — including the ones that are not
6
+ * about a relation at all. A definer function lives in a private schema, so by
7
+ * relation reach it is unexposed; but `anonymous` holds `EXECUTE` on it, so it
8
+ * is one call away from whatever its owner can touch. Grading it by its schema
9
+ * is not conservative, it is wrong in the dangerous direction.
10
+ *
11
+ * The question a routine finding is graded by is therefore the callability
12
+ * one: can an untrusted role reach the function itself — `USAGE` on its
13
+ * schema, and `EXECUTE` on the function, directly, by inheritance, or through
14
+ * Postgres's default `EXECUTE TO PUBLIC`.
15
+ */
16
+ import { canEnterSchema } from '../pg/acl';
17
+ /**
18
+ * Every `schema.name` an untrusted role can execute. Overloads collapse: the
19
+ * finding names a function, not a signature, and a role that can call one
20
+ * overload can reach the body the rule is talking about.
21
+ */
22
+ export function resolveRoutineReach(functions, options) {
23
+ const reachable = new Set();
24
+ if (options.roles.length === 0)
25
+ return reachable;
26
+ for (const fn of functions) {
27
+ const key = `${fn.schema}.${fn.name}`;
28
+ if (reachable.has(key))
29
+ continue;
30
+ if (fn.returnsTrigger) {
31
+ // Postgres refuses a direct call however wide the ACL is. What reaches
32
+ // the body is a write to the table the trigger is attached to, so the
33
+ // API surface is the right question after all.
34
+ if (options.exposedSchemas.has(fn.schema))
35
+ reachable.add(key);
36
+ continue;
37
+ }
38
+ if (options.roles.some((role) => canExecute(fn, role, options)))
39
+ reachable.add(key);
40
+ }
41
+ return reachable;
42
+ }
43
+ function canExecute(fn, role, { graph, schemaAcls }) {
44
+ if (!canEnterSchema(fn.schema, role, graph, schemaAcls))
45
+ return false;
46
+ const attrs = graph.get(role);
47
+ if (attrs?.isSuper)
48
+ return true;
49
+ if (role === fn.owner)
50
+ return true;
51
+ return fn.grants.some((g) => g.role === 'PUBLIC'
52
+ || g.role === role
53
+ || (attrs?.inheritsFrom ?? []).includes(g.role));
54
+ }
package/esm/index.d.ts ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Public surface of `safegres` — a pure-PostgreSQL Row-Level-Security auditor.
3
+ *
4
+ * The auditor introspects pg_class / pg_policy / role grants and emits
5
+ * structured findings (A1–A7, P1, P5). It has no knowledge of any specific
6
+ * application schema or policy DSL.
7
+ */
8
+ export { parsePolicyExpression, type PgAstNode, type PolicyExpression, PolicyParseError } from './ast/parse';
9
+ export type { BaselineBoundary, CallGraphBaseline, CallGraphDiff } from './callgraph/baseline';
10
+ export { boundaryKey, diffCallGraph, parseBaseline, serializeBaseline, toBaseline } from './callgraph/baseline';
11
+ export type { CallGraphEdge, CallGraphNode, CallGraphOptions, CallGraphReport, ChecklistCode, ChecklistItem } from './callgraph/graph';
12
+ export { buildCallGraph } from './callgraph/graph';
13
+ export { checkUntrustedColumnGrants } from './checks/column-grants';
14
+ export type { FunctionBodyAnalysis } from './checks/definer-function';
15
+ export { analyzeFunctionBodies, checkDefinerFunctionReach, checkInsteadOfTriggerWrite, checkUnreadableFunctionReach } from './checks/definer-function';
16
+ export type { SuppressedView, ViewBodyAnalysis } from './checks/definer-view';
17
+ export { analyzeViewBodies, checkDefinerViewBypass, checkUnauditedViewReach, checkUnreadableViewReach } from './checks/definer-view';
18
+ export { checkMissingPrimaryKey, checkRedundantIndexes, checkUnindexedForeignKeys, checkUnindexedSearchColumns, checkUnindexedSortColumns } from './checks/indexes';
19
+ export type { EffectiveColumnGrant, EffectiveGrant, GrantVia, LatticeRoleOptions, RoleAccessEntry, RoleAccessRelation, RoleGraph } from './checks/lattice';
20
+ export { checkDeadPolicies, checkDeadSchemaUsage, checkIndirectCoverageGaps, checkUnreachableGrants, checkUntrustedIndirectAccess, computeRoleAccess, effectiveColumnGrants, effectiveGrants } from './checks/lattice';
21
+ export { checkUntrustedForeignTableGrants, checkUntrustedSequenceGrants } from './checks/object-acls';
22
+ export type { PolicyClause, PredicateColumn } from './checks/policy-index';
23
+ export { checkNonLeakproofPolicyFunctions, checkPolicyColumnCasts, checkUnhoistedPolicyFunctions, checkUnindexedPolicyColumns, collectPredicateColumns } from './checks/policy-index';
24
+ export type { RoleTrustOptions } from './checks/role-trust';
25
+ export { checkPublicGrants, checkUntrustedRolePolicies, checkUntrustedRoleWrites } from './checks/role-trust';
26
+ export type { StatsThresholds } from './checks/stats';
27
+ export { checkDeadTuples, checkSeqScanDominant, checkStats, checkTopStatements, checkUnusedIndexes, DEFAULT_STATS_THRESHOLDS } from './checks/stats';
28
+ export type { ViewExposureAnalysis } from './checks/view-exposure';
29
+ export { analyzeViewExposure, checkLeakyFilterView, checkMatviewSnapshot } from './checks/view-exposure';
30
+ export type { ViewWriteAnalysis } from './checks/view-writes';
31
+ export { analyzeViewWrites, checkDefinerViewWrite, checkUncheckedViewWrite, checkViewRuleBypass } from './checks/view-writes';
32
+ export type { BaselineCandidate, BaselineSelection, ChosenBaseline, RejectedCandidate, SelectBaselineOptions } from './ci/baseline';
33
+ export { CANDIDATE_LIMIT, MAX_BASELINE_AGE_MS, selectBaseline } from './ci/baseline';
34
+ export type { GithubBaselineOptions, GithubBaselineResult } from './ci/github';
35
+ export { selectGithubBaseline } from './ci/github';
36
+ export type { AuditOptions } from './commands/audit';
37
+ export { audit } from './commands/audit';
38
+ export type { DoctorCheck, DoctorOptions, DoctorReport, DoctorStatus } from './commands/doctor';
39
+ export { doctor } from './commands/doctor';
40
+ export type { EvalCaseResult, EvalOptions, EvalReport } from './commands/eval';
41
+ export { runEval } from './commands/eval';
42
+ export type { Provenance } from './config/fingerprint';
43
+ export { configFingerprint } from './config/fingerprint';
44
+ export type { LoadConfigParams } from './config/loader';
45
+ export { loadConfig, safegresConfigLoader } from './config/loader';
46
+ export { constructive, graphile, hasura, minimal, multiTenant, oltp, postgrest, PRESETS, recommended, strict, supabase } from './config/presets';
47
+ export type { ResolvedRule, ResolvedRules } from './config/resolve';
48
+ export { allAstRulesDisabled, applyRulesToFindings, ConfigValidationError, defaultRuleMap, matchTablePattern, resolveRules, ruleCodesForDimension, rulesForTable } from './config/resolve';
49
+ export { CONFIG_SHAPE, SCHEMA_ID, toJsonSchema } from './config/schema';
50
+ export type { CallGraphConfig, EvalConfig, ExposureConfig, FailOnConfig, GithubCommentConfig, GithubReportConfig, Grade, OutputsConfig, OverrideEntry, PerfConfig, PlaneConfig, PlaneFailOnConfig, PlaneKind, ReportConfig, RulesConfig, RuleSetting, SafegresConfig, ScoringConfig, SourceConfig } from './config/types';
51
+ export { validateConfigShape } from './config/validate';
52
+ export type { CaseResult, CorpusCase, ExpectedFinding } from './corpus';
53
+ export { corpusBootstrap, corpusDir, gradeCase, loadCase, loadCorpus } from './corpus';
54
+ export type { ExposureAdapter, PlaneInput, ReachContext } from './exposure/adapters';
55
+ export { BUILTIN_ADAPTERS, constructiveAdapter, definePlanes, graphileAdapter, hasuraAdapter, postgraphileAdapter, postgrestAdapter, resolveAdapters, supabaseAdapter } from './exposure/adapters';
56
+ export type { PlaneReach } from './exposure/planes';
57
+ export { onPlane, relationKey, resolvePlaneReach, scorePlane, stampPlanes } from './exposure/planes';
58
+ export type { ApiReach, ReachEdge, ReachInputs, UnreachableRelation } from './exposure/reach';
59
+ export { BACKWARD_ABILITIES, computeApiReach, FORWARD_ABILITIES, ROOT_ABILITIES } from './exposure/reach';
60
+ export type { BaselineFinding, PerfBaseline, PerfDiff } from './perf/baseline';
61
+ export { diffPerf, findingKey, parsePerfBaseline, serializePerfBaseline, subjectOf, toBaselineFinding, toPerfBaseline } from './perf/baseline';
62
+ export type { ExplainOptions, ExplainReport } from './perf/explain';
63
+ export { proveFindings } from './perf/explain';
64
+ export type { RoleAttributes, SchemaAclGrant, SchemaAclInfo, SchemaAclOptions } from './pg/acl';
65
+ export { introspectRoleGraph, introspectSchemaAcls } from './pg/acl';
66
+ export type { ResolvedExposure, ResolvedPlane } from './pg/exposure';
67
+ export { resolveConstructiveExposure, resolveExposure, resolvePlanes, resolveReach, UNKNOWN_EXPOSURE } from './pg/exposure';
68
+ export type { FunctionGrant, FunctionSnapshot, IntrospectFunctionOptions } from './pg/functions';
69
+ export { introspectFunctions } from './pg/functions';
70
+ export type { ColumnInfo, ForeignKeyInfo, IndexInfo, TableIndexSnapshot, ViewSnapshot } from './pg/indexes';
71
+ export { introspectIndexes, introspectViews } from './pg/indexes';
72
+ export { type ColumnGrantInfo, type GrantInfo, type IntrospectOptions, introspectTables, type PgPrivilege, type PolicyCmd, type PolicyInfo, type QueryExecutor, type TableSnapshot } from './pg/introspect';
73
+ export type { ObjectAclSnapshot, PgObjectKind } from './pg/objects';
74
+ export { introspectObjectAcls } from './pg/objects';
75
+ export { listAuditableRoles, resolveRoles } from './pg/roles';
76
+ export type { IndexUsage, StatementUsage, StatsSnapshot, TableUsage } from './pg/stats';
77
+ export { introspectStats } from './pg/stats';
78
+ export { renderCallGraph, renderCallGraphDiff } from './report/callgraph';
79
+ export type { BaselineProvenance, DimensionSnapshot, ReportComparison, ReportSnapshot, RuleDelta, ScoreDelta } from './report/compare';
80
+ export { baselineLabel, compareReports, describeBaseline, formatAge, formatDelta, parseSnapshot, serializeSnapshot, toSnapshot } from './report/compare';
81
+ export type { GithubRenderOptions } from './report/github';
82
+ export { COMMENT_MARKER, emitGithub, gradeBadge, postStickyComment, renderAnnotations, renderGithubComment, renderGithubSummary, scoreBadge } from './report/github';
83
+ export { renderJson } from './report/json';
84
+ export type { RenderMarkdownOptions } from './report/markdown';
85
+ export { renderMarkdown } from './report/markdown';
86
+ export type { RenderPrettyOptions } from './report/pretty';
87
+ export { renderPretty } from './report/pretty';
88
+ export type { BuildSourceIndexOptions, RenderSarifOptions, SourceIndex, SourceLocation } from './report/sarif';
89
+ export { buildSourceIndex, renderSarif } from './report/sarif';
90
+ export type { ReportView, ViewConfig, ViewDimension, ViewFindings, ViewScore, ViewSection } from './report/view';
91
+ export { ALL_SECTIONS, matchPlane, selectView, viewConfigFromReportConfig } from './report/view';
92
+ export type { RuleMeta } from './rules/registry';
93
+ export { dimensionOf, expandRuleSelector, isKnownRule, RULES, RULES_BY_CODE } from './rules/registry';
94
+ export type { Score, ScoreContext, ScoreDeduction } from './score/score';
95
+ export { computeScore, DEFAULT_GRADE_BANDS, DEFAULT_WEIGHTS, meetsGrade } from './score/score';
96
+ export * from './types';
package/esm/index.js CHANGED
@@ -1,6 +1,57 @@
1
- export * from './admin';
2
- export * from './connect';
3
- export * from './manager';
4
- export * from './roles';
5
- export * from './seed';
6
- export * from './test-client';
1
+ /**
2
+ * Public surface of `safegres` — a pure-PostgreSQL Row-Level-Security auditor.
3
+ *
4
+ * The auditor introspects pg_class / pg_policy / role grants and emits
5
+ * structured findings (A1–A7, P1, P5). It has no knowledge of any specific
6
+ * application schema or policy DSL.
7
+ */
8
+ export { parsePolicyExpression, PolicyParseError } from './ast/parse';
9
+ export { boundaryKey, diffCallGraph, parseBaseline, serializeBaseline, toBaseline } from './callgraph/baseline';
10
+ export { buildCallGraph } from './callgraph/graph';
11
+ export { checkUntrustedColumnGrants } from './checks/column-grants';
12
+ export { analyzeFunctionBodies, checkDefinerFunctionReach, checkInsteadOfTriggerWrite, checkUnreadableFunctionReach } from './checks/definer-function';
13
+ export { analyzeViewBodies, checkDefinerViewBypass, checkUnauditedViewReach, checkUnreadableViewReach } from './checks/definer-view';
14
+ export { checkMissingPrimaryKey, checkRedundantIndexes, checkUnindexedForeignKeys, checkUnindexedSearchColumns, checkUnindexedSortColumns } from './checks/indexes';
15
+ export { checkDeadPolicies, checkDeadSchemaUsage, checkIndirectCoverageGaps, checkUnreachableGrants, checkUntrustedIndirectAccess, computeRoleAccess, effectiveColumnGrants, effectiveGrants } from './checks/lattice';
16
+ export { checkUntrustedForeignTableGrants, checkUntrustedSequenceGrants } from './checks/object-acls';
17
+ export { checkNonLeakproofPolicyFunctions, checkPolicyColumnCasts, checkUnhoistedPolicyFunctions, checkUnindexedPolicyColumns, collectPredicateColumns } from './checks/policy-index';
18
+ export { checkPublicGrants, checkUntrustedRolePolicies, checkUntrustedRoleWrites } from './checks/role-trust';
19
+ export { checkDeadTuples, checkSeqScanDominant, checkStats, checkTopStatements, checkUnusedIndexes, DEFAULT_STATS_THRESHOLDS } from './checks/stats';
20
+ export { analyzeViewExposure, checkLeakyFilterView, checkMatviewSnapshot } from './checks/view-exposure';
21
+ export { analyzeViewWrites, checkDefinerViewWrite, checkUncheckedViewWrite, checkViewRuleBypass } from './checks/view-writes';
22
+ export { CANDIDATE_LIMIT, MAX_BASELINE_AGE_MS, selectBaseline } from './ci/baseline';
23
+ export { selectGithubBaseline } from './ci/github';
24
+ export { audit } from './commands/audit';
25
+ export { doctor } from './commands/doctor';
26
+ export { runEval } from './commands/eval';
27
+ export { configFingerprint } from './config/fingerprint';
28
+ export { loadConfig, safegresConfigLoader } from './config/loader';
29
+ export { constructive, graphile, hasura, minimal, multiTenant, oltp, postgrest, PRESETS, recommended, strict, supabase } from './config/presets';
30
+ export { allAstRulesDisabled, applyRulesToFindings, ConfigValidationError, defaultRuleMap, matchTablePattern, resolveRules, ruleCodesForDimension, rulesForTable } from './config/resolve';
31
+ export { CONFIG_SHAPE, SCHEMA_ID, toJsonSchema } from './config/schema';
32
+ export { validateConfigShape } from './config/validate';
33
+ export { corpusBootstrap, corpusDir, gradeCase, loadCase, loadCorpus } from './corpus';
34
+ export { BUILTIN_ADAPTERS, constructiveAdapter, definePlanes, graphileAdapter, hasuraAdapter, postgraphileAdapter, postgrestAdapter, resolveAdapters, supabaseAdapter } from './exposure/adapters';
35
+ export { onPlane, relationKey, resolvePlaneReach, scorePlane, stampPlanes } from './exposure/planes';
36
+ export { BACKWARD_ABILITIES, computeApiReach, FORWARD_ABILITIES, ROOT_ABILITIES } from './exposure/reach';
37
+ export { diffPerf, findingKey, parsePerfBaseline, serializePerfBaseline, subjectOf, toBaselineFinding, toPerfBaseline } from './perf/baseline';
38
+ export { proveFindings } from './perf/explain';
39
+ export { introspectRoleGraph, introspectSchemaAcls } from './pg/acl';
40
+ export { resolveConstructiveExposure, resolveExposure, resolvePlanes, resolveReach, UNKNOWN_EXPOSURE } from './pg/exposure';
41
+ export { introspectFunctions } from './pg/functions';
42
+ export { introspectIndexes, introspectViews } from './pg/indexes';
43
+ export { introspectTables } from './pg/introspect';
44
+ export { introspectObjectAcls } from './pg/objects';
45
+ export { listAuditableRoles, resolveRoles } from './pg/roles';
46
+ export { introspectStats } from './pg/stats';
47
+ export { renderCallGraph, renderCallGraphDiff } from './report/callgraph';
48
+ export { baselineLabel, compareReports, describeBaseline, formatAge, formatDelta, parseSnapshot, serializeSnapshot, toSnapshot } from './report/compare';
49
+ export { COMMENT_MARKER, emitGithub, gradeBadge, postStickyComment, renderAnnotations, renderGithubComment, renderGithubSummary, scoreBadge } from './report/github';
50
+ export { renderJson } from './report/json';
51
+ export { renderMarkdown } from './report/markdown';
52
+ export { renderPretty } from './report/pretty';
53
+ export { buildSourceIndex, renderSarif } from './report/sarif';
54
+ export { ALL_SECTIONS, matchPlane, selectView, viewConfigFromReportConfig } from './report/view';
55
+ export { dimensionOf, expandRuleSelector, isKnownRule, RULES, RULES_BY_CODE } from './rules/registry';
56
+ export { computeScore, DEFAULT_GRADE_BANDS, DEFAULT_WEIGHTS, meetsGrade } from './score/score';
57
+ export * from './types';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Source-level SQL/PL/pgSQL convention linter.
3
+ *
4
+ * This module used to carry safegres's own copy of the linter. It now lives in
5
+ * the standalone `@pgsql/lint` package (source text in → findings out, no `pg`
6
+ * dependency); safegres consumes it as "the catalog adapter", feeding it the
7
+ * `pg_get_functiondef` text it already reads. This file is a thin re-export so
8
+ * the rest of safegres keeps importing from `../lint` unchanged.
9
+ */
10
+ export type { DynamicSqlSite, LintOptions, LintProblem, LintResult, LintRule, LintRuleMeta, LintUnit, SqlFragment, SuppressedProblem, SuppressionScope } from '@pgsql/lint';
11
+ export { LINT_RULES, LINT_RULES_BY_CODE, LINT_RULES_BY_ID, lintDefinition, parseUnit, Suppressions } from '@pgsql/lint';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Source-level SQL/PL/pgSQL convention linter.
3
+ *
4
+ * This module used to carry safegres's own copy of the linter. It now lives in
5
+ * the standalone `@pgsql/lint` package (source text in → findings out, no `pg`
6
+ * dependency); safegres consumes it as "the catalog adapter", feeding it the
7
+ * `pg_get_functiondef` text it already reads. This file is a thin re-export so
8
+ * the rest of safegres keeps importing from `../lint` unchanged.
9
+ */
10
+ export { LINT_RULES, LINT_RULES_BY_CODE, LINT_RULES_BY_ID, lintDefinition, parseUnit, Suppressions } from '@pgsql/lint';
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Perf baseline: a committed snapshot of accepted performance debt.
3
+ *
4
+ * The point is the ratchet. A large schema will not reach a clean perf report
5
+ * in one pass, but it can refuse to get worse: commit the current findings,
6
+ * then fail CI only on findings that are *not* in the baseline.
7
+ *
8
+ * Only the identity of a finding is stored — code, relation, policy, and the
9
+ * one context field that distinguishes findings of the same code on the same
10
+ * relation. Messages, severities and hints may be reworded between safegres
11
+ * versions without invalidating a committed baseline.
12
+ */
13
+ import type { Finding } from '../types';
14
+ export interface PerfBaseline {
15
+ version: 1;
16
+ findings: BaselineFinding[];
17
+ }
18
+ export interface BaselineFinding {
19
+ code: string;
20
+ schema?: string;
21
+ table?: string;
22
+ policy?: string;
23
+ /** The rule-specific subject: constraint, index, column, expression, or function. */
24
+ subject?: string;
25
+ }
26
+ export interface PerfDiff {
27
+ /** Findings present now but absent from the baseline — new debt. */
28
+ added: Finding[];
29
+ /** Baseline entries no longer produced — fixed, or the relation is gone. */
30
+ removed: BaselineFinding[];
31
+ /** Findings that matched the baseline — accepted debt, still reported. */
32
+ accepted: Finding[];
33
+ }
34
+ export declare function subjectOf(finding: Finding): string | undefined;
35
+ export declare function findingKey(entry: BaselineFinding): string;
36
+ export declare function toBaselineFinding(finding: Finding): BaselineFinding;
37
+ export declare function toPerfBaseline(findings: Finding[]): PerfBaseline;
38
+ export declare function diffPerf(findings: Finding[], baseline: PerfBaseline): PerfDiff;
39
+ export declare function parsePerfBaseline(raw: string): PerfBaseline;
40
+ export declare function serializePerfBaseline(baseline: PerfBaseline): string;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Perf baseline: a committed snapshot of accepted performance debt.
3
+ *
4
+ * The point is the ratchet. A large schema will not reach a clean perf report
5
+ * in one pass, but it can refuse to get worse: commit the current findings,
6
+ * then fail CI only on findings that are *not* in the baseline.
7
+ *
8
+ * Only the identity of a finding is stored — code, relation, policy, and the
9
+ * one context field that distinguishes findings of the same code on the same
10
+ * relation. Messages, severities and hints may be reworded between safegres
11
+ * versions without invalidating a committed baseline.
12
+ */
13
+ /**
14
+ * Context keys that identify *which* object a finding is about, in priority
15
+ * order (X3 carries both `expression` and `column`; the expression is the
16
+ * narrower identity). Rules that can only fire once per relation — X6 — set
17
+ * none of these and are keyed by code + relation alone.
18
+ */
19
+ const SUBJECT_KEYS = ['constraint', 'index', 'expression', 'column', 'function'];
20
+ export function subjectOf(finding) {
21
+ const context = finding.context;
22
+ if (!context)
23
+ return undefined;
24
+ for (const key of SUBJECT_KEYS) {
25
+ const value = context[key];
26
+ if (typeof value === 'string' && value.length > 0)
27
+ return value;
28
+ }
29
+ return undefined;
30
+ }
31
+ export function findingKey(entry) {
32
+ return [entry.code, entry.schema ?? '', entry.table ?? '', entry.policy ?? '', entry.subject ?? ''].join('|');
33
+ }
34
+ export function toBaselineFinding(finding) {
35
+ const subject = subjectOf(finding);
36
+ return {
37
+ code: finding.code,
38
+ ...(finding.schema ? { schema: finding.schema } : {}),
39
+ ...(finding.table ? { table: finding.table } : {}),
40
+ ...(finding.policy ? { policy: finding.policy } : {}),
41
+ ...(subject ? { subject } : {})
42
+ };
43
+ }
44
+ export function toPerfBaseline(findings) {
45
+ const seen = new Set();
46
+ const out = [];
47
+ for (const finding of findings) {
48
+ const entry = toBaselineFinding(finding);
49
+ const key = findingKey(entry);
50
+ if (seen.has(key))
51
+ continue;
52
+ seen.add(key);
53
+ out.push(entry);
54
+ }
55
+ out.sort((a, b) => findingKey(a).localeCompare(findingKey(b)));
56
+ return { version: 1, findings: out };
57
+ }
58
+ export function diffPerf(findings, baseline) {
59
+ const baseKeys = new Set(baseline.findings.map(findingKey));
60
+ const currentKeys = new Set();
61
+ const added = [];
62
+ const accepted = [];
63
+ for (const finding of findings) {
64
+ const key = findingKey(toBaselineFinding(finding));
65
+ currentKeys.add(key);
66
+ if (baseKeys.has(key))
67
+ accepted.push(finding);
68
+ else
69
+ added.push(finding);
70
+ }
71
+ const removed = baseline.findings.filter((entry) => !currentKeys.has(findingKey(entry)));
72
+ return { added, removed, accepted };
73
+ }
74
+ export function parsePerfBaseline(raw) {
75
+ const data = JSON.parse(raw);
76
+ if (data.version !== 1 || !Array.isArray(data.findings)) {
77
+ throw new Error('invalid perf baseline: expected { version: 1, findings: [...] }');
78
+ }
79
+ return { version: 1, findings: data.findings };
80
+ }
81
+ export function serializePerfBaseline(baseline) {
82
+ return JSON.stringify(baseline, null, 2) + '\n';
83
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Proof by EXPLAIN (`--explain`).
3
+ *
4
+ * The `X*` rules infer from the catalog that a query *shape* has no index to
5
+ * serve it. This turns the inference into evidence: for each finding that has
6
+ * a probe shape, plan the query the finding is about and read the plan back.
7
+ *
8
+ * The planner is the authority, so the interesting outcome is disagreement —
9
+ * a finding whose probe plans as an index scan is **refuted** (some index the
10
+ * catalog rules didn't credit does serve it) and stops counting against the
11
+ * score. The reverse is not symmetrical: an empty or unanalyzed table always
12
+ * seq-scans, so a seq scan only *confirms* a finding above the row floor;
13
+ * below it the probe is inconclusive and the finding is left exactly as it
14
+ * was.
15
+ *
16
+ * Probes use `EXPLAIN (GENERIC_PLAN)` — parameters stay parameters, nothing
17
+ * is executed, and no value has to be invented for a column whose domain we
18
+ * know nothing about. That requires PostgreSQL 16+.
19
+ */
20
+ import type { TableIndexSnapshot } from '../pg/indexes';
21
+ import type { QueryExecutor } from '../pg/introspect';
22
+ import type { Finding } from '../types';
23
+ export interface ExplainOptions {
24
+ /**
25
+ * Below this planner row estimate a sequential scan is the correct plan, so
26
+ * a seq-scan result proves nothing. Default 1000.
27
+ */
28
+ minRows?: number;
29
+ }
30
+ export interface ExplainReport {
31
+ /** Findings that were probed. */
32
+ probed: number;
33
+ /** Probes whose plan showed the scan the finding predicted. */
34
+ confirmed: number;
35
+ /** Probes the planner served with an index — the finding was wrong. */
36
+ refuted: number;
37
+ /** Probes on tables too small for the plan to mean anything. */
38
+ inconclusive: number;
39
+ /** Why probing was skipped entirely, when it was. */
40
+ unavailable?: string;
41
+ }
42
+ /**
43
+ * Plan a probe for every probeable perf finding and annotate it in place.
44
+ * Findings the planner refutes are acknowledged (reported, unscored).
45
+ */
46
+ export declare function proveFindings(exec: QueryExecutor, findings: Finding[], tables: Map<string, TableIndexSnapshot>, options?: ExplainOptions): Promise<ExplainReport>;