@orygn/opa-mcp 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/CHANGELOG.md +583 -4
  2. package/README.md +144 -98
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +9 -3
  5. package/dist/cli.js.map +1 -1
  6. package/dist/config.d.ts +14 -4
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +91 -19
  9. package/dist/config.js.map +1 -1
  10. package/dist/constants.d.ts +1 -6
  11. package/dist/constants.d.ts.map +1 -1
  12. package/dist/constants.js +17 -3
  13. package/dist/constants.js.map +1 -1
  14. package/dist/lib/bundle-signatures.d.ts +23 -0
  15. package/dist/lib/bundle-signatures.d.ts.map +1 -0
  16. package/dist/lib/bundle-signatures.js +152 -0
  17. package/dist/lib/bundle-signatures.js.map +1 -0
  18. package/dist/lib/child-env.d.ts +9 -2
  19. package/dist/lib/child-env.d.ts.map +1 -1
  20. package/dist/lib/child-env.js +38 -6
  21. package/dist/lib/child-env.js.map +1 -1
  22. package/dist/lib/conftest-cli.d.ts +25 -10
  23. package/dist/lib/conftest-cli.d.ts.map +1 -1
  24. package/dist/lib/conftest-cli.js +233 -50
  25. package/dist/lib/conftest-cli.js.map +1 -1
  26. package/dist/lib/errors.d.ts +4 -5
  27. package/dist/lib/errors.d.ts.map +1 -1
  28. package/dist/lib/errors.js +15 -3
  29. package/dist/lib/errors.js.map +1 -1
  30. package/dist/lib/inline-paths.d.ts +37 -0
  31. package/dist/lib/inline-paths.d.ts.map +1 -0
  32. package/dist/lib/inline-paths.js +162 -0
  33. package/dist/lib/inline-paths.js.map +1 -0
  34. package/dist/lib/install-id.d.ts.map +1 -1
  35. package/dist/lib/install-id.js +18 -3
  36. package/dist/lib/install-id.js.map +1 -1
  37. package/dist/lib/json-stream.d.ts +26 -0
  38. package/dist/lib/json-stream.d.ts.map +1 -0
  39. package/dist/lib/json-stream.js +69 -0
  40. package/dist/lib/json-stream.js.map +1 -0
  41. package/dist/lib/opa-cli.d.ts +74 -16
  42. package/dist/lib/opa-cli.d.ts.map +1 -1
  43. package/dist/lib/opa-cli.js +107 -30
  44. package/dist/lib/opa-cli.js.map +1 -1
  45. package/dist/lib/opa-client.d.ts +27 -1
  46. package/dist/lib/opa-client.d.ts.map +1 -1
  47. package/dist/lib/opa-client.js +98 -14
  48. package/dist/lib/opa-client.js.map +1 -1
  49. package/dist/lib/opa-paths.d.ts +34 -0
  50. package/dist/lib/opa-paths.d.ts.map +1 -0
  51. package/dist/lib/opa-paths.js +101 -0
  52. package/dist/lib/opa-paths.js.map +1 -0
  53. package/dist/lib/output.d.ts.map +1 -1
  54. package/dist/lib/output.js +83 -10
  55. package/dist/lib/output.js.map +1 -1
  56. package/dist/lib/regal-cli.d.ts +16 -1
  57. package/dist/lib/regal-cli.d.ts.map +1 -1
  58. package/dist/lib/regal-cli.js +40 -6
  59. package/dist/lib/regal-cli.js.map +1 -1
  60. package/dist/lib/rego-ast-walker.d.ts +0 -6
  61. package/dist/lib/rego-ast-walker.d.ts.map +1 -1
  62. package/dist/lib/rego-ast-walker.js +181 -55
  63. package/dist/lib/rego-ast-walker.js.map +1 -1
  64. package/dist/lib/rego-counterexample.d.ts +15 -2
  65. package/dist/lib/rego-counterexample.d.ts.map +1 -1
  66. package/dist/lib/rego-counterexample.js +63 -15
  67. package/dist/lib/rego-counterexample.js.map +1 -1
  68. package/dist/lib/rego-input-path.d.ts +20 -0
  69. package/dist/lib/rego-input-path.d.ts.map +1 -0
  70. package/dist/lib/rego-input-path.js +70 -0
  71. package/dist/lib/rego-input-path.js.map +1 -0
  72. package/dist/lib/rego-ir.d.ts +34 -3
  73. package/dist/lib/rego-ir.d.ts.map +1 -1
  74. package/dist/lib/rego-smt-encoder.d.ts +88 -5
  75. package/dist/lib/rego-smt-encoder.d.ts.map +1 -1
  76. package/dist/lib/rego-smt-encoder.js +270 -31
  77. package/dist/lib/rego-smt-encoder.js.map +1 -1
  78. package/dist/lib/rego-type-inferencer.d.ts +27 -2
  79. package/dist/lib/rego-type-inferencer.d.ts.map +1 -1
  80. package/dist/lib/rego-type-inferencer.js +71 -3
  81. package/dist/lib/rego-type-inferencer.js.map +1 -1
  82. package/dist/lib/rego-verify-engine.d.ts.map +1 -1
  83. package/dist/lib/rego-verify-engine.js +203 -90
  84. package/dist/lib/rego-verify-engine.js.map +1 -1
  85. package/dist/lib/rego-z3.d.ts +66 -0
  86. package/dist/lib/rego-z3.d.ts.map +1 -1
  87. package/dist/lib/rego-z3.js +312 -5
  88. package/dist/lib/rego-z3.js.map +1 -1
  89. package/dist/lib/security.d.ts +8 -0
  90. package/dist/lib/security.d.ts.map +1 -1
  91. package/dist/lib/security.js +130 -54
  92. package/dist/lib/security.js.map +1 -1
  93. package/dist/lib/subprocess.d.ts +13 -0
  94. package/dist/lib/subprocess.d.ts.map +1 -1
  95. package/dist/lib/subprocess.js +153 -18
  96. package/dist/lib/subprocess.js.map +1 -1
  97. package/dist/lib/tool-helpers.d.ts +12 -25
  98. package/dist/lib/tool-helpers.d.ts.map +1 -1
  99. package/dist/lib/tool-helpers.js +66 -40
  100. package/dist/lib/tool-helpers.js.map +1 -1
  101. package/dist/prompts/index.js +2 -2
  102. package/dist/resources/index.js +1 -1
  103. package/dist/resources/index.js.map +1 -1
  104. package/dist/resources/patterns.d.ts +1 -1
  105. package/dist/resources/patterns.d.ts.map +1 -1
  106. package/dist/resources/patterns.js +42 -15
  107. package/dist/resources/patterns.js.map +1 -1
  108. package/dist/server.d.ts +2 -2
  109. package/dist/server.d.ts.map +1 -1
  110. package/dist/server.js +50 -7
  111. package/dist/server.js.map +1 -1
  112. package/dist/tools/authoring/capabilities.d.ts +4 -0
  113. package/dist/tools/authoring/capabilities.d.ts.map +1 -1
  114. package/dist/tools/authoring/capabilities.js +41 -4
  115. package/dist/tools/authoring/capabilities.js.map +1 -1
  116. package/dist/tools/authoring/check-schema.d.ts +6 -0
  117. package/dist/tools/authoring/check-schema.d.ts.map +1 -1
  118. package/dist/tools/authoring/check-schema.js +56 -3
  119. package/dist/tools/authoring/check-schema.js.map +1 -1
  120. package/dist/tools/authoring/format.d.ts.map +1 -1
  121. package/dist/tools/authoring/format.js +2 -2
  122. package/dist/tools/authoring/format.js.map +1 -1
  123. package/dist/tools/authoring/lint.d.ts.map +1 -1
  124. package/dist/tools/authoring/lint.js +8 -6
  125. package/dist/tools/authoring/lint.js.map +1 -1
  126. package/dist/tools/authoring/migrate-v1.d.ts.map +1 -1
  127. package/dist/tools/authoring/migrate-v1.js +20 -4
  128. package/dist/tools/authoring/migrate-v1.js.map +1 -1
  129. package/dist/tools/authoring/parse.d.ts.map +1 -1
  130. package/dist/tools/authoring/parse.js +3 -3
  131. package/dist/tools/authoring/parse.js.map +1 -1
  132. package/dist/tools/bundles/build.d.ts.map +1 -1
  133. package/dist/tools/bundles/build.js +12 -4
  134. package/dist/tools/bundles/build.js.map +1 -1
  135. package/dist/tools/bundles/sign.d.ts +9 -2
  136. package/dist/tools/bundles/sign.d.ts.map +1 -1
  137. package/dist/tools/bundles/sign.js +125 -19
  138. package/dist/tools/bundles/sign.js.map +1 -1
  139. package/dist/tools/bundles/verify.d.ts +1 -1
  140. package/dist/tools/bundles/verify.d.ts.map +1 -1
  141. package/dist/tools/bundles/verify.js +58 -19
  142. package/dist/tools/bundles/verify.js.map +1 -1
  143. package/dist/tools/conftest/index.d.ts +5 -0
  144. package/dist/tools/conftest/index.d.ts.map +1 -1
  145. package/dist/tools/conftest/index.js.map +1 -1
  146. package/dist/tools/conftest/pull.d.ts.map +1 -1
  147. package/dist/tools/conftest/pull.js +24 -10
  148. package/dist/tools/conftest/pull.js.map +1 -1
  149. package/dist/tools/conftest/push.d.ts.map +1 -1
  150. package/dist/tools/conftest/push.js +17 -7
  151. package/dist/tools/conftest/push.js.map +1 -1
  152. package/dist/tools/conftest/test.d.ts +10 -6
  153. package/dist/tools/conftest/test.d.ts.map +1 -1
  154. package/dist/tools/conftest/test.js +61 -39
  155. package/dist/tools/conftest/test.js.map +1 -1
  156. package/dist/tools/conftest/verify.d.ts +10 -6
  157. package/dist/tools/conftest/verify.d.ts.map +1 -1
  158. package/dist/tools/conftest/verify.js +40 -32
  159. package/dist/tools/conftest/verify.js.map +1 -1
  160. package/dist/tools/evaluation/_shared.js +9 -12
  161. package/dist/tools/evaluation/_shared.js.map +1 -1
  162. package/dist/tools/evaluation/bench.d.ts +29 -2
  163. package/dist/tools/evaluation/bench.d.ts.map +1 -1
  164. package/dist/tools/evaluation/bench.js +53 -9
  165. package/dist/tools/evaluation/bench.js.map +1 -1
  166. package/dist/tools/evaluation/compile.js +4 -4
  167. package/dist/tools/evaluation/compile.js.map +1 -1
  168. package/dist/tools/evaluation/eval.d.ts.map +1 -1
  169. package/dist/tools/evaluation/eval.js +12 -16
  170. package/dist/tools/evaluation/eval.js.map +1 -1
  171. package/dist/tools/evaluation/exec.d.ts +16 -0
  172. package/dist/tools/evaluation/exec.d.ts.map +1 -1
  173. package/dist/tools/evaluation/exec.js +38 -6
  174. package/dist/tools/evaluation/exec.js.map +1 -1
  175. package/dist/tools/evaluation/test-multiroot.d.ts +6 -0
  176. package/dist/tools/evaluation/test-multiroot.d.ts.map +1 -1
  177. package/dist/tools/evaluation/test-multiroot.js +35 -8
  178. package/dist/tools/evaluation/test-multiroot.js.map +1 -1
  179. package/dist/tools/evaluation/test.d.ts +45 -0
  180. package/dist/tools/evaluation/test.d.ts.map +1 -1
  181. package/dist/tools/evaluation/test.js +153 -32
  182. package/dist/tools/evaluation/test.js.map +1 -1
  183. package/dist/tools/helpers/coverage-gaps.d.ts +11 -3
  184. package/dist/tools/helpers/coverage-gaps.d.ts.map +1 -1
  185. package/dist/tools/helpers/coverage-gaps.js +7 -7
  186. package/dist/tools/helpers/coverage-gaps.js.map +1 -1
  187. package/dist/tools/helpers/describe-policy.d.ts.map +1 -1
  188. package/dist/tools/helpers/describe-policy.js +2 -2
  189. package/dist/tools/helpers/describe-policy.js.map +1 -1
  190. package/dist/tools/helpers/explain-decision.d.ts.map +1 -1
  191. package/dist/tools/helpers/explain-decision.js +3 -4
  192. package/dist/tools/helpers/explain-decision.js.map +1 -1
  193. package/dist/tools/helpers/explain-undefined.d.ts +9 -3
  194. package/dist/tools/helpers/explain-undefined.d.ts.map +1 -1
  195. package/dist/tools/helpers/explain-undefined.js +214 -73
  196. package/dist/tools/helpers/explain-undefined.js.map +1 -1
  197. package/dist/tools/helpers/fix.d.ts +11 -5
  198. package/dist/tools/helpers/fix.d.ts.map +1 -1
  199. package/dist/tools/helpers/fix.js +88 -45
  200. package/dist/tools/helpers/fix.js.map +1 -1
  201. package/dist/tools/helpers/generate-test-skeleton.d.ts.map +1 -1
  202. package/dist/tools/helpers/generate-test-skeleton.js +105 -21
  203. package/dist/tools/helpers/generate-test-skeleton.js.map +1 -1
  204. package/dist/tools/helpers/playground-share.d.ts +4 -2
  205. package/dist/tools/helpers/playground-share.d.ts.map +1 -1
  206. package/dist/tools/helpers/playground-share.js +13 -3
  207. package/dist/tools/helpers/playground-share.js.map +1 -1
  208. package/dist/tools/helpers/policy-diff.d.ts.map +1 -1
  209. package/dist/tools/helpers/policy-diff.js +3 -4
  210. package/dist/tools/helpers/policy-diff.js.map +1 -1
  211. package/dist/tools/helpers/security-audit.d.ts.map +1 -1
  212. package/dist/tools/helpers/security-audit.js +19 -19
  213. package/dist/tools/helpers/security-audit.js.map +1 -1
  214. package/dist/tools/helpers/verify.d.ts.map +1 -1
  215. package/dist/tools/helpers/verify.js +5 -1
  216. package/dist/tools/helpers/verify.js.map +1 -1
  217. package/dist/tools/meta/index.d.ts +1 -1
  218. package/dist/tools/server-management/_shared.d.ts +15 -4
  219. package/dist/tools/server-management/_shared.d.ts.map +1 -1
  220. package/dist/tools/server-management/_shared.js +86 -13
  221. package/dist/tools/server-management/_shared.js.map +1 -1
  222. package/dist/tools/server-management/data.d.ts.map +1 -1
  223. package/dist/tools/server-management/data.js +88 -23
  224. package/dist/tools/server-management/data.js.map +1 -1
  225. package/dist/tools/server-management/decisions.d.ts.map +1 -1
  226. package/dist/tools/server-management/decisions.js +16 -3
  227. package/dist/tools/server-management/decisions.js.map +1 -1
  228. package/dist/tools/server-management/policies.d.ts.map +1 -1
  229. package/dist/tools/server-management/policies.js +44 -7
  230. package/dist/tools/server-management/policies.js.map +1 -1
  231. package/dist/tools/server-management/status.d.ts.map +1 -1
  232. package/dist/tools/server-management/status.js +80 -13
  233. package/dist/tools/server-management/status.js.map +1 -1
  234. package/dist/types.d.ts +10 -1
  235. package/dist/types.d.ts.map +1 -1
  236. package/package.json +2 -1
