@orygn/opa-mcp 0.2.1 → 0.4.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 (45) hide show
  1. package/CHANGELOG.md +91 -3
  2. package/README.md +67 -31
  3. package/dist/config.d.ts +14 -2
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +18 -0
  6. package/dist/config.js.map +1 -1
  7. package/dist/constants.d.ts +1 -1
  8. package/dist/constants.js +1 -1
  9. package/dist/lib/child-env.d.ts +34 -0
  10. package/dist/lib/child-env.d.ts.map +1 -0
  11. package/dist/lib/child-env.js +144 -0
  12. package/dist/lib/child-env.js.map +1 -0
  13. package/dist/lib/conftest-cli.d.ts.map +1 -1
  14. package/dist/lib/conftest-cli.js +1 -0
  15. package/dist/lib/conftest-cli.js.map +1 -1
  16. package/dist/lib/opa-cli.d.ts.map +1 -1
  17. package/dist/lib/opa-cli.js +1 -0
  18. package/dist/lib/opa-cli.js.map +1 -1
  19. package/dist/lib/regal-cli.d.ts.map +1 -1
  20. package/dist/lib/regal-cli.js +1 -0
  21. package/dist/lib/regal-cli.js.map +1 -1
  22. package/dist/lib/rego-smt-encoder.d.ts.map +1 -1
  23. package/dist/lib/rego-smt-encoder.js.map +1 -1
  24. package/dist/lib/subprocess.d.ts +36 -3
  25. package/dist/lib/subprocess.d.ts.map +1 -1
  26. package/dist/lib/subprocess.js +121 -20
  27. package/dist/lib/subprocess.js.map +1 -1
  28. package/dist/lib/tool-helpers.d.ts.map +1 -1
  29. package/dist/lib/tool-helpers.js +19 -5
  30. package/dist/lib/tool-helpers.js.map +1 -1
  31. package/dist/resources/patterns.d.ts +1 -1
  32. package/dist/resources/patterns.d.ts.map +1 -1
  33. package/dist/resources/patterns.js +11 -5
  34. package/dist/resources/patterns.js.map +1 -1
  35. package/dist/tools/authoring/capabilities.js +2 -2
  36. package/dist/tools/authoring/check-schema.js +1 -1
  37. package/dist/tools/authoring/check-schema.js.map +1 -1
  38. package/dist/tools/evaluation/test-multiroot.d.ts.map +1 -1
  39. package/dist/tools/evaluation/test-multiroot.js +9 -10
  40. package/dist/tools/evaluation/test-multiroot.js.map +1 -1
  41. package/dist/tools/evaluation/test.js +13 -3
  42. package/dist/tools/evaluation/test.js.map +1 -1
  43. package/dist/types.d.ts +1 -1
  44. package/dist/types.d.ts.map +1 -1
  45. package/package.json +9 -9
package/CHANGELOG.md CHANGED
@@ -17,6 +17,82 @@ not part of the public surface and may change in minor releases.
17
17
 
18
18
  ## [Unreleased]
19
19
 
