@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,263 @@
1
+ "use strict";
2
+ /**
3
+ * Structural index-hygiene checks (perf dimension, `X*`).
4
+ *
5
+ * Everything here is pure catalog analysis — deterministic, workload-free,
6
+ * and safe to run in CI against an empty database.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.checkUnindexedForeignKeys = checkUnindexedForeignKeys;
10
+ exports.checkRedundantIndexes = checkRedundantIndexes;
11
+ exports.checkMissingPrimaryKey = checkMissingPrimaryKey;
12
+ exports.checkUnindexedSearchColumns = checkUnindexedSearchColumns;
13
+ exports.checkUnindexedSortColumns = checkUnindexedSortColumns;
14
+ const paths_1 = require("../pg/paths");
15
+ /**
16
+ * X1: a foreign key with no index that can serve it.
17
+ *
18
+ * An index covers a FK when its *leading* columns are exactly the FK's
19
+ * columns (order among them is irrelevant for equality lookups) and it
20
+ * covers every row. Without one, `ON DELETE`/`ON UPDATE` checks on the
21
+ * referenced side and joins on the referencing side degrade to sequential
22
+ * scans.
23
+ *
24
+ * Partial and expression indexes do not count: the planner cannot rely on
25
+ * them for the referential-integrity lookup.
26
+ *
27
+ * `paths` supplies the access-path signals. They never remove a finding on
28
+ * their own — see {@link WriteOncePointerPolicy} and `classifyPaths`.
29
+ */
30
+ function checkUnindexedForeignKeys(table, paths, onWriteOncePointer = 'report') {
31
+ // Partitions inherit their parent's indexes; the parent carries the finding.
32
+ if (table.isPartition)
33
+ return [];
34
+ const findings = [];
35
+ for (const fk of table.foreignKeys) {
36
+ if (fk.columns.length === 0)
37
+ continue;
38
+ if (table.indexes.some((idx) => indexCoversColumns(idx, fk.columns)))
39
+ continue;
40
+ const path = paths?.get((0, paths_1.pathKey)(table.schema, table.name, fk.name));
41
+ const writeOnceShaped = path?.assessment === 'write-once-shaped';
42
+ if (writeOnceShaped && onWriteOncePointer === 'suppress')
43
+ continue;
44
+ const cols = fk.columnNames.join(', ');
45
+ findings.push({
46
+ code: 'X1',
47
+ severity: 'medium',
48
+ category: 'index',
49
+ schema: table.schema,
50
+ table: table.name,
51
+ message: `Foreign key ${fk.name} on ${table.schema}.${table.name} (${cols}) has no covering index`,
52
+ hint: writeOnceShaped
53
+ ? `CREATE INDEX ON ${table.schema}.${table.name} (${cols}) — or, if nothing queries this relation, stop exposing it. ${path.signals.map((s) => s.detail).join('; ')}.`
54
+ : `CREATE INDEX ON ${table.schema}.${table.name} (${cols}); without it, deletes/updates on ${fk.references} and joins across this key scan the whole table.`,
55
+ context: {
56
+ constraint: fk.name,
57
+ columns: fk.columnNames,
58
+ references: fk.references,
59
+ ...(path ? { pathSignals: path.signals.map((s) => s.name), pathAssessment: path.assessment } : {})
60
+ }
61
+ });
62
+ }
63
+ return findings;
64
+ }
65
+ /**
66
+ * X5: an index that another index already serves.
67
+ *
68
+ * Two shapes: an exact duplicate, or a non-unique index whose columns are a
69
+ * leading-column prefix of a wider index. Both cost write throughput and
70
+ * disk for nothing. Constraint-backed indexes (PK/UNIQUE/EXCLUDE) are never
71
+ * reported — dropping them would drop the constraint.
72
+ *
73
+ * An index attached to a parent's partitioned index is never reported either:
74
+ * it can only be dropped with the parent's index, and the parent carries that
75
+ * finding once instead of every partition repeating it. An index created on
76
+ * the partition alone is the partition's own, and is reported there.
77
+ */
78
+ function checkRedundantIndexes(table) {
79
+ const findings = [];
80
+ const candidates = new Set(table.indexes.filter((i) => !i.primary && !i.constraint && !i.partial && !i.expression && !i.unique && !i.attached));
81
+ for (const idx of candidates) {
82
+ const covering = table.indexes.find((other) => {
83
+ if (other.name === idx.name)
84
+ return false;
85
+ if (other.partial || other.expression)
86
+ return false;
87
+ if (other.method !== idx.method)
88
+ return false;
89
+ if (!isPrefix(idx.columns, other.columns))
90
+ return false;
91
+ // Exact duplicates: when both are droppable, report only one of the
92
+ // pair, deterministically.
93
+ if (idx.columns.length === other.columns.length
94
+ && candidates.has(other)
95
+ && idx.name < other.name)
96
+ return false;
97
+ return true;
98
+ });
99
+ if (!covering)
100
+ continue;
101
+ const duplicate = idx.columns.length === covering.columns.length;
102
+ findings.push({
103
+ code: 'X5',
104
+ severity: 'low',
105
+ category: 'index',
106
+ schema: table.schema,
107
+ table: table.name,
108
+ message: `Index ${idx.name} (${idx.columnNames.join(', ')}) is ${duplicate ? 'a duplicate of' : 'a leading-column prefix of'} `
109
+ + `${covering.name} (${covering.columnNames.join(', ')})`,
110
+ hint: `DROP INDEX ${table.schema}.${idx.name}; every query it serves is served by ${covering.name}.`,
111
+ context: { index: idx.name, coveredBy: covering.name, duplicate }
112
+ });
113
+ }
114
+ return findings;
115
+ }
116
+ /**
117
+ * X6: no primary key and no usable replica identity.
118
+ *
119
+ * Rows cannot be addressed individually: updates and deletes have no cheap
120
+ * lookup path, logical replication and CDC cannot identify old rows, and
121
+ * realtime/change-feed features break.
122
+ */
123
+ function checkMissingPrimaryKey(table) {
124
+ if (table.isPartition)
125
+ return null;
126
+ if (table.hasPrimaryKey)
127
+ return null;
128
+ // `f` (FULL) and `i` (index) still give logical decoding an identity.
129
+ if (table.replicaIdentity === 'f' || table.replicaIdentity === 'i')
130
+ return null;
131
+ return {
132
+ code: 'X6',
133
+ severity: 'low',
134
+ category: 'index',
135
+ schema: table.schema,
136
+ table: table.name,
137
+ message: `${table.schema}.${table.name} has no primary key`,
138
+ hint: 'Add a primary key, or set REPLICA IDENTITY FULL / USING INDEX, so rows can be addressed by updates, deletes, and logical replication.',
139
+ context: { replicaIdentity: table.replicaIdentity }
140
+ };
141
+ }
142
+ /**
143
+ * Search column types that only perform with a specialised index, mapped to
144
+ * the access methods that can serve them.
145
+ *
146
+ * The column type *is* the declaration: `graphile-search` detects searchable
147
+ * columns by codec (tsvector → full-text filter, vector → similarity search),
148
+ * so a bare `tsvector` column is already an API promise. BM25 and pg_trgm are
149
+ * absent on purpose — they are discovered *from* their indexes, so a missing
150
+ * index means the feature was never exposed rather than exposed and slow.
151
+ */
152
+ const SEARCH_TYPES = {
153
+ tsvector: {
154
+ methods: ['gin', 'gist'],
155
+ feature: 'full-text search',
156
+ example: 'USING gin (%s)'
157
+ },
158
+ vector: {
159
+ methods: ['hnsw', 'ivfflat'],
160
+ feature: 'vector similarity search',
161
+ example: 'USING hnsw (%s vector_cosine_ops)'
162
+ },
163
+ halfvec: {
164
+ methods: ['hnsw', 'ivfflat'],
165
+ feature: 'vector similarity search',
166
+ example: 'USING hnsw (%s halfvec_cosine_ops)'
167
+ },
168
+ sparsevec: {
169
+ methods: ['hnsw'],
170
+ feature: 'vector similarity search',
171
+ example: 'USING hnsw (%s sparsevec_cosine_ops)'
172
+ }
173
+ };
174
+ /**
175
+ * X7: a search column with no index the search can use.
176
+ *
177
+ * A `tsvector` or `vector` column is exposed as a search field by
178
+ * `graphile-search` purely from its type — the schema promises a fast path.
179
+ * Without a GIN/GiST (full-text) or HNSW/IVFFlat (vector) index the promise
180
+ * is served by a sequential scan plus a per-row distance or match computation,
181
+ * which is the single worst plan the API can produce.
182
+ */
183
+ function checkUnindexedSearchColumns(table) {
184
+ if (table.isPartition)
185
+ return [];
186
+ const findings = [];
187
+ for (const column of table.columns) {
188
+ const spec = SEARCH_TYPES[column.baseType];
189
+ if (!spec)
190
+ continue;
191
+ const served = table.indexes.some((idx) => spec.methods.includes(idx.method) && idx.columns.includes(column.attnum));
192
+ if (served)
193
+ continue;
194
+ findings.push({
195
+ code: 'X7',
196
+ severity: 'medium',
197
+ category: 'index',
198
+ schema: table.schema,
199
+ table: table.name,
200
+ message: `${table.schema}.${table.name}.${column.name} is a ${column.type} column with no ${spec.methods.join('/')} index — ${spec.feature} scans the whole table`,
201
+ hint: `CREATE INDEX ON ${table.schema}.${table.name} ${spec.example.replace('%s', column.name)};`,
202
+ context: { column: column.name, type: column.type, methods: spec.methods }
203
+ });
204
+ }
205
+ return findings;
206
+ }
207
+ /** Types whose columns are, in practice, what a connection is sorted by. */
208
+ const SORT_TYPES = new Set(['timestamptz', 'timestamp', 'date']);
209
+ /**
210
+ * X8: a sort-shaped column that leads no index.
211
+ *
212
+ * Every PostGraphile connection can be ordered by any column and paginated
213
+ * with a cursor over that order. Sorting on an unindexed column forces a full
214
+ * sort of the (RLS-filtered) result on every page, and keyset pagination
215
+ * degenerates into a scan-and-discard. Timestamp columns are singled out
216
+ * because they are what feeds are actually ordered by — a heuristic, hence
217
+ * `info` by default; turn it off with `perf.rules: { "X8": "off" }`.
218
+ */
219
+ function checkUnindexedSortColumns(table) {
220
+ if (table.isPartition)
221
+ return [];
222
+ // A leading column can serve both ASC and DESC ordering; a trailing one
223
+ // cannot serve the sort on its own.
224
+ const leading = new Set(table.indexes.filter((i) => !i.partial).map((i) => i.columns[0]).filter((c) => c !== undefined));
225
+ const findings = [];
226
+ for (const column of table.columns) {
227
+ if (!SORT_TYPES.has(column.baseType))
228
+ continue;
229
+ if (leading.has(column.attnum))
230
+ continue;
231
+ findings.push({
232
+ code: 'X8',
233
+ severity: 'info',
234
+ category: 'index',
235
+ schema: table.schema,
236
+ table: table.name,
237
+ message: `${table.schema}.${table.name}.${column.name} (${column.type}) leads no index — ordering or paginating a connection by it sorts the whole table`,
238
+ hint: `If the API orders by this column, CREATE INDEX ON ${table.schema}.${table.name} (${column.name} DESC); otherwise disable X8 or add the table to perf.ignore.`,
239
+ context: { column: column.name, type: column.type }
240
+ });
241
+ }
242
+ return findings;
243
+ }
244
+ /**
245
+ * True when `index` can serve an equality lookup on exactly `columns`:
246
+ * its leading columns are that same set, and it covers every row.
247
+ */
248
+ function indexCoversColumns(index, columns) {
249
+ if (index.partial || index.expression)
250
+ return false;
251
+ if (index.method !== 'btree')
252
+ return false;
253
+ if (index.columns.length < columns.length)
254
+ return false;
255
+ const leading = new Set(index.columns.slice(0, columns.length));
256
+ return columns.every((c) => leading.has(c));
257
+ }
258
+ /** True when `a` is a leading-column prefix of `b` (`a` may equal `b`). */
259
+ function isPrefix(a, b) {
260
+ if (a.length > b.length)
261
+ return false;
262
+ return a.every((col, i) => b[i] === col);
263
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * The grant/RLS/policy lattice (L-series).
3
+ *
4
+ * The A/R rules reason from grants exactly as the catalog stores them, which
5
+ * misses two ways access actually arrives: `GRANT TO PUBLIC` (one ACL row
6
+ * that applies to every role) and role inheritance (zero ACL rows on the
7
+ * member). The lattice rules evaluate the *effective* cell each
8
+ * (relation, role, privilege) lands in:
9
+ *
10
+ * | grant | RLS | policy | verdict |
11
+ * |-------|-----|--------|-------------------------------------------|
12
+ * | yes | on | yes | normal — policy-mediated access |
13
+ * | yes | on | no | dead grant (L1, indirect; A4/A5, direct) |
14
+ * | yes | off | — | unmediated (L5 for untrusted, indirect) |
15
+ * | no | — | yes | dead policy (L2) |
16
+ *
17
+ * plus schema-USAGE composition: an object grant the grantee cannot reach
18
+ * because USAGE is missing (L3), and USAGE that reaches no object (L4).
19
+ *
20
+ * Every L rule ships signal-only: L1–L3 are fail-closed (zero score weight by
21
+ * default), L4/L5 default to `info`. Escalate via config once the findings
22
+ * prove themselves.
23
+ */
24
+ import type { RoleAttributes, SchemaAclInfo } from '../pg/acl';
25
+ import type { GrantInfo, PgPrivilege, TableSnapshot } from '../pg/introspect';
26
+ import type { Finding } from '../types';
27
+ export type RoleGraph = Map<string, RoleAttributes>;
28
+ /** How an effective privilege arrived at a role. */
29
+ export type GrantVia = 'direct' | 'PUBLIC' | `member of ${string}`;
30
+ export interface EffectiveGrant {
31
+ privilege: PgPrivilege;
32
+ via: GrantVia;
33
+ }
34
+ /**
35
+ * Every privilege `role` effectively holds on `table`: direct grants, grants
36
+ * TO PUBLIC, and grants to roles it inherits from. Deduplicated per privilege
37
+ * with the most direct provenance winning (direct > PUBLIC > inherited).
38
+ *
39
+ * Takes anything carrying an ACL, not just a table: a view's own grants
40
+ * compose the same way, and the view-ownership reach edge asks this same
41
+ * question of them.
42
+ */
43
+ export declare function effectiveGrants(table: Pick<TableSnapshot, 'grants'>, role: string, graph: RoleGraph): EffectiveGrant[];
44
+ /**
45
+ * How `role` comes to hold EXECUTE on a function, or `null` when it does not.
46
+ *
47
+ * A function ACL carries one privilege, so the closure {@link effectiveGrants}
48
+ * performs per privilege collapses to a single provenance — but it is the same
49
+ * closure: a grant to PUBLIC and a grant to a role the caller INHERITs from
50
+ * both confer EXECUTE without naming the caller anywhere.
51
+ */
52
+ export declare function effectiveExecute(fn: {
53
+ grants: Array<{
54
+ role: string;
55
+ }>;
56
+ }, role: string, graph: RoleGraph): GrantVia | null;
57
+ /** A privilege `role` holds on some, but not all, of a relation's columns. */
58
+ export interface EffectiveColumnGrant extends EffectiveGrant {
59
+ /** The columns it covers, sorted. */
60
+ columns: string[];
61
+ }
62
+ /**
63
+ * Every privilege `role` effectively holds on individual *columns* of `table`,
64
+ * composed exactly like {@link effectiveGrants} — direct, PUBLIC, inherited.
65
+ *
66
+ * A column grant lives in `pg_attribute.attacl` and never appears in the
67
+ * relation's ACL, so a role can hold nothing at all by `effectiveGrants` and
68
+ * still read the columns that matter. Privileges the role already holds on the
69
+ * whole relation are dropped: the column grant then adds no reach, and
70
+ * reporting it would double-count the same access.
71
+ */
72
+ export declare function effectiveColumnGrants(table: Pick<TableSnapshot, 'grants' | 'columnGrants'>, role: string, graph: RoleGraph): EffectiveColumnGrant[];
73
+ /**
74
+ * L1: an *indirect* grant (TO PUBLIC, or inherited from a granted role) on an
75
+ * RLS-enabled table with no applicable permissive policy — a dead grant that
76
+ * A4/A5 cannot see because no direct ACL row names the role. Default-deny
77
+ * admits nothing today; the risk is the grant silently activating the day a
78
+ * broad policy is added.
79
+ */
80
+ export declare function checkIndirectCoverageGaps(table: TableSnapshot, graph: RoleGraph): Finding[];
81
+ /**
82
+ * L2: a permissive policy names a role that holds no grant the policy could
83
+ * mediate — authorization written for a role that cannot reach the table.
84
+ * Either the grant was forgotten (a broken feature) or the policy is stale.
85
+ */
86
+ export declare function checkDeadPolicies(table: TableSnapshot, graph: RoleGraph): Finding[];
87
+ /**
88
+ * L3: an object grant the grantee cannot use because it lacks USAGE on the
89
+ * schema — direct, via PUBLIC, or inherited. The grant is unreachable today
90
+ * and activates silently the day USAGE is granted.
91
+ */
92
+ export declare function checkUnreachableGrants(table: TableSnapshot, schemaAcls: Map<string, SchemaAclInfo>, graph: RoleGraph): Finding[];
93
+ /**
94
+ * L4: schema USAGE that opens the door to nothing — the role reaches no
95
+ * relation (directly, via PUBLIC, or by inheritance) and can EXECUTE no
96
+ * function in the schema. Advisory only: sequences, types and future objects
97
+ * are not modeled, so this is a review candidate, not a proof.
98
+ *
99
+ * `views` matters more than its optionality suggests: a role whose only
100
+ * reachable object in the schema is a view holds USAGE that is load-bearing,
101
+ * and L8 exists precisely because that view can be the role's entire read
102
+ * path. Called without them, L4 sees a table-only schema and reports a grant
103
+ * the API depends on as revokable — so pass them wherever they are available.
104
+ */
105
+ export declare function checkDeadSchemaUsage(schemaAcls: SchemaAclInfo[], tables: TableSnapshot[], graph: RoleGraph, views?: Array<{
106
+ schema: string;
107
+ grants: GrantInfo[];
108
+ }>): Finding[];
109
+ export interface LatticeRoleOptions {
110
+ /** Role names considered untrusted (exact match). */
111
+ roles?: string[];
112
+ /** Take the untrusted roles from the resolved exposure surface. See `RoleTrustOptions`. */
113
+ rolesFrom?: 'exposure' | 'anon';
114
+ }
115
+ export interface UnaddressableGrantOptions {
116
+ /** API-edge roles: the roles requests actually arrive as. */
117
+ roles: string[];
118
+ /**
119
+ * Relations named by some RLS policy predicate anywhere in the database.
120
+ *
121
+ * The reason this parameter exists: a grant on a relation the API cannot
122
+ * address is *not* automatically dead, because a policy on a different
123
+ * relation may subquery it under the querying role. Revoking that grant
124
+ * breaks authorization at runtime, silently, everywhere. A relation any
125
+ * predicate mentions is therefore never reported, whatever the API says.
126
+ */
127
+ policyReferenced: Set<string>;
128
+ }
129
+ /**
130
+ * L6: an API-edge role holds privileges on a relation its own API cannot
131
+ * address.
132
+ *
133
+ * The composition the two halves of safegres were built for: the lattice knows
134
+ * what a role effectively holds, exposure reach knows what the generated API
135
+ * can name, and the difference is grant surface that exists for nothing. It is
136
+ * not a leak — the role would have to connect directly to use it — which is
137
+ * exactly why it is the kind of finding that survives for years.
138
+ */
139
+ export declare function checkUnaddressableGrant(table: TableSnapshot, graph: RoleGraph, unaddressable: Set<string>, options: UnaddressableGrantOptions): Finding[];
140
+ /**
141
+ * L5: an untrusted role reaches an RLS-disabled table *indirectly* — through
142
+ * a grant TO PUBLIC or by inheriting from a granted role. Direct grants are
143
+ * A2/R1 territory; the indirect paths are exactly the ones that slip past
144
+ * exact-name matching (e.g. a world-readable table reached by `anonymous`
145
+ * through PUBLIC).
146
+ */
147
+ export declare function checkUntrustedIndirectAccess(table: TableSnapshot, graph: RoleGraph, options?: LatticeRoleOptions): Finding[];
148
+ /** One role's effective reach into one relation. */
149
+ export interface RoleAccessRelation {
150
+ schema: string;
151
+ table: string;
152
+ privileges: PgPrivilege[];
153
+ /** Most direct provenance across the privileges. */
154
+ via: GrantVia;
155
+ /**
156
+ * Present when the reach is column-scoped: the columns reachable, and the
157
+ * privileges above are the column-level ones. Absent for whole-relation
158
+ * access.
159
+ */
160
+ columns?: string[];
161
+ /** `unmediated` = RLS off or bypassed; `mediated` = at least one applicable policy; `dead` = RLS default-deny. */
162
+ access: 'unmediated' | 'mediated' | 'dead';
163
+ }
164
+ export interface RoleAccessEntry {
165
+ role: string;
166
+ /** Relations where at least one privilege is usable (mediated or unmediated). */
167
+ accessibleTables: number;
168
+ /** Relations readable/writable with no RLS mediation at all. */
169
+ unmediated: RoleAccessRelation[];
170
+ /** Relations reachable only through policies. */
171
+ mediated: number;
172
+ /** Relations where every effective privilege is dead (RLS default-deny). */
173
+ dead: number;
174
+ }
175
+ /**
176
+ * The direct answer to "what can role X see?": every relation the role
177
+ * effectively reaches, classified by whether RLS mediates the access.
178
+ */
179
+ export declare function computeRoleAccess(tables: TableSnapshot[], graph: RoleGraph, roles: string[]): RoleAccessEntry[];