@@ -25,6 +25,61 @@ import { buildChildEnv } from './child-env.js';
25
25
  * OPA_MCP_MAX_SUBPROCESS_BYTES.
26
26
  */
27
27
  export const DEFAULT_MAX_OUTPUT_BYTES = 32 * 1024 * 1024;
28
+ /**
29
+ * How long to wait for the stdio pipes to close once the child has exited.
30
+ *
31
+ * The child's own output is complete at 'exit'; 'close' follows when every
32
+ * holder of the pipes is gone. A grandchild that inherited them, which is the
33
+ * ordinary shape of a wrapper script around the binary, keeps 'close' from
34
+ * ever firing, and a result that waits for it never settles.
35
+ */
36
+ const PIPE_DRAIN_GRACE_MS = 1_000;
37
+ /** Grace between SIGTERM and SIGKILL. */
38
+ const KILL_ESCALATION_MS = 2_000;
39
+ /**
40
+ * Ceiling on the drain after 'exit', however often late data re-arms it. The
41
+ * deadline bounds the drain while it is still pending; once it has fired, a
42
+ * writer that survived the group signal could otherwise keep the result open
43
+ * for as long as it kept writing.
44
+ */
45
+ const PIPE_DRAIN_MAX_MS = 5 * PIPE_DRAIN_GRACE_MS;
46
+ /**
47
+ * Signal the child, and on POSIX its whole process group, so a grandchild
48
+ * started by a wrapper script dies with it. The child is spawned as a group
49
+ * leader for this purpose; when the group is already gone, or the platform
50
+ * has no groups, fall back to the child alone.
51
+ */
52
+ function signalTree(child, sig) {
53
+ // The group is addressed by the child's pid, which the kernel may hand to
54
+ // an unrelated process once the child has been reaped. Past that point only
55
+ // the handle is safe to use, and Node's kill on a dead handle is a no-op.
56
+ const unreaped = child.exitCode === null && child.signalCode === null;
57
+ if (unreaped && process.platform !== 'win32' && child.pid !== undefined) {
58
+ try {
59
+ process.kill(-child.pid, sig);
60
+ return;
61
+ }
62
+ catch {
63
+ // ESRCH or EPERM: no group to signal. The child itself may still be there.
64
+ }
65
+ }
66
+ child.kill(sig);
67
+ }
68
+ /** Children that have been spawned and have not settled yet. */
69
+ const liveChildren = new Set();
70
+ /**
71
+ * Signal every child still running. A child sits in its own process group on
72
+ * POSIX, so a signal that stops the server does not reach it on its own; the
73
+ * server calls this on the way out. Returns how many were signalled.
74
+ */
75
+ export function terminateChildren(sig = 'SIGTERM') {
76
+ let n = 0;
77
+ for (const child of liveChildren) {
78
+ signalTree(child, sig);
79
+ n++;
80
+ }
81
+ return n;
82
+ }
28
83
  /** Accumulates a stream's chunks up to a byte ceiling. */