20
+ ## [0.4.0] - 2026-09-03
21
+
22
+ ### Security
23
+
24
+ - Policies evaluated through this server no longer receive the server's environment.
25
+ Rego exposes the environment of the `opa` process through `opa.runtime().env`, and
26
+ child processes inherited `process.env`, so any evaluated policy could read
27
+ `OPA_TOKEN`, the `GITHUB_TOKEN` the README asks users to set for
28
+ `rego_playground_share`, and everything else the operator's client passed in.
29
+ `rego_eval` accepts inline source, so `OPA_MCP_ALLOWED_PATHS` never applied and no
30
+ filesystem access was needed; a policy arriving through a README, an issue, or a
31
+ diff was enough. `conftest_test` was affected the same way, and is the likelier
32
+ route to third-party policy. Children now get an explicit allow-list containing no
33
+ secret. `OPA_MCP_PASSTHROUGH_ENV` opts individual variables back in, and anything
34
+ named there is readable by evaluated policy by design.
35
+
36
+ On Windows, libuv copies a fixed set of variables to every child regardless of what
37
+ is requested. `USERNAME`, `USERDOMAIN` and `LOGONSERVER` cannot be removed, so they
38
+ are blanked rather than left to disclose the operating-system user and domain.
39
+
40
+ ### Fixed
41
+
42
+ - A subprocess that produces very large output no longer takes the server down.
43
+ stdout and stderr were captured without a size limit, and decoding a capture past
44
+ V8's maximum string length throws inside an async `close` handler, where no tool's
45
+ `try`/`catch` can reach it and the process exits. A policy iterating
46
+ `numbers.range(1, 1000)` under `--explain full` produced 518 MiB in under seven
47
+ seconds, so the 30-second timeout never applied; `opa` buffers its result and writes
48
+ it in one burst at exit, which means the runs that complete comfortably are the
49
+ dangerous ones. Capture is now capped per stream, the child is stopped on overflow,
50
+ and the tool returns the new `OUTPUT_TOO_LARGE` error code. Configurable with
51
+ `OPA_MCP_MAX_SUBPROCESS_BYTES`, default 32 MiB.
52
+
53
+ - `rego_test_multiroot` reports `OUTPUT_TOO_LARGE` and `TIMEOUT` through the same mapper as
54
+ every other tool. It carried its own copy of the failure ladder, and a run killed for
55
+ producing too much output reported `OPA_BINARY_NOT_FOUND`, sending the caller after an
56
+ install problem that did not exist. This is the same defect that was fixed for timeouts in
57
+ 0.3.0, in the one place that did not share the fix.
58
+
59
+ ### Added
60
+
61
+ - `OPA_MCP_MAX_SUBPROCESS_BYTES` and `OPA_MCP_PASSTHROUGH_ENV` environment variables.
62
+ - `OUTPUT_TOO_LARGE` error code.
63
+
64
+
65
+ ## [0.3.0] - 2026-08-05
66
+
67
+ ### Changed
68
+
69
+ - The bundled OPA binary moves from 0.69.0 to 1.19.0. This is a breaking change for
70
+ anyone relying on the bundle. OPA 1.0 made Rego v1 the default, so rule bodies
71
+ require the `if` keyword and partial set rules require `contains`; policies written
72
+ against OPA 0.x no longer parse. `rego_migrate_v1` converts them. Installs that
73
+ supply their own binary through `OPA_BINARY` or `PATH` are unaffected, since the
74
+ bundled copy is only a fallback. The Docker image ships the same version.
75
+
76
+ ### Fixed
77
+
78
+ - `rego_test` returns `INVALID_REGO` when the policies under test fail to load. It
79
+ previously reported a successful run with zero tests, so a caller whose policies had
80
+ stopped compiling was told the suite was fine. Most likely to surface right after
81
+ this upgrade, on trees that still hold v0 policies.
82
+
83
+ - A subprocess that exceeds the configured timeout returns `TIMEOUT` instead of
84
+ `OPA_BINARY_NOT_FOUND`. A killed process reports a null exit code, and the failure
85
+ mappers checked that before the timeout flag, so a slow command looked like a missing
86
+ install. Affected every CLI-backed tool, and `rego_test_multiroot` separately.
87
+
88
+ - Three Rego snippets in the bundled pattern library did not compile: two failed to
89
+ parse, and the rate-limiting example named a rule `count`, which shadowed the built-in
90
+ it then called. All shipped snippets are now compiled against the bundled OPA in CI.
91
+
92
+ - Corrected the tool list on the Docker Hub page, which named seven tools that do not
93
+ exist (`rego_bundle_*`, `rego_parse`, `rego_profile`, `rego_compile`) and undercounted
94
+ the total. Added the missing `rego_playground_share` entry to the README tool tables.
95
+
20
96
  ## [0.2.1] - 2026-06-25
21
97
 
22
98
  ### Fixed
@@ -952,7 +1028,19 @@ wrappers end-to-end. CI matrix: Ubuntu, macOS, and Windows on Node
952
1028
  20 and 22, plus CodeQL security scanning and weekly Dependabot updates
953
1029
  for npm, GitHub Actions, and Docker base images.
954
1030
 
955
- [Unreleased]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.12...HEAD
1031
+ [Unreleased]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.4.0...HEAD
1032
+ [0.4.0]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.3.0...v0.4.0
1033
+ [0.3.0]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.2.1...v0.3.0
1034
+ [0.2.1]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.2.0...v0.2.1
1035
+ [0.2.0]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.20...v0.2.0
1036
+ [0.1.20]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.19...v0.1.20
1037
+ [0.1.19]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.18...v0.1.19
1038
+ [0.1.18]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.17...v0.1.18
1039
+ [0.1.17]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.16...v0.1.17
1040
+ [0.1.16]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.15...v0.1.16
1041
+ [0.1.15]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.14...v0.1.15
1042
+ [0.1.14]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.13...v0.1.14
1043
+ [0.1.13]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.12...v0.1.13
956
1044
  [0.1.12]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.11...v0.1.12
957
1045
  [0.1.11]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.10...v0.1.11
958
1046
  [0.1.10]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.9...v0.1.10
@@ -963,6 +1051,6 @@ for npm, GitHub Actions, and Docker base images.
963
1051
  [0.1.5]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.4...v0.1.5
964
1052
  [0.1.4]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.3...v0.1.4
965
1053
  [0.1.3]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.2...v0.1.3
966
- [0.1.2]: https://github.com/OrygnsCode/opa-mcp-server/releases/tag/v0.1.2
967
- [0.1.1]: https://github.com/OrygnsCode/opa-mcp-server/releases/tag/v0.1.1
1054
+ [0.1.2]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.1...v0.1.2
1055
+ [0.1.1]: https://github.com/OrygnsCode/opa-mcp-server/compare/v0.1.0...v0.1.1
968
1056
  [0.1.0]: https://github.com/OrygnsCode/opa-mcp-server/releases/tag/v0.1.0
package/README.md CHANGED
@@ -22,10 +22,20 @@ environment.
22
22
  +--------------------+ 52 tools +-----------------+ +---------------------+
23
23
  ```
24
24
 
25
- > **Status:** v0.2.1. Tool surface, error codes, and
25
+ > **Status:** v0.4.0. Tool surface, error codes, and
26
26
  > environment variables follow [SemVer](https://semver.org/) from
27
27
  > v0.1.0 forward.
28
28
 
29
+ > **Upgrading to 0.4.0:** subprocesses no longer inherit the server's
30
+ > environment. A policy that read a variable through `opa.runtime().env`
31
+ > will no longer see it; name the variable in `OPA_MCP_PASSTHROUGH_ENV` if
32
+ > it is genuinely needed. See the security section for why.
33
+
34
+ > **Upgrading to 0.3.0:** the bundled OPA is now 1.19, so Rego v0 policies
35
+ > no longer parse (`if` is required before a rule body, `contains` before a
36
+ > partial set). Run `rego_migrate_v1` to convert them. If you supply your own
37
+ > binary via `OPA_BINARY` or `PATH`, nothing changes.
38
+
29
39
  ---
30
40
 
31
41
  ## Table of contents
@@ -225,8 +235,9 @@ which opa && which regal # macOS / Linux
225
235
  Get-Command opa, regal | Select-Object Source # Windows
226
236
  ```
227
237
 
228
- This does not affect the **Docker** or **MCPB** install paths; those
229
- ship `opa` and `regal` inside the bundle and bypass `PATH` entirely.
238
+ This does not affect the **Docker** install path, which ships `opa` and
239
+ `regal` in the image and bypasses `PATH` entirely. The **MCPB** bundle
240
+ carries neither and resolves `opa` the same way the npm install does.
230
241
  See [Troubleshooting](#troubleshooting) for full detail.
231
242
 
232
243
  ## Configuration
@@ -235,20 +246,22 @@ The server reads its configuration from environment variables. Every
235
246
  variable is optional; defaults are sensible for a local OPA on
236
247
  `http://localhost:8181`.
237
248
 
238
- | Variable | Default | Purpose |
239
- | ---------------------------- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
240
- | `OPA_URL` | `http://localhost:8181` | Base URL of an OPA REST endpoint, used by `opa_*` tools. |
241
- | `OPA_TOKEN` | _(unset)_ | Bearer token for OPA, if your instance requires auth. Treated as a secret. Never echoed in logs or tool responses. |
242
- | `OPA_BINARY` | `opa` (on `PATH`) | Path to the `opa` CLI, used by `rego_*` tools. |
243
- | `REGAL_BINARY` | `regal` (on `PATH`) | Path to the `regal` linter. Only required by `rego_lint`. |
244
- | `CONFTEST_BINARY` | `conftest` (on `PATH`) | Path to the `conftest` binary. Only required by `conftest_*` tools. Returns `CONFTEST_NOT_FOUND` if absent. |
245
- | `OPA_MCP_ALLOWED_PATHS` | _(unset)_ | Comma- or semicolon-separated list of directories the server is allowed to read policies from. **When unset, file-based tools refuse to read from disk.** |
246
- | `OPA_MCP_LOG_FILE` | `<tmpdir>/orygn-opa-mcp.log` | Path the server appends logs to. The server never writes to stdout; that channel is reserved for the MCP protocol. |
247
- | `OPA_MCP_LOG_LEVEL` | `info` | One of `debug`, `info`, `warn`, `error`. |
248
- | `OPA_MCP_MAX_RESPONSE_BYTES` | `100000` | Hard cap on a single tool response. Larger payloads are truncated with a `__truncated: true` marker. |
249
- | `OPA_MCP_TIMEOUT_MS` | `30000` | Hard timeout for any spawned subprocess (`opa`, `regal`). After this, the child gets `SIGTERM` and then `SIGKILL`. |
250
- | `OPA_MCP_HTTP_TIMEOUT_MS` | `15000` | Timeout for HTTP requests to the OPA REST API. |
251
- | `OPA_MCP_NO_TELEMETRY` | _(unset)_ | Set to `1` to disable the anonymous startup ping. The ping sends the server version, OS platform, and a random install ID. The install ID is stored at `~/.orygn/opa-mcp/install-id` and is generated once on first run. No policy content or file paths are ever sent. |
249
+ | Variable | Default | Purpose |
250
+ | ------------------------------ | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
251
+ | `OPA_URL` | `http://localhost:8181` | Base URL of an OPA REST endpoint, used by `opa_*` tools. |
252
+ | `OPA_TOKEN` | _(unset)_ | Bearer token for OPA, if your instance requires auth. Treated as a secret. Never echoed in logs or tool responses. |
253
+ | `OPA_BINARY` | `opa` (on `PATH`) | Path to the `opa` CLI, used by `rego_*` tools. |
254
+ | `REGAL_BINARY` | `regal` (on `PATH`) | Path to the `regal` linter. Required by `rego_lint`, `rego_fix`, and `rego_security_audit`. |
255
+ | `CONFTEST_BINARY` | `conftest` (on `PATH`) | Path to the `conftest` binary. Only required by `conftest_*` tools. Returns `CONFTEST_NOT_FOUND` if absent. |
256
+ | `OPA_MCP_ALLOWED_PATHS` | _(unset)_ | Comma- or semicolon-separated list of directories the server is allowed to read policies from. **When unset, file-based tools refuse to read from disk.** |
257
+ | `OPA_MCP_LOG_FILE` | `<tmpdir>/orygn-opa-mcp.log` | Path the server appends logs to. The server never writes to stdout; that channel is reserved for the MCP protocol. |
258
+ | `OPA_MCP_LOG_LEVEL` | `info` | One of `debug`, `info`, `warn`, `error`. |
259
+ | `OPA_MCP_MAX_RESPONSE_BYTES` | `100000` | Hard cap on a single tool response. Larger payloads are truncated with a `__truncated: true` marker. |
260
+ | `OPA_MCP_TIMEOUT_MS` | `30000` | Hard timeout for any spawned subprocess (`opa`, `regal`). After this, the child gets `SIGTERM` and then `SIGKILL`. |
261
+ | `OPA_MCP_HTTP_TIMEOUT_MS` | `15000` | Timeout for HTTP requests to the OPA REST API. |
262
+ | `OPA_MCP_NO_TELEMETRY` | _(unset)_ | Set to `1` to disable the anonymous startup ping. The ping sends the server version, OS platform, and a random install ID. The install ID is stored at `~/.orygn/opa-mcp/install-id` and is generated once on first run. No policy content or file paths are ever sent. |
263
+ | `OPA_MCP_MAX_SUBPROCESS_BYTES` | `33554432` (32 MiB) | Maximum bytes captured from a subprocess's stdout and stderr, counted separately. On overflow the stream is clamped, the child is stopped, and the tool returns `OUTPUT_TOO_LARGE`. Distinct from `OPA_MCP_MAX_RESPONSE_BYTES`, which trims the reply after the output is already in memory. |
264
+ | `OPA_MCP_PASSTHROUGH_ENV` | _(unset)_ | Comma-separated variable names to pass through to `opa`, `regal` and `conftest`. Everything else is withheld. **Anything named here is readable by any policy the server evaluates**, via `opa.runtime().env`, so use it only for values that are safe in that position. |
252
265
 
253
266
  Paths in `OPA_MCP_ALLOWED_PATHS` and the `*_BINARY` variables must be
254
267
  absolute. Relative paths and missing binaries are rejected with structured
@@ -269,7 +282,8 @@ Stable error codes: `INVALID_INPUT`, `INVALID_REGO`, `INVALID_BUNDLE`,
269
282
  `OPA_AUTH_FAILED`, `POLICY_NOT_FOUND`, `DATA_NOT_FOUND`, `PATH_NOT_ALLOWED`,
270
283
  `PATH_NOT_FOUND`, `DEPENDENCY_CONFLICT`, `NO_TESTS_FOUND`,
271
284
  `COVERAGE_BELOW_THRESHOLD`, `OPA_VERSION_UNSUPPORTED`, `VERIFY_INCONCLUSIVE`,
272
- `Z3_INIT_ERROR`, `GITHUB_TOKEN_MISSING`, `GIST_CREATE_FAILED`, `TIMEOUT`,
285
+ `Z3_INIT_ERROR`, `GITHUB_TOKEN_MISSING`, `GIST_CREATE_FAILED`,
286
+ `OUTPUT_TOO_LARGE`, `TIMEOUT`,
273
287
  `CANCELLED`, `UNKNOWN_ERROR`.
274
288
 
275
289
  ### Category A: Authoring & static analysis
@@ -295,7 +309,7 @@ Operate on Rego source code without needing a running OPA server. Wrap
295
309
  ```jsonc
296
310
  // Input
297
311
  {
298
- "source": "package x\nallow{input.user==\"admin\"}"
312
+ "source": "package x\nallow if input.user==\"admin\""
299
313
  }
300
314
 
301
315
  // Output (ok)
@@ -419,6 +433,7 @@ the tasks agents are actually asked to do.
419
433
  | `rego_policy_diff` | Evaluate the same query against two policies in parallel and compare the results. Returns `equal: true/false`, the raw value from each side (`resultA`/`resultB`), and `changedPaths` -- dot/bracket JSON paths that differ. Each side takes inline source or a file/directory path. Useful for verifying refactor equivalence or mapping divergence between two policy versions. |
420
434
  | `rego_verify` | Formally verify a property about a Rego rule using SMT solving (Microsoft Z3 via WASM). Unlike testing, this checks ALL possible inputs mathematically and either proves the property holds or returns a concrete counterexample. Supports `always_true`, `never_true`, and `satisfiable` property kinds. Handles equality, comparison, string built-ins (`startswith`, `endswith`, `contains`, `regex.match`), multi-clause rules, and cross-rule inlining. Reports `INCONCLUSIVE` for negation-as-failure and comprehensions. |
421
435
  | `rego_explain_undefined` | Explain why a Rego query is undefined. Combines a plain eval, a full-trace eval, and per-condition AST analysis to identify the exact body expression blocking each rule. Returns a structured breakdown of which conditions blocked each rule plus a human-readable summary. |
436
+ | `rego_playground_share` | Publish a policy (and optional input) as a secret GitHub Gist and return the link, for sharing a reproduction. Requires `GITHUB_TOKEN` with the `gist` scope; returns `GITHUB_TOKEN_MISSING` otherwise. |
422
437
 
423
438
  ### Category F: Conftest (configuration policy testing)
424
439
 
@@ -440,7 +455,7 @@ YAML/JSON/HCL/TOML/INI against Rego policies using
440
455
  // Input
441
456
  {
442
457
  "inlineConfig": "apiVersion: v1\nkind: Pod\nspec:\n containers:\n - name: app\n image: nginx:latest",
443
- "inlinePolicy": "package main\ndeny[msg] { input.spec.containers[_].image == \"nginx:latest\"; msg := \"pin your image tag\" }"
458
+ "inlinePolicy": "package main\ndeny contains msg if { input.spec.containers[_].image == \"nginx:latest\"; msg := \"pin your image tag\" }"
444
459
  }
445
460
 
446
461
  // Output
@@ -566,18 +581,28 @@ Agent: Done. Policy `rbac` is live on staging at $OPA_URL.
566
581
  └───────────────────────────────────────────────────────────────────────────────────────┘
567
582
  ```
568
583
 
569
- Three things worth knowing if you're going to operate this:
584
+ Four things worth knowing if you're going to operate this:
570
585
 
571
586
  1. **stdout is the protocol channel.** The server logs to a file via
572
587
  `lib/logger.ts` and never writes to stdout. If you see stray stdout
573
588
  bytes, the client disconnects; the MCP transport layer is strict.
574
589
  2. **No tool throws.** Every tool catches its own exceptions and returns
575
590
  a structured `{ ok: false, error: ... }` envelope. The agent sees a
576
- stable error vocabulary, not a stack trace.
577
- 3. **Subprocesses are tightly bounded.** `lib/subprocess.ts` runs `opa`
578
- and `regal` with `shell: false`, a hard timeout, and `SIGTERM`-then-
579
- `SIGKILL` escalation. There is no path through the server where an
580
- agent can construct a shell command.
591
+ stable error vocabulary, not a stack trace. Decoding subprocess output
592
+ happens inside an async callback, where a throw would bypass those
593
+ handlers entirely, so that path is bounded by bytes rather than left to
594
+ a `try`/`catch` that could not see it.
595
+ 3. **Subprocesses are bounded in time, size, and environment.**
596
+ `lib/subprocess.ts` runs the binaries with `shell: false`, a hard
597
+ timeout with `SIGTERM`-then-`SIGKILL` escalation, and a per-stream byte
598
+ cap. There is no path through the server where an agent can construct a
599
+ shell command. The timeout alone is not enough: `opa` buffers a result
600
+ in memory and writes it in one burst at exit, so a command that finishes
601
+ well inside the timeout can still deliver hundreds of megabytes.
602
+ 4. **Children do not inherit the server's environment.** `lib/child-env.ts`
603
+ builds an explicit allow-list instead. Rego can read its interpreter's
604
+ environment through `opa.runtime().env`, so anything passed down is
605
+ readable by any policy the server evaluates.
581
606
 
582
607
  ## Security
583
608
 
@@ -587,8 +612,18 @@ be exposed on the network.
587
612
 
588
613
  - File-based tools refuse to read anything outside `OPA_MCP_ALLOWED_PATHS`.
589
614
  When that variable is unset, file tools return `PATH_NOT_ALLOWED`.
590
- - Subprocesses run with `shell: false` and a hard timeout.
591
- - `OPA_TOKEN` is never echoed in tool responses or log entries.
615
+ - Subprocesses run with `shell: false`, a hard timeout, and a byte cap on
616
+ captured output.
617
+ - **Evaluated policy cannot read the server's environment.** Rego exposes the
618
+ environment of the `opa` process through `opa.runtime().env`, so a child that
619
+ inherited `process.env` would hand `OPA_TOKEN`, `GITHUB_TOKEN`, and every
620
+ other variable to any policy it evaluated. Since `rego_eval` accepts inline
621
+ source, no filesystem access is needed to reach that, which puts it one
622
+ prompt injection away from any untrusted Rego an agent reads. Children get an
623
+ explicit allow-list instead (`lib/child-env.ts`), containing no secret.
624
+ `OPA_MCP_PASSTHROUGH_ENV` opts individual variables back in.
625
+ - `OPA_TOKEN` is never echoed in tool responses or log entries, and is not
626
+ passed to any child process.
592
627
  - Releases are published with
593
628
  [npm provenance](https://docs.npmjs.com/generating-provenance-statements);
594
629
  the Docker image is built reproducibly from the committed `Dockerfile`.
@@ -633,8 +668,9 @@ is not enough -- the spawned server gets a reduced `PATH`. The
633
668
  [`examples/`](./examples) configs already include these env vars; just
634
669
  edit the placeholder paths.
635
670
 
636
- This issue does **not** affect the Docker or MCPB install paths. Those
637
- bundle `opa` and `regal` and bypass `PATH` entirely.
671
+ This issue does **not** affect the Docker install path, which bundles
672
+ `opa` and `regal` and bypasses `PATH` entirely. The MCPB bundle resolves
673
+ `opa` from `OPA_BINARY` or `PATH`, so it can hit this.
638
674
 
639
675
  **The server starts, then the client says "disconnected."**
640
676
 
@@ -715,7 +751,7 @@ Breaking changes will be:
715
751
 
716
752
  Pinned versions of the upstream toolchain (`opa` and `regal`) are treated
717
753
  as part of the build, not as a dependency the operator manages. The
718
- Dockerfile, MCPB bundle, and CI all use the same pin; bumps go through
754
+ Dockerfile and CI use the same pin; bumps go through
719
755
  Dependabot or a manual PR.
720
756
 
721
757
  ## License
package/dist/config.d.ts CHANGED
@@ -31,6 +31,16 @@ declare const ConfigSchema: z.ZodObject<{
31
31
  * write to a file path the agent specifies.
32
32
  */
33
33
  maxResponseBytes: z.ZodDefault<z.ZodNumber>;
34
+ /**
35
+ * Maximum bytes captured from a subprocess's stdout and stderr, each counted
36
+ * separately. On overflow the stream is clamped and the child is killed.
37
+ *
38
+ * This is a memory bound, distinct from `maxResponseBytes`, which trims the
39
+ * response after the output has already been read into the heap. A capture
40
+ * past V8's max string length cannot be decoded at all, and the throw would
41
+ * land in an async callback where no tool can catch it.
42
+ */
43
+ maxSubprocessBytes: z.ZodDefault<z.ZodNumber>;
34
44
  }, "strip", z.ZodTypeAny, {
35
45
  opaUrl: string;
36
46
  opaBinary: string;
@@ -40,8 +50,9 @@ declare const ConfigSchema: z.ZodObject<{
40
50
  httpTimeoutMs: number;
41
51
  allowedPaths: string[];
42
52
  logFile: string;
43
- logLevel: "debug" | "info" | "warn" | "error";
53
+ logLevel: "error" | "debug" | "info" | "warn";
44
54
  maxResponseBytes: number;
55
+ maxSubprocessBytes: number;
45
56
  opaToken?: string | undefined;
46
57
  }, {
47
58
  opaUrl?: string | undefined;
@@ -53,8 +64,9 @@ declare const ConfigSchema: z.ZodObject<{
53
64
  httpTimeoutMs?: number | undefined;
54
65
  allowedPaths?: string[] | undefined;
55
66
  logFile?: string | undefined;
56
- logLevel?: "debug" | "info" | "warn" | "error" | undefined;
67
+ logLevel?: "error" | "debug" | "info" | "warn" | undefined;
57
68
  maxResponseBytes?: number | undefined;
69
+ maxSubprocessBytes?: number | undefined;
58
70
  }>;
59
71
  export type Config = z.infer<typeof ConfigSchema>;
60
72
  export declare function loadConfig(): Config;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,QAAA,MAAM,YAAY;IAChB,wEAAwE;;IASxE,2EAA2E;;IAG3E,2DAA2D;;IAG3D,+DAA+D;;IAG/D,qEAAqE;;IAGrE,kEAAkE;;IAGlE,mDAAmD;;IAGnD;;;;;OAKG;;IAGH,uEAAuE;;IAGvE,qCAAqC;;IAGrC;;;;OAIG;;;;;;;;;;;;;;;;;;;;;;;;;;EAEH,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAyBlD,wBAAgB,UAAU,IAAI,MAAM,CAqCnC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,QAAA,MAAM,YAAY;IAChB,wEAAwE;;IASxE,2EAA2E;;IAG3E,2DAA2D;;IAG3D,+DAA+D;;IAG/D,qEAAqE;;IAGrE,kEAAkE;;IAGlE,mDAAmD;;IAGnD;;;;;OAKG;;IAGH,uEAAuE;;IAGvE,qCAAqC;;IAGrC;;;;OAIG;;IAGH;;;;;;;;OAQG;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUH,CAAC;AAEH,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AA0BlD,wBAAgB,UAAU,IAAI,MAAM,CAsCnC"}
package/dist/config.js CHANGED
@@ -9,6 +9,7 @@ import { tmpdir } from 'node:os';
9
9
  import { join } from 'node:path';
10
10
  import { z } from 'zod';
11
11
  import { resolveOpaBinary } from './lib/resolve-binary.js';
12
+ import { DEFAULT_MAX_OUTPUT_BYTES } from './lib/subprocess.js';
12
13
  const ConfigSchema = z.object({
13
14
  /** Base URL of a running OPA server (used by `opa_*` runtime tools). */
14
15
  opaUrl: z
@@ -47,6 +48,21 @@ const ConfigSchema = z.object({
47
48
  * write to a file path the agent specifies.
48
49
  */
49
50
  maxResponseBytes: z.coerce.number().int().positive().default(100_000),
51
+ /**
52
+ * Maximum bytes captured from a subprocess's stdout and stderr, each counted
53
+ * separately. On overflow the stream is clamped and the child is killed.
54
+ *
55
+ * This is a memory bound, distinct from `maxResponseBytes`, which trims the
56
+ * response after the output has already been read into the heap. A capture
57
+ * past V8's max string length cannot be decoded at all, and the throw would
58
+ * land in an async callback where no tool can catch it.
59
+ */
60
+ maxSubprocessBytes: z.coerce
61
+ .number()
62
+ .int()
63
+ .positive()
64
+ .max(DEFAULT_MAX_OUTPUT_BYTES * 8, 'OPA_MCP_MAX_SUBPROCESS_BYTES is too large to decode safely; keep it well under 512 MiB.')
65
+ .default(DEFAULT_MAX_OUTPUT_BYTES),
50
66
  });
51
67
  function parseAllowedPaths(raw) {
52
68
  if (!raw)
@@ -69,6 +85,7 @@ const ENV_VAR_NAMES = {
69
85
  logFile: 'OPA_MCP_LOG_FILE',
70
86
  logLevel: 'OPA_MCP_LOG_LEVEL',
71
87
  maxResponseBytes: 'OPA_MCP_MAX_RESPONSE_BYTES',
88
+ maxSubprocessBytes: 'OPA_MCP_MAX_SUBPROCESS_BYTES',
72
89
  };
73
90
  export function loadConfig() {
74
91
  const allowedPaths = parseAllowedPaths(process.env['OPA_MCP_ALLOWED_PATHS']);
@@ -84,6 +101,7 @@ export function loadConfig() {
84
101
  logFile: process.env['OPA_MCP_LOG_FILE'],
85
102
  logLevel: process.env['OPA_MCP_LOG_LEVEL'],
86
103
  maxResponseBytes: process.env['OPA_MCP_MAX_RESPONSE_BYTES'],
104
+ maxSubprocessBytes: process.env['OPA_MCP_MAX_SUBPROCESS_BYTES'],
87
105
  });
88
106
  if (!parsed.success) {
89
107
  console.error('opa-mcp: invalid configuration');
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -4,5 +4,5 @@
4
4
  * (server.ts -> tools -> meta -> server.ts).
5
5
  */
6
6
  export declare const SERVER_NAME = "orygn-opa-mcp";
7
- export declare const SERVER_VERSION = "0.2.1";
7
+ export declare const SERVER_VERSION = "0.4.0";
8
8
  //# sourceMappingURL=constants.d.ts.map
package/dist/constants.js CHANGED
@@ -4,5 +4,5 @@
4
4
  * (server.ts -> tools -> meta -> server.ts).
5
5
  */
6
6
  export const SERVER_NAME = 'orygn-opa-mcp';
7
- export const SERVER_VERSION = '0.2.1';
7
+ export const SERVER_VERSION = '0.4.0';
8
8
  //# sourceMappingURL=constants.js.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Builds the environment handed to `opa`, `regal` and `conftest`.
3
+ *
4
+ * Rego can read its interpreter's environment: `opa.runtime().env` returns the
5
+ * whole thing. So every variable this server passes down is readable by any
6
+ * policy it evaluates, and `rego_eval` takes inline source -- no filesystem
7
+ * access, so `OPA_MCP_ALLOWED_PATHS` never applies. Untrusted Rego reaches an
8
+ * agent through a README, an issue, or a pull request diff, which makes a
9
+ * pass-through of `process.env` a prompt injection away from handing over
10
+ * `OPA_TOKEN`, the `GITHUB_TOKEN` this project's own README asks users to put in
11
+ * their client config, and whatever else the operator's shell happens to hold.
12
+ *
13
+ * So the child gets an explicit allow-list instead. Nothing on it is a secret.
14
+ * Measured against the bundled OPA 1.19.0, `version`, `eval`, `check`, `test`
15
+ * and `build` all succeed with a completely empty environment, so the entries
16
+ * below exist for correctness in real-world setups (proxies, custom CA bundles,
17
+ * tool config discovery), not to make the binaries run.
18
+ *
19
+ * Operators who genuinely need a variable in policy can name it in
20
+ * `OPA_MCP_PASSTHROUGH_ENV`, which is opt-in precisely because it undoes this.
21
+ */
22
+ /**
23
+ * Build the child environment: the allow-list, plus any opted-in passthrough,
24
+ * plus `extra` from the caller (which wins, since it is explicit intent).
25
+ *
26
+ * @param extra Variables the calling command needs. Readable by evaluated
27
+ * policy like everything else here, so pass secrets only when the
28
+ * command actually requires them.
29
+ * @param source Environment to read from. Injectable for testing.
30
+ */
31
+ export declare function buildChildEnv(extra?: Record<string, string>, source?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
32
+ /** Exposed for tests and for documenting the surface. */
33
+ export declare const ALLOWED_CHILD_ENV_VARS: readonly string[];
34
+ //# sourceMappingURL=child-env.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-env.d.ts","sourceRoot":"","sources":["../../src/lib/child-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA0GH;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,MAAM,GAAE,MAAM,CAAC,UAAwB,GACtC,MAAM,CAAC,UAAU,CAanB;AAED,yDAAyD;AACzD,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAAY,CAAC"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Builds the environment handed to `opa`, `regal` and `conftest`.
3
+ *
4
+ * Rego can read its interpreter's environment: `opa.runtime().env` returns the
5
+ * whole thing. So every variable this server passes down is readable by any
6
+ * policy it evaluates, and `rego_eval` takes inline source -- no filesystem
7
+ * access, so `OPA_MCP_ALLOWED_PATHS` never applies. Untrusted Rego reaches an
8
+ * agent through a README, an issue, or a pull request diff, which makes a
9
+ * pass-through of `process.env` a prompt injection away from handing over
10
+ * `OPA_TOKEN`, the `GITHUB_TOKEN` this project's own README asks users to put in
11
+ * their client config, and whatever else the operator's shell happens to hold.
12
+ *
13
+ * So the child gets an explicit allow-list instead. Nothing on it is a secret.
14
+ * Measured against the bundled OPA 1.19.0, `version`, `eval`, `check`, `test`
15
+ * and `build` all succeed with a completely empty environment, so the entries
16
+ * below exist for correctness in real-world setups (proxies, custom CA bundles,
17
+ * tool config discovery), not to make the binaries run.
18
+ *
19
+ * Operators who genuinely need a variable in policy can name it in
20
+ * `OPA_MCP_PASSTHROUGH_ENV`, which is opt-in precisely because it undoes this.
21
+ */
22
+ /** Vars every platform benefits from. Locale keeps output formatting stable. */
23
+ const COMMON = ['PATH', 'LANG', 'LC_ALL', 'LC_CTYPE', 'LC_NUMERIC', 'TZ'];
24
+ /** POSIX home and temp. Regal and Conftest look under HOME for config. */
25
+ const POSIX = ['HOME', 'TMPDIR', 'SHELL', 'USER', 'LOGNAME'];
26
+ /**
27
+ * Windows equivalents. A Go binary starts without these, but process creation
28
+ * and temp-file handling are better behaved with them present.
29
+ */
30
+ const WINDOWS = [
31
+ 'PATHEXT',
32
+ 'SystemRoot',
33
+ 'SystemDrive',
34
+ 'COMSPEC',
35
+ 'windir',
36
+ 'TEMP',
37
+ 'TMP',
38
+ 'USERPROFILE',
39
+ 'HOMEDRIVE',
40
+ 'HOMEPATH',
41
+ 'APPDATA',
42
+ 'LOCALAPPDATA',
43
+ 'PROCESSOR_ARCHITECTURE',
44
+ 'NUMBER_OF_PROCESSORS',
45
+ ];
46
+ /**
47
+ * Proxy and TLS trust. Without these, `http.send`, bundle downloads and remote
48
+ * schema fetches fail behind a corporate proxy or a custom CA.
49
+ *
50
+ * A proxy URL can embed credentials, so these are the least inert entries here.
51
+ * They are included because breaking every corporate user is the worse trade,
52
+ * and they are the operator's own infrastructure credentials rather than the
53
+ * cloud and repository tokens that motivated this module.
54
+ */
55
+ const NETWORK = [
56
+ 'HTTP_PROXY',
57
+ 'HTTPS_PROXY',
58
+ 'NO_PROXY',
59
+ 'ALL_PROXY',
60
+ 'http_proxy',
61
+ 'https_proxy',
62
+ 'no_proxy',
63
+ 'all_proxy',
64
+ 'SSL_CERT_FILE',
65
+ 'SSL_CERT_DIR',
66
+ ];
67
+ /**
68
+ * Where the tools look for their own config and credentials. These name file
69
+ * locations; they are not themselves secrets. Conftest reads registry auth from
70
+ * the Docker config file, so `conftest_pull` and `conftest_push` need these.
71
+ */
72
+ const TOOL_CONFIG = [
73
+ 'XDG_CONFIG_HOME',
74
+ 'XDG_CACHE_HOME',
75
+ 'XDG_DATA_HOME',
76
+ 'DOCKER_CONFIG',
77
+ 'REGISTRY_AUTH_FILE',
78
+ ];
79
+ const ALLOWED = [...COMMON, ...POSIX, ...WINDOWS, ...NETWORK, ...TOOL_CONFIG];
80
+ /** Names an operator opted into via OPA_MCP_PASSTHROUGH_ENV. */
81
+ function passthroughNames(source) {
82
+ const raw = source['OPA_MCP_PASSTHROUGH_ENV'];
83
+ if (!raw)
84
+ return [];
85
+ return raw
86
+ .split(/[;,]/)
87
+ .map((s) => s.trim())
88
+ .filter((s) => s.length > 0);
89
+ }
90
+ /**
91
+ * Windows treats variable names case-insensitively, and the casing in
92
+ * `process.env` follows whatever the parent used, so an exact-key lookup would
93
+ * silently miss `Path` where the list says `PATH`.
94
+ */
95
+ function lookup(source, name) {
96
+ const direct = source[name];
97
+ if (direct !== undefined)
98
+ return direct;
99
+ if (process.platform !== 'win32')
100
+ return undefined;
101
+ const wanted = name.toLowerCase();
102
+ for (const key of Object.keys(source)) {
103
+ if (key.toLowerCase() === wanted)
104
+ return source[key];
105
+ }
106
+ return undefined;
107
+ }
108
+ /**
109
+ * Windows-only identity variables that libuv copies from the parent no matter
110
+ * what environment is requested -- spawning with `{}` still delivers them, and
111
+ * measurably so. They are not secrets, but they name the operating-system user,
112
+ * the Windows domain and the domain controller, which is more than an untrusted
113
+ * policy needs to know about the machine evaluating it.
114
+ *
115
+ * They cannot be removed, only overwritten, so they are blanked. None of the
116
+ * three binaries reads them: every representative `opa` command succeeds with a
117
+ * completely empty environment on both platforms.
118
+ */
119
+ const WINDOWS_IDENTITY_TO_BLANK = ['USERNAME', 'USERDOMAIN', 'LOGONSERVER'];
120
+ /**
121
+ * Build the child environment: the allow-list, plus any opted-in passthrough,
122
+ * plus `extra` from the caller (which wins, since it is explicit intent).
123
+ *
124
+ * @param extra Variables the calling command needs. Readable by evaluated
125
+ * policy like everything else here, so pass secrets only when the
126
+ * command actually requires them.
127
+ * @param source Environment to read from. Injectable for testing.
128
+ */
129
+ export function buildChildEnv(extra, source = process.env) {
130
+ const env = {};
131
+ if (process.platform === 'win32') {
132
+ for (const name of WINDOWS_IDENTITY_TO_BLANK)
133
+ env[name] = '';
134
+ }
135
+ for (const name of [...ALLOWED, ...passthroughNames(source)]) {
136
+ const value = lookup(source, name);
137
+ if (value !== undefined)
138
+ env[name] = value;
139
+ }
140
+ return extra ? { ...env, ...extra } : env;
141
+ }
142
+ /** Exposed for tests and for documenting the surface. */
143
+ export const ALLOWED_CHILD_ENV_VARS = ALLOWED;
144
+ //# sourceMappingURL=child-env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"child-env.js","sourceRoot":"","sources":["../../src/lib/child-env.ts"],"names":[],"mappings":"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"}