29
84
  class CappedBuffer {
30
85
  limit;
@@ -88,6 +143,9 @@ export async function runBinary(binary, opts) {
88
143
  env: buildChildEnv(opts.env),
89
144
  shell: false,
90
145
  windowsHide: true,
146
+ // A process group of its own on POSIX, so a kill reaches a grandchild
147
+ // as well. See signalTree.
148
+ detached: process.platform !== 'win32',
91
149
  });
92
150
  const limit = opts.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
93
151
  const stdout = new CappedBuffer(limit);
@@ -96,27 +154,53 @@ export async function runBinary(binary, opts) {
96
154
  let aborted = false;
97
155
  let settled = false;
98
156
  let killTimer;
157
+ let drainTimer;
158
+ /** Set once 'exit' has fired: the child is gone, only its pipes remain. */
159
+ let exited;
160
+ /** Absolute time by which the drain finishes regardless of late data. */
161
+ let drainUntil = 0;
162
+ liveChildren.add(child);
163
+ // Whether the child has actually ended. `child.killed` is not that: Node
164
+ // sets it as soon as a signal was sent, whether or not the child obeyed,
165
+ // so a guard on it never escalated and a child that trapped SIGTERM hung
166
+ // the call for good.
167
+ const alive = () => child.exitCode === null && child.signalCode === null;
99
168
  const killChild = () => {
100
- child.kill('SIGTERM');
169
+ if (settled)
170
+ return;
171
+ signalTree(child, 'SIGTERM');
101
172
  killTimer = setTimeout(() => {
102
- if (!child.killed)
103
- child.kill('SIGKILL');
104
- }, 2_000);
173
+ if (alive())
174
+ signalTree(child, 'SIGKILL');
175
+ }, KILL_ESCALATION_MS);
105
176
  };
106
177
  const timer = setTimeout(() => {
178
+ // The child is already gone and only the drain is pending: settle from
179
+ // what was captured. Calling that a timeout would be wrong.
180
+ if (exited) {
181
+ finishFromCapture();
182
+ return;
183
+ }
107
184
  timedOut = true;
108
185
  killChild();
109
186
  }, opts.timeoutMs);
110
- if (opts.signal) {
111
- opts.signal.addEventListener('abort', () => {
112
- aborted = true;
113
- killChild();
114
- }, { once: true });
115
- }
187
+ // Once the child has exited there is nothing to cancel, and a listener
188
+ // left behind after settling would signal a pid the kernel may have
189
+ // handed to someone else. rego_test_multiroot passes one signal through
190
+ // a whole sequence of runs, so the listener has to go when the run does.
191
+ const onAbort = () => {
192
+ if (settled || exited)
193
+ return;
194
+ aborted = true;
195
+ killChild();
196
+ };
197
+ opts.signal?.addEventListener('abort', onAbort, { once: true });
116
198
  const clearTimers = () => {
117
199
  clearTimeout(timer);
118
200
  if (killTimer)
119
201
  clearTimeout(killTimer);
202
+ if (drainTimer)
203
+ clearTimeout(drainTimer);
120
204
  };
121
205
  /**
122
206
  * Resolve at most once. 'error' and 'close' can both fire -- killing a child
@@ -128,16 +212,51 @@ export async function runBinary(binary, opts) {
128
212
  return;
129
213
  settled = true;
130
214
  clearTimers();
215
+ liveChildren.delete(child);
216
+ opts.signal?.removeEventListener('abort', onAbort);
131
217
  resolvePromise(result);
132
218
  };
133
- child.stdout?.on('data', (chunk) => {
134
- if (stdout.push(chunk))
135
- killChild();
136
- });
137
- child.stderr?.on('data', (chunk) => {
138
- if (stderr.push(chunk))
219
+ /**
220
+ * Build the result from the capture and let go of the pipes. Used once the
221
+ * child has exited but something it left behind still holds its stdio.
222
+ */
223
+ const finishFromCapture = () => {
224
+ if (settled || !exited)
225
+ return;
226
+ child.stdout?.destroy();
227
+ child.stderr?.destroy();
228
+ finish(exited.code, exited.signal);
229
+ };
230
+ // (Re)start the wait for 'close'. Bytes can still be sitting in the pipe
231
+ // at 'exit', and on a loaded machine the reads that deliver them may
232
+ // queue behind an expired timer; each chunk that arrives pushes the
233
+ // grace out again, and the deadline above bounds the whole thing.
234
+ const armDrain = () => {
235
+ if (drainTimer)
236
+ clearTimeout(drainTimer);
237
+ const left = drainUntil - Date.now();
238
+ if (left <= 0) {
239
+ finishFromCapture();
240
+ return;
241
+ }
242
+ drainTimer = setTimeout(finishFromCapture, Math.min(PIPE_DRAIN_GRACE_MS, left));
243
+ };
244
+ const onData = (buf, chunk) => {
245
+ const overflowed = buf.push(chunk);
246
+ if (exited) {
247
+ // Nothing left to kill; a writer that outlived the child is either
248
+ // done, or has just been cut off by the cap.
249
+ if (overflowed)
250
+ finishFromCapture();
251
+ else
252
+ armDrain();
253
+ return;
254
+ }
255
+ if (overflowed)
139
256
  killChild();
140
- });
257
+ };
258
+ child.stdout?.on('data', (chunk) => onData(stdout, chunk));
259
+ child.stderr?.on('data', (chunk) => onData(stderr, chunk));
141
260
  // Killing a child mid-write can surface an EPIPE on the pipe. Without a
142
261
  // listener that is an unhandled 'error' event, which would take the whole
143
262
  // server down -- the exact failure mode this cap exists to prevent.
@@ -153,9 +272,10 @@ export async function runBinary(binary, opts) {
153
272
  aborted,
154
273
  durationMs: Date.now() - start,
155
274
  outputTruncated: false,
275
+ signal: null,
156
276
  });
157
277
  });
158
- child.on('close', (code) => {
278
+ const finish = (code, signal) => {
159
279
  // Decoding runs here, in an async callback whose throw would escape every
160
280
  // try/catch upstream and reach the process as an uncaughtException. The
161
281
  // byte cap is what makes that unreachable; this is the belt to its braces.
@@ -168,6 +288,7 @@ export async function runBinary(binary, opts) {
168
288
  aborted,
169
289
  durationMs: Date.now() - start,
170
290
  outputTruncated: stdout.didOverflow || stderr.didOverflow,
291
+ signal,
171
292
  });
172
293
  }
173
294
  catch (e) {
@@ -179,9 +300,23 @@ export async function runBinary(binary, opts) {
179
300
  aborted,
180
301
  durationMs: Date.now() - start,
181
302
  outputTruncated: true,
303
+ signal,
182
304
  });
183
305
  }
306
+ };
307
+ // 'close' carries the complete output and is the normal way to finish.
308
+ // 'exit' starts a short grace for it: when the pipes are still held open
309
+ // by something the child left behind, the result is built from what was
310
+ // captured and the server's ends of the pipes are released.
311
+ child.on('exit', (code, signal) => {
312
+ // A spawn failure settles on 'error' and may still be followed by 'exit'.
313
+ if (settled)
314
+ return;
315
+ exited = { code, signal };
316
+ drainUntil = Date.now() + PIPE_DRAIN_MAX_MS;
317
+ armDrain();
184
318
  });
319
+ child.on('close', (code, signal) => finish(code, signal));
185
320
  if (opts.stdin !== undefined) {
186
321
  child.stdin?.write(opts.stdin);
187
322
  child.stdin?.end();
@@ -1 +1 @@
1
- {"version":3,"file":"subprocess.js","sourceRoot":"","sources":["../../src/lib/subprocess.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"subprocess.js","sourceRoot":"","sources":["../../src/lib/subprocess.ts"],"names":[],"mappings":"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"}
@@ -6,41 +6,28 @@
6
6
  * the formatted MCP result. The pieces here factor out the parts every
7
7
  * tool repeats so each tool file can stay focused on its own logic.
8
8
  */
9
- /**
10
- * Matches the temp-file paths written by OpaCli.withTempSource and
11
- * RegalCli.withTempSource when handling inline source. Both now use
12
- * mkdtemp which produces a private directory; the file inside is always
13
- * named input.rego. Matches both Unix (/) and Windows (\) separators.
14
- */
15
- export declare const INLINE_TEMP_PATH_PATTERN: RegExp;
16
- /**
17
- * Replace a file path that refers to one of our temp files with the
18
- * sentinel string `<inline>`. Returns the original string unchanged when
19
- * it does not match the pattern, so callers can unconditionally apply it
20
- * to all location.file values regardless of whether inline source was used.
21
- */
22
- export declare function sanitizeInlinePath(file: string): string;
23
- /**
24
- * Recursively rewrite every temp-file path -- in both string values and object
25
- * keys -- to the `<inline>` sentinel. OPA writes inline source to a temp file,
26
- * so trace (`explanation`), coverage, and profile output reference that path;
27
- * this normalizes the whole structure so callers never see an absolute temp
28
- * path. Only our own temp paths match the pattern, so real user file paths pass
29
- * through untouched.
30
- */
31
- export declare function sanitizeInlinePathsDeep(value: unknown): unknown;
32
9
  import type { Config } from '../config.js';
33
10
  import { type McpToolResult } from './output.js';
34
11
  import type { SpawnResult } from './subprocess.js';
35
12
  import type { ToolEnvelope } from '../types.js';
13
+ export { INLINE_TEMP_PATH_PATTERN, sanitizeInlinePath, sanitizeInlinePathsDeep, sanitizeInlineText, } from './inline-paths.js';
36
14
  /**
37
15
  * Convert a subprocess outcome into a structured tool error envelope
38
- * for the cases that are universal across all CLI-backed tools:
39
- * binary missing, subprocess timed out. Returns `undefined` when the
16
+ * for the cases that are universal across all CLI-backed tools: cancelled
17
+ * by the client, timed out, output over the capture cap, killed by a
18
+ * signal from outside the server, binary missing. Returns `undefined` when the
40
19
  * subprocess exited normally (exitCode is a number) so the caller can
41
20
  * inspect the result.
42
21
  */
43
22
  export declare function mapSubprocessFailure(result: SpawnResult, binary: 'opa' | 'regal' | 'conftest'): ToolEnvelope<never> | undefined;
23
+ /**
24
+ * Parse the last top-level JSON object in `text`, for a command that prints
25
+ * one document per run (`opa test --coverage --count N`). Strings are
26
+ * honoured, so a brace inside a file name does not end a document. Returns
27
+ * `undefined` when there is none. `accept` narrows it to the last object of
28
+ * the shape wanted, so trace text that happens to hold braces is skipped.
29
+ */
30
+ export declare function lastJsonObject<T = unknown>(text: string, accept?: (parsed: unknown) => boolean): T | undefined;
44
31
  /**
45
32
  * Try to parse `text` as JSON. Returns `undefined` on failure so the
46
33
  * caller can fall back to a textual error envelope rather than throwing.
@@ -1 +1 @@
1
- {"version":3,"file":"tool-helpers.d.ts","sourceRoot":"","sources":["../../src/lib/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,QAAsD,CAAC;AAE5F;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAW/D;AACD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAE/D;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,UAAU,GACnC,YAAY,CAAC,KAAK,CAAC,GAAG,SAAS,CAiDjC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAOrE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GACpC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,CAAA;CAAE,CAU9E;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GACnC,OAAO,CAAC,aAAa,CAAC,CAgBxB"}
1
+ {"version":3,"file":"tool-helpers.d.ts","sourceRoot":"","sources":["../../src/lib/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAG3C,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAE/D,OAAO,EACL,wBAAwB,EACxB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,mBAAmB,CAAC;AAE3B;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,KAAK,GAAG,OAAO,GAAG,UAAU,GACnC,YAAY,CAAC,KAAK,CAAC,GAAG,SAAS,CA+DjC;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,CAAC,GAAG,OAAO,EACxC,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,CAAC,MAAM,EAAE,OAAO,KAAK,OAAoB,GAChD,CAAC,GAAG,SAAS,CAkCf;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAOrE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EAAE,EACf,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GACpC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAC,KAAK,CAAC,CAAA;CAAE,CAU9E;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GACnC,OAAO,CAAC,aAAa,CAAC,CAgBxB"}
@@ -6,52 +6,16 @@
6
6
  * the formatted MCP result. The pieces here factor out the parts every
7
7
  * tool repeats so each tool file can stay focused on its own logic.
8
8
  */
9
- /**
10
- * Matches the temp-file paths written by OpaCli.withTempSource and
11
- * RegalCli.withTempSource when handling inline source. Both now use
12
- * mkdtemp which produces a private directory; the file inside is always
13
- * named input.rego. Matches both Unix (/) and Windows (\) separators.
14
- */
15
- export const INLINE_TEMP_PATH_PATTERN = /orygn-(?:opa|regal)-mcp-[^/\\]+[/\\]input\.rego$/i;
16
- /**
17
- * Replace a file path that refers to one of our temp files with the
18
- * sentinel string `<inline>`. Returns the original string unchanged when
19
- * it does not match the pattern, so callers can unconditionally apply it
20
- * to all location.file values regardless of whether inline source was used.
21
- */
22
- export function sanitizeInlinePath(file) {
23
- return INLINE_TEMP_PATH_PATTERN.test(file) ? '<inline>' : file;
24
- }
25
- /**
26
- * Recursively rewrite every temp-file path -- in both string values and object
27
- * keys -- to the `<inline>` sentinel. OPA writes inline source to a temp file,
28
- * so trace (`explanation`), coverage, and profile output reference that path;
29
- * this normalizes the whole structure so callers never see an absolute temp
30
- * path. Only our own temp paths match the pattern, so real user file paths pass
31
- * through untouched.
32
- */
33
- export function sanitizeInlinePathsDeep(value) {
34
- if (typeof value === 'string')
35
- return sanitizeInlinePath(value);
36
- if (Array.isArray(value))
37
- return value.map(sanitizeInlinePathsDeep);
38
- if (value !== null && typeof value === 'object') {
39
- const out = {};
40
- for (const [key, val] of Object.entries(value)) {
41
- out[sanitizeInlinePath(key)] = sanitizeInlinePathsDeep(val);
42
- }
43
- return out;
44
- }
45
- return value;
46
- }
47
9
  import { err } from './errors.js';
48
10
  import { logger } from './logger.js';
49
11
  import { formatEnvelope } from './output.js';
50
12
  import { validatePath } from './security.js';
13
+ export { INLINE_TEMP_PATH_PATTERN, sanitizeInlinePath, sanitizeInlinePathsDeep, sanitizeInlineText, } from './inline-paths.js';
51
14
  /**
52
15
  * Convert a subprocess outcome into a structured tool error envelope
53
- * for the cases that are universal across all CLI-backed tools:
54
- * binary missing, subprocess timed out. Returns `undefined` when the
16
+ * for the cases that are universal across all CLI-backed tools: cancelled
17
+ * by the client, timed out, output over the capture cap, killed by a
18
+ * signal from outside the server, binary missing. Returns `undefined` when the
55
19
  * subprocess exited normally (exitCode is a number) so the caller can
56
20
  * inspect the result.
57
21
  */
@@ -80,6 +44,16 @@ export function mapSubprocessFailure(result, binary) {
80
44
  details: { durationMs: result.durationMs },
81
45
  });
82
46
  }
47
+ // The server's own kills are handled above. A signal that reaches here came
48
+ // from outside it, most often the kernel's out-of-memory killer, and the
49
+ // binary is installed and was running; sending the user to reinstall it
50
+ // would be wrong.
51
+ if (result.exitCode === null && result.signal) {
52
+ return err('SUBPROCESS_KILLED', `${binary} was terminated by ${result.signal} before it finished.`, {
53
+ hint: 'The process was killed from outside the server, for example by the out-of-memory killer or a container limit. Check the host memory limits and system logs.',
54
+ details: { signal: result.signal, durationMs: result.durationMs },
55
+ });
56
+ }
83
57
  if (result.exitCode === null) {
84
58
  const code = binary === 'opa'
85
59
  ? 'OPA_BINARY_NOT_FOUND'
@@ -95,6 +69,58 @@ export function mapSubprocessFailure(result, binary) {
95
69
  }
96
70
  return undefined;
97
71
  }
72
+ /**
73
+ * Parse the last top-level JSON object in `text`, for a command that prints
74
+ * one document per run (`opa test --coverage --count N`). Strings are
75
+ * honoured, so a brace inside a file name does not end a document. Returns
76
+ * `undefined` when there is none. `accept` narrows it to the last object of
77
+ * the shape wanted, so trace text that happens to hold braces is skipped.
78
+ */
79
+ export function lastJsonObject(text, accept = () => true) {
80
+ let depth = 0;
81
+ let inString = false;
82
+ let escaped = false;
83
+ let start = -1;
84
+ let last;
85
+ for (let i = 0; i < text.length; i++) {
86
+ const ch = text[i];
87
+ if (inString) {
88
+ if (escaped)
89
+ escaped = false;
90
+ else if (ch === '\\')
91
+ escaped = true;
92
+ else if (ch === '"')
93
+ inString = false;
94
+ continue;
95
+ }
96
+ if (ch === '"')
97
+ inString = true;
98
+ else if (ch === '{') {
99
+ if (depth === 0)
100
+ start = i;
101
+ depth++;
102
+ }
103
+ else if (ch === '}') {
104
+ // A stray closing brace must not put every later object out of reach.
105
+ if (depth > 0)
106
+ depth--;
107
+ if (depth === 0 && start !== -1) {
108
+ const candidate = tryParseJson(text.slice(start, i + 1));
109
+ if (candidate !== undefined && accept(candidate))
110
+ last = text.slice(start, i + 1);
111
+ start = -1;
112
+ }
113
+ }
114
+ }
115
+ if (last === undefined)
116
+ return undefined;
117
+ try {
118
+ return JSON.parse(last);
119
+ }
120
+ catch {
121
+ return undefined;
122
+ }
123
+ }
98
124
  /**
99
125
  * Try to parse `text` as JSON. Returns `undefined` on failure so the
100
126
  * caller can fall back to a textual error envelope rather than throwing.
@@ -1 +1 @@
1
- {"version":3,"file":"tool-helpers.js","sourceRoot":"","sources":["../../src/lib/tool-helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tool-helpers.js","sourceRoot":"","sources":["../../src/lib/tool-helpers.ts"],"names":[],"mappings":"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"}
@@ -58,7 +58,7 @@ Apply this checklist, calling tools as needed:
58
58
  concern in a policy hot path.
59
59
  6. **Annotations.** Check that exported rules have docstrings. If
60
60
  missing, suggest minimal \`# METADATA\` annotations.
61
- 7. **Shape of input.** Use \`rego_describe_policy\` to enumerate input
61
+ 7. **Shape of input.** Use \`rego_infer_input_schema\` to enumerate input
62
62
  refs the policy reads; confirm the documented input contract
63
63
  matches.
64
64
 
@@ -81,7 +81,7 @@ Workflow:
81
81
  Identify which rules were evaluated and which fired.
82
82
  4. The cause is one of:
83
83
  a. **Input mismatch** -- the policy expected a different input shape.
84
- Use \`rego_describe_policy\` to list the refs the policy reads
84
+ Use \`rego_infer_input_schema\` to list the refs the policy reads
85
85
  and confirm each is present in the input.
86
86
  b. **Rule logic** -- a guard fired or didn't fire when it should
87
87
  have. Read the trace and explain which rule's body evaluated to
@@ -49,7 +49,7 @@ async function buildBuiltinsResource(opa) {
49
49
  .filter((n) => typeof n === 'string' && SECURITY_SENSITIVE.has(n))
50
50
  .sort();
51
51
  return JSON.stringify({
52
- version_note: 'Derived at read time from `opa capabilities --current`. Reflects the OPA build linked into this MCP server, not necessarily the OPA the user has installed locally.',
52
+ version_note: 'Derived at read time from `opa capabilities --current` of the opa binary this server resolved: OPA_BINARY, then PATH, then the bundled copy.',
53
53
  builtin_count: caps.builtins?.length ?? 0,
54
54
  categories: grouped,
55
55
  future_keywords: caps.future_keywords ?? [],
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"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"}
@@ -5,5 +5,5 @@
5
5
  * - A test
6
6
  * - Common pitfalls
7
7
  */
8
- export declare const PATTERNS = "# Rego pattern library\n\nCommon Rego patterns with working examples, tests, and pitfalls. Each\npattern is self-contained -- copy, adapt, and ship.\n\n---\n\n## 1. Role-based access control (RBAC)\n\n**When to use:** the simplest authorization model. Every user has one\nor more roles; each role grants a set of actions on a set of\nresources. Sufficient for ~80% of internal applications.\n\n```rego\npackage rbac\n\nimport rego.v1\n\ndefault allow := false\n\n# Permissions table -- extend as roles evolve.\npermissions := {\n \"admin\": {\"read\", \"write\", \"delete\", \"manage_users\"},\n \"editor\": {\"read\", \"write\"},\n \"viewer\": {\"read\"},\n}\n\nallow if {\n some role in input.user.roles\n input.action in permissions[role]\n}\n\n# Why was it denied? -- useful for audit logs.\ndeny_reasons contains reason if {\n not allow\n input.user\n reason := sprintf(\n \"user %q has roles %v, none grant %q\",\n [input.user.id, input.user.roles, input.action],\n )\n}\n\ndeny_reasons contains \"anonymous request\" if {\n not allow\n not input.user\n}\n```\n\n**Test:**\n\n```rego\npackage rbac_test\n\nimport rego.v1\nimport data.rbac\n\ntest_admin_can_delete if {\n rbac.allow with input as {\n \"user\": {\"id\": \"alice\", \"roles\": [\"admin\"]},\n \"action\": \"delete\",\n }\n}\n\ntest_viewer_cannot_delete if {\n not rbac.allow with input as {\n \"user\": {\"id\": \"bob\", \"roles\": [\"viewer\"]},\n \"action\": \"delete\",\n }\n}\n\ntest_anonymous_denied if {\n not rbac.allow with input as {\"action\": \"read\"}\n reasons := rbac.deny_reasons with input as {\"action\": \"read\"}\n \"anonymous request\" in reasons\n}\n```\n\n**Pitfalls:**\n- The `permissions` table grows unbounded. Move to data files\n (`data.permissions`) once you have more than ~20 roles.\n- Roles overlap with groups in real auth systems; map at the boundary\n rather than carrying both.\n\n---\n\n## 2. Attribute-based access control (ABAC)\n\n**When to use:** when \"who\" alone isn't enough -- decisions also depend\non resource attributes (ownership, tenant, sensitivity) and context\n(time of day, source IP).\n\n```rego\npackage abac\n\nimport rego.v1\n\ndefault allow := false\n\n# A user can read any resource they own.\nallow if {\n input.action == \"read\"\n input.resource.owner_id == input.user.id\n}\n\n# A user can read shared resources at their organization.\nallow if {\n input.action == \"read\"\n input.resource.shared\n input.resource.org_id == input.user.org_id\n}\n\n# Admins can do anything within their organization.\nallow if {\n \"admin\" in input.user.roles\n input.resource.org_id == input.user.org_id\n}\n\n# Don't show \"secret\" resources to anyone outside the owner's\n# organization, even admins.\nallow := false if {\n input.resource.classification == \"secret\"\n input.resource.org_id != input.user.org_id\n}\n```\n\n**Test:**\n\n```rego\npackage abac_test\n\nimport rego.v1\nimport data.abac\n\ntest_owner_reads_own if {\n abac.allow with input as {\n \"action\": \"read\",\n \"user\": {\"id\": \"u1\", \"org_id\": \"o1\"},\n \"resource\": {\"owner_id\": \"u1\", \"org_id\": \"o1\"},\n }\n}\n\ntest_admin_blocked_from_secret_in_other_org if {\n not abac.allow with input as {\n \"action\": \"read\",\n \"user\": {\"id\": \"u1\", \"org_id\": \"o1\", \"roles\": [\"admin\"]},\n \"resource\": {\n \"owner_id\": \"u2\",\n \"org_id\": \"o2\",\n \"classification\": \"secret\",\n },\n }\n}\n```\n\n**Pitfalls:**\n- Multiple `allow` rules combine with logical OR. Use explicit\n `allow := false if ...` to *override* an allow.\n- Don't compute attributes inside the policy; compute them at the\n boundary and pass via `input`.\n\n---\n\n## 3. Kubernetes admission control\n\n**When to use:** validate or mutate Kubernetes resources at admission\ntime. Run as a Gatekeeper, OPA-as-a-webhook, or Kyverno-equivalent\npolicy layer.\n\n```rego\npackage k8s.admission\n\nimport rego.v1\n\n# Reject pods without resource limits.\ndeny contains msg if {\n input.request.kind.kind == \"Pod\"\n container := input.request.object.spec.containers[_]\n not container.resources.limits.memory\n msg := sprintf(\n \"Pod %q container %q is missing resources.limits.memory\",\n [input.request.object.metadata.name, container.name],\n )\n}\n\n# Reject privileged containers in production.\ndeny contains msg if {\n input.request.kind.kind == \"Pod\"\n input.request.namespace != \"kube-system\"\n container := input.request.object.spec.containers[_]\n container.securityContext.privileged == true\n msg := sprintf(\n \"privileged containers are not allowed: %q in %q\",\n [container.name, input.request.object.metadata.name],\n )\n}\n```\n\n**Pitfalls:**\n- Use `input.request.object` for the resource being admitted; the\n envelope shape comes from Kubernetes, not your control.\n- For mutations, return a JSON Patch via the `patch` field. Test\n patches with the actual admission webhook in dry-run mode before\n enforcing.\n- Iteration order is undefined; never rely on `containers[0]` to mean\n anything specific.\n\n---\n\n## 4. Infrastructure-as-Code gates (Terraform)\n\n**When to use:** validate Terraform plans before apply. Catch overly\npermissive IAM, public S3 buckets, missing encryption.\n\n```rego\npackage terraform\n\nimport rego.v1\n\n# Reject S3 buckets without server-side encryption.\ndeny contains msg if {\n resource := input.resource_changes[_]\n resource.type == \"aws_s3_bucket\"\n after := resource.change.after\n not after.server_side_encryption_configuration\n msg := sprintf(\n \"S3 bucket %q has no server-side encryption configured\",\n [resource.address],\n )\n}\n\n# Reject IAM policies with action \"*\" on resource \"*\".\ndeny contains msg if {\n resource := input.resource_changes[_]\n resource.type == \"aws_iam_policy\"\n policy := json.unmarshal(resource.change.after.policy)\n statement := policy.Statement[_]\n statement.Effect == \"Allow\"\n \"*\" in statement.Action\n statement.Resource == \"*\"\n msg := sprintf(\"IAM policy %q grants Allow * on *\", [resource.address])\n}\n```\n\n**Pitfalls:**\n- Terraform plan JSON is verbose and version-specific. Pin the\n `terraform plan -json` schema you target.\n- `resource_changes[_].change.after` may be `null` for destroys --\n guard against it.\n- For wide-radius changes, use `opa exec --decision` against a plan\n file in CI, not the live API.\n\n---\n\n## 5. API authorization (HTTP request gating)\n\n**When to use:** at the API gateway / reverse proxy layer, validate\neach request against the caller's identity and the requested\nendpoint.\n\n```rego\npackage api.authz\n\nimport rego.v1\n\ndefault allow := false\n\n# Public endpoints -- no auth required.\npublic_endpoints := {\n {\"method\": \"GET\", \"path\": [\"health\"]},\n {\"method\": \"GET\", \"path\": [\"version\"]},\n}\n\nallow if {\n some endpoint in public_endpoints\n matches_endpoint(endpoint)\n}\n\n# Authenticated reads on resources the user has access to.\nallow if {\n input.method == \"GET\"\n input.user\n user_can_read(input.user, input.path)\n}\n\n# Authenticated writes only with specific scopes.\nallow if {\n input.method in {\"POST\", \"PUT\", \"PATCH\", \"DELETE\"}\n input.user\n \"write\" in input.user.scopes\n user_can_write(input.user, input.path)\n}\n\nmatches_endpoint(spec) if {\n spec.method == input.method\n spec.path == input.path\n}\n\nuser_can_read(user, path) if {\n path[0] == \"users\"\n user.id == path[1]\n}\n\nuser_can_write(user, path) if {\n path[0] == \"users\"\n user.id == path[1]\n}\n```\n\n**Pitfalls:**\n- `input.path` is typically an array (`[\"users\", \"alice\"]`), not a\n string. Build it consistently at the gateway.\n- Path-prefix matching is easy; full pattern matching is not. For\n parameterized routes, decode at the gateway and pass structured\n fields.\n\n---\n\n## 6. Rate limiting (with sliding window data)\n\n**When to use:** allow N requests per principal per window. Lightweight\nlimit enforcement; for high throughput, push to a dedicated rate\nlimiter.\n\n```rego\npackage rate\n\nimport rego.v1\n\n# Configuration: 100 requests per principal per 60-second window.\nlimit := 100\nwindow_seconds := 60\n\n# input.now is a unix timestamp in nanoseconds.\n# data.requests[principal] is an array of nanosecond timestamps.\n\ncurrent_window := requests if {\n requests := [t | some t in data.requests[input.principal]; t > input.now - window_seconds * 1000000000]\n}\n\n# Do not name this rule `count`: it would shadow the built-in of the same\n# name, and the call below would resolve to the rule instead.\nrequest_count := count(current_window)\n\nallow if request_count < limit\n\ndeny_reason := sprintf(\n \"rate limit exceeded: %d requests in last %d seconds (limit %d)\",\n [request_count, window_seconds, limit],\n) if not allow\n```\n\n**Pitfalls:**\n- `data.requests` grows unbounded unless the writer prunes outside the\n window. Schedule prune on every write.\n- This pattern is *advisory* -- under load, two concurrent decisions\n can both see `count == limit - 1` and both allow. For strict\n limits, use a Lua/Redis token bucket at the gateway and have OPA\n validate the token, not count requests.\n\n---\n\n## Where these patterns came from\n\nEach is distilled from production policy code. The full Rego files,\ntests, and policy data fixtures live in this server's GitHub\nrepository under `tests/fixtures/policies/`.\n\nFor more patterns, see:\n\n- OPA Playground: https://play.openpolicyagent.org/\n- Awesome OPA: https://github.com/anderseknert/awesome-opa\n- Styra DAS pattern library: https://docs.styra.com/das/policies\n";
8
+ export declare const PATTERNS = "# Rego pattern library\n\nCommon Rego patterns with working examples, tests, and pitfalls. Each\npattern is self-contained -- copy, adapt, and ship.\n\n---\n\n## 1. Role-based access control (RBAC)\n\n**When to use:** the simplest authorization model. Every user has one\nor more roles; each role grants a set of actions on a set of\nresources. Sufficient for ~80% of internal applications.\n\n```rego\npackage rbac\n\nimport rego.v1\n\ndefault allow := false\n\n# Permissions table -- extend as roles evolve.\npermissions := {\n \"admin\": {\"read\", \"write\", \"delete\", \"manage_users\"},\n \"editor\": {\"read\", \"write\"},\n \"viewer\": {\"read\"},\n}\n\nallow if {\n some role in input.user.roles\n input.action in permissions[role]\n}\n\n# Why was it denied? -- useful for audit logs.\ndeny_reasons contains reason if {\n not allow\n input.user\n reason := sprintf(\n \"user %q has roles %v, none grant %q\",\n [input.user.id, input.user.roles, input.action],\n )\n}\n\ndeny_reasons contains \"anonymous request\" if {\n not allow\n not input.user\n}\n```\n\n**Test:**\n\n```rego\npackage rbac_test\n\nimport rego.v1\nimport data.rbac\n\ntest_admin_can_delete if {\n rbac.allow with input as {\n \"user\": {\"id\": \"alice\", \"roles\": [\"admin\"]},\n \"action\": \"delete\",\n }\n}\n\ntest_viewer_cannot_delete if {\n not rbac.allow with input as {\n \"user\": {\"id\": \"bob\", \"roles\": [\"viewer\"]},\n \"action\": \"delete\",\n }\n}\n\ntest_anonymous_denied if {\n not rbac.allow with input as {\"action\": \"read\"}\n reasons := rbac.deny_reasons with input as {\"action\": \"read\"}\n \"anonymous request\" in reasons\n}\n```\n\n**Pitfalls:**\n- The `permissions` table grows unbounded. Move to data files\n (`data.permissions`) once you have more than ~20 roles.\n- Roles overlap with groups in real auth systems; map at the boundary\n rather than carrying both.\n\n---\n\n## 2. Attribute-based access control (ABAC)\n\n**When to use:** when \"who\" alone isn't enough -- decisions also depend\non resource attributes (ownership, tenant, sensitivity) and context\n(time of day, source IP).\n\n```rego\npackage abac\n\nimport rego.v1\n\ndefault allow := false\n\n# Don't show \"secret\" resources to anyone outside the owner's\n# organization, even admins.\n#\n# Written as a guard the permissive rules consult. A second rule\n# assigning `allow := false` is a conflict in Rego, not an override:\n# both rules produce a value for the same document and evaluation\n# fails with eval_conflict_error. Deny wins by being a precondition,\n# not by being written last.\nhidden_from_other_org if {\n input.resource.classification == \"secret\"\n input.resource.org_id != input.user.org_id\n}\n\n# A user can read any resource they own.\nallow if {\n input.action == \"read\"\n input.resource.owner_id == input.user.id\n not hidden_from_other_org\n}\n\n# A user can read shared resources at their organization.\nallow if {\n input.action == \"read\"\n input.resource.shared\n input.resource.org_id == input.user.org_id\n not hidden_from_other_org\n}\n\n# Admins can do anything within their organization.\nallow if {\n \"admin\" in input.user.roles\n input.resource.org_id == input.user.org_id\n not hidden_from_other_org\n}\n```\n\n**Test:**\n\n```rego\npackage abac_test\n\nimport rego.v1\nimport data.abac\n\ntest_owner_reads_own if {\n abac.allow with input as {\n \"action\": \"read\",\n \"user\": {\"id\": \"u1\", \"org_id\": \"o1\"},\n \"resource\": {\"owner_id\": \"u1\", \"org_id\": \"o1\"},\n }\n}\n\ntest_admin_blocked_from_secret_in_other_org if {\n not abac.allow with input as {\n \"action\": \"read\",\n \"user\": {\"id\": \"u1\", \"org_id\": \"o1\", \"roles\": [\"admin\"]},\n \"resource\": {\n \"owner_id\": \"u2\",\n \"org_id\": \"o2\",\n \"classification\": \"secret\",\n },\n }\n}\n```\n\n**Pitfalls:**\n- Multiple `allow` rules combine with logical OR. A later rule\n cannot override an earlier one by assigning a different value:\n two rules producing different values for the same document is an\n `eval_conflict_error` and evaluation fails. Express a denial as a\n condition the permissive rules must pass, as `hidden_from_other_org`\n does above.\n- Don't compute attributes inside the policy; compute them at the\n boundary and pass via `input`.\n\n---\n\n## 3. Kubernetes admission control\n\n**When to use:** validate or mutate Kubernetes resources at admission\ntime. Run as a Gatekeeper, OPA-as-a-webhook, or Kyverno-equivalent\npolicy layer.\n\n```rego\npackage k8s.admission\n\nimport rego.v1\n\n# Reject pods without resource limits.\ndeny contains msg if {\n input.request.kind.kind == \"Pod\"\n container := input.request.object.spec.containers[_]\n not container.resources.limits.memory\n msg := sprintf(\n \"Pod %q container %q is missing resources.limits.memory\",\n [input.request.object.metadata.name, container.name],\n )\n}\n\n# Reject privileged containers in production.\ndeny contains msg if {\n input.request.kind.kind == \"Pod\"\n input.request.namespace != \"kube-system\"\n container := input.request.object.spec.containers[_]\n container.securityContext.privileged == true\n msg := sprintf(\n \"privileged containers are not allowed: %q in %q\",\n [container.name, input.request.object.metadata.name],\n )\n}\n```\n\n**Pitfalls:**\n- Use `input.request.object` for the resource being admitted; the\n envelope shape comes from Kubernetes, not your control.\n- For mutations, return a JSON Patch via the `patch` field. Test\n patches with the actual admission webhook in dry-run mode before\n enforcing.\n- Iteration order is undefined; never rely on `containers[0]` to mean\n anything specific.\n\n---\n\n## 4. Infrastructure-as-Code gates (Terraform)\n\n**When to use:** validate Terraform plans before apply. Catch overly\npermissive IAM, public S3 buckets, missing encryption.\n\n```rego\npackage terraform\n\nimport rego.v1\n\n# Reject S3 buckets without server-side encryption.\ndeny contains msg if {\n resource := input.resource_changes[_]\n resource.type == \"aws_s3_bucket\"\n after := resource.change.after\n not after.server_side_encryption_configuration\n msg := sprintf(\n \"S3 bucket %q has no server-side encryption configured\",\n [resource.address],\n )\n}\n\n# Reject IAM policies with action \"*\" on resource \"*\".\n#\n# AWS accepts either a bare string or an array for Statement, Action\n# and Resource, so each is widened to a set before the wildcard test.\n# Testing the array form alone lets the most common admin policy of\n# all, {\"Action\": \"*\", \"Resource\": \"*\"}, through untouched.\nto_set(v) := {v} if is_string(v)\n\nto_set(v) := {x | some x in v} if is_array(v)\n\nstatements(policy) := to_set(policy.Statement) if is_array(policy.Statement)\n\nstatements(policy) := {policy.Statement} if is_object(policy.Statement)\n\ndeny contains msg if {\n resource := input.resource_changes[_]\n resource.type == \"aws_iam_policy\"\n policy := json.unmarshal(resource.change.after.policy)\n some statement in statements(policy)\n statement.Effect == \"Allow\"\n \"*\" in to_set(statement.Action)\n \"*\" in to_set(statement.Resource)\n msg := sprintf(\"IAM policy %q grants Allow * on *\", [resource.address])\n}\n```\n\n**Pitfalls:**\n- Terraform plan JSON is verbose and version-specific. Pin the\n `terraform plan -json` schema you target.\n- `resource_changes[_].change.after` may be `null` for destroys --\n guard against it.\n- IAM policy documents are string-or-array in several places. Match\n both, or a policy that grants everything slips through.\n- For wide-radius changes, use `opa exec --decision` against a plan\n file in CI, not the live API.\n\n---\n\n## 5. API authorization (HTTP request gating)\n\n**When to use:** at the API gateway / reverse proxy layer, validate\neach request against the caller's identity and the requested\nendpoint.\n\n```rego\npackage api.authz\n\nimport rego.v1\n\ndefault allow := false\n\n# Public endpoints -- no auth required.\npublic_endpoints := {\n {\"method\": \"GET\", \"path\": [\"health\"]},\n {\"method\": \"GET\", \"path\": [\"version\"]},\n}\n\nallow if {\n some endpoint in public_endpoints\n matches_endpoint(endpoint)\n}\n\n# Authenticated reads on resources the user has access to.\nallow if {\n input.method == \"GET\"\n input.user\n user_can_read(input.user, input.path)\n}\n\n# Authenticated writes only with specific scopes.\nallow if {\n input.method in {\"POST\", \"PUT\", \"PATCH\", \"DELETE\"}\n input.user\n \"write\" in input.user.scopes\n user_can_write(input.user, input.path)\n}\n\nmatches_endpoint(spec) if {\n spec.method == input.method\n spec.path == input.path\n}\n\nuser_can_read(user, path) if {\n path[0] == \"users\"\n user.id == path[1]\n}\n\nuser_can_write(user, path) if {\n path[0] == \"users\"\n user.id == path[1]\n}\n```\n\n**Pitfalls:**\n- `input.path` is typically an array (`[\"users\", \"alice\"]`), not a\n string. Build it consistently at the gateway.\n- Path-prefix matching is easy; full pattern matching is not. For\n parameterized routes, decode at the gateway and pass structured\n fields.\n\n---\n\n## 6. Rate limiting (with sliding window data)\n\n**When to use:** allow N requests per principal per window. Lightweight\nlimit enforcement; for high throughput, push to a dedicated rate\nlimiter.\n\n```rego\npackage rate\n\nimport rego.v1\n\n# Configuration: 100 requests per principal per 60-second window.\nlimit := 100\nwindow_seconds := 60\n\n# input.now is a unix timestamp in nanoseconds.\n# data.requests[principal] is an array of nanosecond timestamps.\n\ncurrent_window := requests if {\n requests := [t | some t in data.requests[input.principal]; t > input.now - window_seconds * 1000000000]\n}\n\n# Do not name this rule `count`: it would shadow the built-in of the same\n# name, and the call below would resolve to the rule instead.\nrequest_count := count(current_window)\n\nallow if request_count < limit\n\ndeny_reason := sprintf(\n \"rate limit exceeded: %d requests in last %d seconds (limit %d)\",\n [request_count, window_seconds, limit],\n) if not allow\n```\n\n**Pitfalls:**\n- `data.requests` grows unbounded unless the writer prunes outside the\n window. Schedule prune on every write.\n- This pattern is *advisory* -- under load, two concurrent decisions\n can both see `count == limit - 1` and both allow. For strict\n limits, use a Lua/Redis token bucket at the gateway and have OPA\n validate the token, not count requests.\n\n---\n\n## Where these patterns came from\n\nEach is distilled from production policy code, and this server's\nintegration tests evaluate every pattern against OPA.\n\nFor more patterns, see:\n\n- OPA Playground: https://play.openpolicyagent.org/\n- Awesome OPA: https://github.com/anderseknert/awesome-opa\n- Styra DAS pattern library: https://docs.styra.com/das/policies\n";
9
9
  //# sourceMappingURL=patterns.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/resources/patterns.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,wwTAoXpB,CAAC"}
1
+ {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../src/resources/patterns.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,m/VA+YpB,CAAC"}