@orygn/opa-mcp 0.3.0 → 0.5.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 (157) hide show
  1. package/CHANGELOG.md +396 -3
  2. package/README.md +132 -98
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +8 -3
  5. package/dist/cli.js.map +1 -1
  6. package/dist/config.d.ts +15 -3
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +74 -14
  9. package/dist/config.js.map +1 -1
  10. package/dist/constants.d.ts +1 -1
  11. package/dist/constants.js +1 -1
  12. package/dist/lib/bundle-signatures.d.ts +23 -0
  13. package/dist/lib/bundle-signatures.d.ts.map +1 -0
  14. package/dist/lib/bundle-signatures.js +152 -0
  15. package/dist/lib/bundle-signatures.js.map +1 -0
  16. package/dist/lib/child-env.d.ts +41 -0
  17. package/dist/lib/child-env.d.ts.map +1 -0
  18. package/dist/lib/child-env.js +176 -0
  19. package/dist/lib/child-env.js.map +1 -0
  20. package/dist/lib/conftest-cli.d.ts +25 -10
  21. package/dist/lib/conftest-cli.d.ts.map +1 -1
  22. package/dist/lib/conftest-cli.js +153 -33
  23. package/dist/lib/conftest-cli.js.map +1 -1
  24. package/dist/lib/install-id.d.ts.map +1 -1
  25. package/dist/lib/install-id.js +18 -3
  26. package/dist/lib/install-id.js.map +1 -1
  27. package/dist/lib/json-stream.d.ts +26 -0
  28. package/dist/lib/json-stream.d.ts.map +1 -0
  29. package/dist/lib/json-stream.js +69 -0
  30. package/dist/lib/json-stream.js.map +1 -0
  31. package/dist/lib/opa-cli.d.ts +64 -16
  32. package/dist/lib/opa-cli.d.ts.map +1 -1
  33. package/dist/lib/opa-cli.js +99 -28
  34. package/dist/lib/opa-cli.js.map +1 -1
  35. package/dist/lib/opa-paths.d.ts +24 -0
  36. package/dist/lib/opa-paths.d.ts.map +1 -0
  37. package/dist/lib/opa-paths.js +97 -0
  38. package/dist/lib/opa-paths.js.map +1 -0
  39. package/dist/lib/regal-cli.d.ts +16 -1
  40. package/dist/lib/regal-cli.d.ts.map +1 -1
  41. package/dist/lib/regal-cli.js +41 -6
  42. package/dist/lib/regal-cli.js.map +1 -1
  43. package/dist/lib/rego-ast-walker.d.ts.map +1 -1
  44. package/dist/lib/rego-ast-walker.js +141 -17
  45. package/dist/lib/rego-ast-walker.js.map +1 -1
  46. package/dist/lib/rego-counterexample.d.ts +1 -1
  47. package/dist/lib/rego-counterexample.d.ts.map +1 -1
  48. package/dist/lib/rego-counterexample.js +23 -5
  49. package/dist/lib/rego-counterexample.js.map +1 -1
  50. package/dist/lib/rego-ir.d.ts +32 -2
  51. package/dist/lib/rego-ir.d.ts.map +1 -1
  52. package/dist/lib/rego-smt-encoder.d.ts +53 -5
  53. package/dist/lib/rego-smt-encoder.d.ts.map +1 -1
  54. package/dist/lib/rego-smt-encoder.js +192 -26
  55. package/dist/lib/rego-smt-encoder.js.map +1 -1
  56. package/dist/lib/rego-type-inferencer.d.ts +1 -1
  57. package/dist/lib/rego-type-inferencer.d.ts.map +1 -1
  58. package/dist/lib/rego-type-inferencer.js +3 -3
  59. package/dist/lib/rego-type-inferencer.js.map +1 -1
  60. package/dist/lib/rego-verify-engine.d.ts.map +1 -1
  61. package/dist/lib/rego-verify-engine.js +109 -77
  62. package/dist/lib/rego-verify-engine.js.map +1 -1
  63. package/dist/lib/rego-z3.d.ts +10 -0
  64. package/dist/lib/rego-z3.d.ts.map +1 -1
  65. package/dist/lib/rego-z3.js +38 -0
  66. package/dist/lib/rego-z3.js.map +1 -1
  67. package/dist/lib/security.d.ts +8 -0
  68. package/dist/lib/security.d.ts.map +1 -1
  69. package/dist/lib/security.js +130 -54
  70. package/dist/lib/security.js.map +1 -1
  71. package/dist/lib/subprocess.d.ts +36 -3
  72. package/dist/lib/subprocess.d.ts.map +1 -1
  73. package/dist/lib/subprocess.js +121 -20
  74. package/dist/lib/subprocess.js.map +1 -1
  75. package/dist/lib/tool-helpers.d.ts.map +1 -1
  76. package/dist/lib/tool-helpers.js +10 -0
  77. package/dist/lib/tool-helpers.js.map +1 -1
  78. package/dist/resources/patterns.d.ts +1 -1
  79. package/dist/resources/patterns.d.ts.map +1 -1
  80. package/dist/resources/patterns.js +40 -12
  81. package/dist/resources/patterns.js.map +1 -1
  82. package/dist/server.d.ts.map +1 -1
  83. package/dist/server.js +10 -2
  84. package/dist/server.js.map +1 -1
  85. package/dist/tools/authoring/check-schema.js +1 -1
  86. package/dist/tools/authoring/check-schema.js.map +1 -1
  87. package/dist/tools/bundles/build.d.ts.map +1 -1
  88. package/dist/tools/bundles/build.js +12 -4
  89. package/dist/tools/bundles/build.js.map +1 -1
  90. package/dist/tools/bundles/sign.d.ts +9 -2
  91. package/dist/tools/bundles/sign.d.ts.map +1 -1
  92. package/dist/tools/bundles/sign.js +145 -19
  93. package/dist/tools/bundles/sign.js.map +1 -1
  94. package/dist/tools/bundles/verify.d.ts +1 -1
  95. package/dist/tools/bundles/verify.d.ts.map +1 -1
  96. package/dist/tools/bundles/verify.js +58 -19
  97. package/dist/tools/bundles/verify.js.map +1 -1
  98. package/dist/tools/conftest/pull.d.ts.map +1 -1
  99. package/dist/tools/conftest/pull.js +24 -10
  100. package/dist/tools/conftest/pull.js.map +1 -1
  101. package/dist/tools/conftest/push.d.ts.map +1 -1
  102. package/dist/tools/conftest/push.js +17 -7
  103. package/dist/tools/conftest/push.js.map +1 -1
  104. package/dist/tools/conftest/test.d.ts +10 -6
  105. package/dist/tools/conftest/test.d.ts.map +1 -1
  106. package/dist/tools/conftest/test.js +47 -24
  107. package/dist/tools/conftest/test.js.map +1 -1
  108. package/dist/tools/conftest/verify.d.ts +10 -6
  109. package/dist/tools/conftest/verify.d.ts.map +1 -1
  110. package/dist/tools/conftest/verify.js +30 -18
  111. package/dist/tools/conftest/verify.js.map +1 -1
  112. package/dist/tools/evaluation/bench.d.ts +16 -0
  113. package/dist/tools/evaluation/bench.d.ts.map +1 -1
  114. package/dist/tools/evaluation/bench.js +30 -5
  115. package/dist/tools/evaluation/bench.js.map +1 -1
  116. package/dist/tools/evaluation/exec.d.ts +16 -0
  117. package/dist/tools/evaluation/exec.d.ts.map +1 -1
  118. package/dist/tools/evaluation/exec.js +35 -2
  119. package/dist/tools/evaluation/exec.js.map +1 -1
  120. package/dist/tools/evaluation/test-multiroot.d.ts +4 -0
  121. package/dist/tools/evaluation/test-multiroot.d.ts.map +1 -1
  122. package/dist/tools/evaluation/test-multiroot.js +21 -14
  123. package/dist/tools/evaluation/test-multiroot.js.map +1 -1
  124. package/dist/tools/evaluation/test.d.ts +45 -0
  125. package/dist/tools/evaluation/test.d.ts.map +1 -1
  126. package/dist/tools/evaluation/test.js +146 -27
  127. package/dist/tools/evaluation/test.js.map +1 -1
  128. package/dist/tools/helpers/coverage-gaps.d.ts +11 -3
  129. package/dist/tools/helpers/coverage-gaps.d.ts.map +1 -1
  130. package/dist/tools/helpers/coverage-gaps.js +4 -3
  131. package/dist/tools/helpers/coverage-gaps.js.map +1 -1
  132. package/dist/tools/helpers/explain-undefined.d.ts +9 -3
  133. package/dist/tools/helpers/explain-undefined.d.ts.map +1 -1
  134. package/dist/tools/helpers/explain-undefined.js +83 -51
  135. package/dist/tools/helpers/explain-undefined.js.map +1 -1
  136. package/dist/tools/helpers/verify.d.ts.map +1 -1
  137. package/dist/tools/helpers/verify.js +5 -1
  138. package/dist/tools/helpers/verify.js.map +1 -1
  139. package/dist/tools/server-management/_shared.d.ts +15 -4
  140. package/dist/tools/server-management/_shared.d.ts.map +1 -1
  141. package/dist/tools/server-management/_shared.js +73 -12
  142. package/dist/tools/server-management/_shared.js.map +1 -1
  143. package/dist/tools/server-management/data.d.ts.map +1 -1
  144. package/dist/tools/server-management/data.js +88 -23
  145. package/dist/tools/server-management/data.js.map +1 -1
  146. package/dist/tools/server-management/decisions.d.ts.map +1 -1
  147. package/dist/tools/server-management/decisions.js +16 -3
  148. package/dist/tools/server-management/decisions.js.map +1 -1
  149. package/dist/tools/server-management/policies.d.ts.map +1 -1
  150. package/dist/tools/server-management/policies.js +44 -7
  151. package/dist/tools/server-management/policies.js.map +1 -1
  152. package/dist/tools/server-management/status.d.ts.map +1 -1
  153. package/dist/tools/server-management/status.js +80 -13
  154. package/dist/tools/server-management/status.js.map +1 -1
  155. package/dist/types.d.ts +10 -1
  156. package/dist/types.d.ts.map +1 -1
  157. package/package.json +2 -1
package/dist/config.js CHANGED
@@ -6,9 +6,17 @@
6
6
  * Cursor, VS Code) pass config via the `env` object in their JSON.
7
7
  */
8
8
  import { tmpdir } from 'node:os';
9
- import { join } from 'node:path';
9
+ import { isAbsolute, 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';
13
+ /**
14
+ * Node clamps a timer of 2^31 ms or more to 1 ms, with only a process warning.
15
+ * A large value reads as "effectively no timeout" and produced the opposite:
16
+ * every subprocess and every HTTP call timed out immediately.
17
+ */
18
+ const MAX_TIMER_MS = 2_147_483_647;
19
+ const TIMER_TOO_LARGE = 'Timeout must be below 2147483647 ms (about 24 days). Node clamps anything larger to 1 ms, which times out every call immediately.';
12
20
  const ConfigSchema = z.object({
13
21
  /** Base URL of a running OPA server (used by `opa_*` runtime tools). */
14
22
  opaUrl: z
@@ -27,16 +35,36 @@ const ConfigSchema = z.object({
27
35
  /** Path to the `conftest` binary. Defaults to `conftest` on PATH. */
28
36
  conftestBinary: z.string().default('conftest'),
29
37
  /** Hard timeout in ms for any spawned subprocess (opa, regal). */
30
- subprocessTimeoutMs: z.coerce.number().int().positive().default(30_000),
38
+ subprocessTimeoutMs: z.coerce
39
+ .number()
40
+ .int()
41
+ .positive()
42
+ .max(MAX_TIMER_MS, TIMER_TOO_LARGE)
43
+ .default(30_000),
31
44
  /** HTTP request timeout for OPA REST API calls. */
32
- httpTimeoutMs: z.coerce.number().int().positive().default(15_000),
45
+ httpTimeoutMs: z.coerce
46
+ .number()
47
+ .int()
48
+ .positive()
49
+ .max(MAX_TIMER_MS, TIMER_TOO_LARGE)
50
+ .default(15_000),
33
51
  /**
34
52
  * Allow-listed root directories for file path inputs. Tools that accept
35
53
  * filesystem paths reject anything outside these roots. Empty by
36
54
  * default -- file-based tools refuse to read from disk until the
37
55
  * operator explicitly opts in via `OPA_MCP_ALLOWED_PATHS`.
38
56
  */
39
- allowedPaths: z.array(z.string()).default([]),
57
+ allowedPaths: z
58
+ .array(
59
+ // A relative root is resolved against the server's working directory,
60
+ // which for a stdio server is wherever the client happened to launch it.
61
+ // The same configuration would then permit different directories on
62
+ // different machines, so it is refused rather than guessed at, which is
63
+ // what the documentation has always said happens.
64
+ z.string().refine(isAbsolute, {
65
+ message: 'must be an absolute path',
66
+ }))
67
+ .default([]),
40
68
  /** Path to the log file. Defaults to OS tmpdir + orygn-opa-mcp.log. */
41
69
  logFile: z.string().default(join(tmpdir(), 'orygn-opa-mcp.log')),
42
70
  /** Log level for the file logger. */
@@ -47,6 +75,21 @@ const ConfigSchema = z.object({
47
75
  * write to a file path the agent specifies.
48
76
  */
49
77
  maxResponseBytes: z.coerce.number().int().positive().default(100_000),
78
+ /**
79
+ * Maximum bytes captured from a subprocess's stdout and stderr, each counted
80
+ * separately. On overflow the stream is clamped and the child is killed.
81
+ *
82
+ * This is a memory bound, distinct from `maxResponseBytes`, which trims the
83
+ * response after the output has already been read into the heap. A capture
84
+ * past V8's max string length cannot be decoded at all, and the throw would
85
+ * land in an async callback where no tool can catch it.
86
+ */
87
+ maxSubprocessBytes: z.coerce
88
+ .number()
89
+ .int()
90
+ .positive()
91
+ .max(DEFAULT_MAX_OUTPUT_BYTES * 8, 'OPA_MCP_MAX_SUBPROCESS_BYTES is too large to decode safely; keep it well under 512 MiB.')
92
+ .default(DEFAULT_MAX_OUTPUT_BYTES),
50
93
  });
51
94
  function parseAllowedPaths(raw) {
52
95
  if (!raw)
@@ -69,21 +112,38 @@ const ENV_VAR_NAMES = {
69
112
  logFile: 'OPA_MCP_LOG_FILE',
70
113
  logLevel: 'OPA_MCP_LOG_LEVEL',
71
114
  maxResponseBytes: 'OPA_MCP_MAX_RESPONSE_BYTES',
115
+ maxSubprocessBytes: 'OPA_MCP_MAX_SUBPROCESS_BYTES',
72
116
  };
117
+ /**
118
+ * Read an environment variable, treating an empty or blank value as unset.
119
+ *
120
+ * A shell that expands an unset variable leaves an empty string behind, and
121
+ * `OPA_BINARY=""` was taken as a real path: it is not the literal default
122
+ * `opa`, so binary resolution skipped the bundled build and every tool call
123
+ * tried to spawn nothing. Blank means "not configured" for every variable here.
124
+ */
125
+ function env(name) {
126
+ const raw = process.env[name];
127
+ if (raw === undefined)
128
+ return undefined;
129
+ const trimmed = raw.trim();
130
+ return trimmed.length > 0 ? trimmed : undefined;
131
+ }
73
132
  export function loadConfig() {
74
133
  const allowedPaths = parseAllowedPaths(process.env['OPA_MCP_ALLOWED_PATHS']);
75
134
  const parsed = ConfigSchema.safeParse({
76
- opaUrl: process.env['OPA_URL'],
77
- opaToken: process.env['OPA_TOKEN'],
78
- opaBinary: process.env['OPA_BINARY'],
79
- regalBinary: process.env['REGAL_BINARY'],
80
- conftestBinary: process.env['CONFTEST_BINARY'],
81
- subprocessTimeoutMs: process.env['OPA_MCP_TIMEOUT_MS'],
82
- httpTimeoutMs: process.env['OPA_MCP_HTTP_TIMEOUT_MS'],
135
+ opaUrl: env('OPA_URL'),
136
+ opaToken: env('OPA_TOKEN'),
137
+ opaBinary: env('OPA_BINARY'),
138
+ regalBinary: env('REGAL_BINARY'),
139
+ conftestBinary: env('CONFTEST_BINARY'),
140
+ subprocessTimeoutMs: env('OPA_MCP_TIMEOUT_MS'),
141
+ httpTimeoutMs: env('OPA_MCP_HTTP_TIMEOUT_MS'),
83
142
  allowedPaths,
84
- logFile: process.env['OPA_MCP_LOG_FILE'],
85
- logLevel: process.env['OPA_MCP_LOG_LEVEL'],
86
- maxResponseBytes: process.env['OPA_MCP_MAX_RESPONSE_BYTES'],
143
+ logFile: env('OPA_MCP_LOG_FILE'),
144
+ logLevel: env('OPA_MCP_LOG_LEVEL'),
145
+ maxResponseBytes: env('OPA_MCP_MAX_RESPONSE_BYTES'),
146
+ maxSubprocessBytes: env('OPA_MCP_MAX_SUBPROCESS_BYTES'),
87
147
  });
88
148
  if (!parsed.success) {
89
149
  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.3.0";
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.3.0';
7
+ export const SERVER_VERSION = '0.4.0';
8
8
  //# sourceMappingURL=constants.js.map
@@ -0,0 +1,23 @@
1
+ export interface SignaturesSummary {
2
+ /** JWT `alg` header, e.g. `RS256`. */
3
+ algorithm: string;
4
+ /** Number of files the signature covers. */
5
+ filesSigned: number;
6
+ keyId?: string;
7
+ scope?: string;
8
+ }
9
+ /**
10
+ * Read the summary of a `.signatures.json` file. Throws on any shape OPA
11
+ * would not have written, so a caller never reports a file it cannot read.
12
+ */
13
+ export declare function readSignaturesSummary(path: string): Promise<SignaturesSummary>;
14
+ export declare function isKeyOrAlgorithmError(output: string): boolean;
15
+ export type VerificationFailureReason = 'key_invalid' | 'not_a_bundle' | 'signature_invalid' | 'scope_mismatch' | 'file_modified' | 'file_missing' | 'file_added' | 'unsigned' | 'signatures_malformed' | 'file_unparseable' | 'bundle_load_error' | 'unknown';
16
+ export interface VerificationFailure {
17
+ reason: VerificationFailureReason;
18
+ summary: string;
19
+ hint?: string;
20
+ }
21
+ /** Classify OPA's stdout+stderr from a failed verification. */
22
+ export declare function classifyVerificationFailure(output: string): VerificationFailure;
23
+ //# sourceMappingURL=bundle-signatures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-signatures.d.ts","sourceRoot":"","sources":["../../src/lib/bundle-signatures.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAQD;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA0BpF;AASD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,MAAM,MAAM,yBAAyB,GACjC,aAAa,GACb,cAAc,GACd,mBAAmB,GACnB,gBAAgB,GAChB,eAAe,GACf,cAAc,GACd,YAAY,GACZ,UAAU,GACV,sBAAsB,GACtB,kBAAkB,GAClB,mBAAmB,GACnB,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,yBAAyB,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAuFD,+DAA+D;AAC/D,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAW/E"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Helpers for OPA bundle signatures.
3
+ *
4
+ * `opa sign` writes `{ "signatures": ["<JWT>"] }` with exactly one JWT. Its
5
+ * header carries the algorithm and its payload lists every signed file as
6
+ * `{ name, hash, algorithm }`, plus whatever extra claims (`keyid`, `scope`)
7
+ * were supplied through a claims file. Reading that summary back is how the
8
+ * sign tool describes what it just produced, and it is deliberately not a
9
+ * verification: nothing here checks the signature, only OPA does that.
10
+ */
11
+ import { readFile } from 'node:fs/promises';
12
+ function base64UrlDecode(segment) {
13
+ const b64 = segment.replace(/-/g, '+').replace(/_/g, '/');
14
+ const padded = b64 + '='.repeat((4 - (b64.length % 4)) % 4);
15
+ return Buffer.from(padded, 'base64').toString('utf8');
16
+ }
17
+ /**
18
+ * Read the summary of a `.signatures.json` file. Throws on any shape OPA
19
+ * would not have written, so a caller never reports a file it cannot read.
20
+ */
21
+ export async function readSignaturesSummary(path) {
22
+ const raw = await readFile(path, 'utf8');
23
+ const parsed = JSON.parse(raw);
24
+ const sigs = parsed.signatures;
25
+ if (!Array.isArray(sigs) || sigs.length !== 1 || typeof sigs[0] !== 'string') {
26
+ throw new Error('expected exactly one JWT in "signatures"');
27
+ }
28
+ const parts = sigs[0].split('.');
29
+ if (parts.length !== 3)
30
+ throw new Error('signature is not a JWT');
31
+ const header = JSON.parse(base64UrlDecode(parts[0]));
32
+ const payload = JSON.parse(base64UrlDecode(parts[1]));
33
+ if (typeof header.alg !== 'string')
34
+ throw new Error('JWT header has no alg');
35
+ if (!Array.isArray(payload.files))
36
+ throw new Error('JWT payload has no files array');
37
+ const summary = {
38
+ algorithm: header.alg,
39
+ filesSigned: payload.files.length,
40
+ };
41
+ if (typeof payload.keyid === 'string')
42
+ summary.keyId = payload.keyid;
43
+ if (typeof payload.scope === 'string')
44
+ summary.scope = payload.scope;
45
+ return summary;
46
+ }
47
+ /**
48
+ * `opa sign` and `opa build` both print these when the key file is not a PEM
49
+ * key or the algorithm is not one OPA knows. They are the caller's input, not
50
+ * the bundle, so the tools report them as INVALID_INPUT.
51
+ */
52
+ const KEY_OR_ALGORITHM_ERROR = /failed to parse PEM block|unknown signature algorithm/i;
53
+ export function isKeyOrAlgorithmError(output) {
54
+ return KEY_OR_ALGORITHM_ERROR.test(output);
55
+ }
56
+ /**
57
+ * Each pattern is text OPA 1.19 prints, on stdout, for that failure.
58
+ *
59
+ * The order follows what OPA does while loading a signed bundle: it reads the
60
+ * key and algorithm, checks the JWT against the key, compares the scope claim,
61
+ * then reads every file. A Rego file is hashed as written, so its digest is
62
+ * compared before it is parsed. A data file or `.manifest` is hashed by parsed
63
+ * value, so one that no longer parses fails BEFORE its digest is compared;
64
+ * that is why an unparseable data file is `file_unparseable` (possibly
65
+ * modified) and not a load error of signed content. Rego parse errors come
66
+ * last, after the signature and every digest passed. Where messages overlap,
67
+ * the more specific rule is listed first.
68
+ */
69
+ const FAILURE_RULES = [
70
+ {
71
+ reason: 'key_invalid',
72
+ pattern: KEY_OR_ALGORITHM_ERROR,
73
+ summary: 'the verification key or algorithm could not be used',
74
+ hint: 'verificationKey must be a PEM public key, and signingAlg one OPA supports. For an HMAC-signed bundle pass signingAlg (HS256, HS384 or HS512) so the file is read as the secret rather than as PEM.',
75
+ },
76
+ {
77
+ reason: 'not_a_bundle',
78
+ pattern: /archive read failed|bundle read failed/i,
79
+ summary: 'the path is not a bundle directory or a .tar.gz archive',
80
+ },
81
+ {
82
+ reason: 'signature_invalid',
83
+ pattern: /failed to verify JWT signature|verification error/i,
84
+ summary: 'the signature does not verify with this key',
85
+ hint: 'Check the key and pass signingAlg matching how the bundle was signed. OPA verifies against the single key given regardless of the signature keyid claim, so this is the key or the algorithm, not the key id.',
86
+ },
87
+ {
88
+ reason: 'scope_mismatch',
89
+ pattern: /scope mismatch/i,
90
+ summary: 'the scope given does not match the scope claim in the signature',
91
+ hint: 'Pass scope with exactly the value the bundle was signed with, and no scope if it was signed without one.',
92
+ },
93
+ {
94
+ reason: 'file_modified',
95
+ pattern: /digest mismatch/i,
96
+ summary: 'a signed file was modified after signing',
97
+ },
98
+ {
99
+ reason: 'file_missing',
100
+ pattern: /specified in bundle signatures but not found/i,
101
+ summary: 'a file the signature covers is missing from the bundle',
102
+ },
103
+ {
104
+ reason: 'file_added',
105
+ pattern: /not included in bundle signature/i,
106
+ summary: 'the bundle contains a file the signature does not cover',
107
+ hint: 'A directory signature names files under the directory name given to opa, so this is also what a directory reports when verified under a different name than it was signed with. Moving it elsewhere under the same name is fine; renaming it is not. For an artifact that survives renaming, build a signed archive with opa_bundle_build.',
108
+ },
109
+ {
110
+ reason: 'unsigned',
111
+ pattern: /missing \.signatures\.json/i,
112
+ summary: 'the bundle has no .signatures.json',
113
+ },
114
+ {
115
+ reason: 'signatures_malformed',
116
+ pattern: /signatures decode|missing JWT|base64 decode JWT|split compact JWT|unexpected end of JSON input|\.signatures\.json/i,
117
+ summary: '.signatures.json is not a valid signature file',
118
+ },
119
+ {
120
+ reason: 'file_unparseable',
121
+ pattern: /yaml:/i,
122
+ summary: 'a data file or .manifest could not be parsed, so its digest was never compared',
123
+ hint: 'OPA hashes data files by parsed value and stops at the first one it cannot parse, before comparing digests. The file may have been modified after signing. Restore it, or fix it and sign again.',
124
+ },
125
+ {
126
+ reason: 'bundle_load_error',
127
+ pattern: /rego_\w+_error|errors? occurred|manifest roots/i,
128
+ summary: 'the bundle could not be loaded, so verification did not complete',
129
+ hint: 'OPA parses Rego after the signature and every digest passed, so the policy is signed and unmodified but does not load under Rego v1. A policy written for Rego v0 verifies with v0Compatible.',
130
+ },
131
+ {
132
+ // A JWT payload that decodes but is not JSON: OPA prints only the JSON
133
+ // error, with none of the prefixes matched above.
134
+ reason: 'signatures_malformed',
135
+ pattern: /invalid character/i,
136
+ summary: '.signatures.json is not a valid signature file',
137
+ },
138
+ ];
139
+ /** Classify OPA's stdout+stderr from a failed verification. */
140
+ export function classifyVerificationFailure(output) {
141
+ for (const rule of FAILURE_RULES) {
142
+ if (rule.pattern.test(output)) {
143
+ const { pattern: _pattern, ...failure } = rule;
144
+ return failure;
145
+ }
146
+ }
147
+ return {
148
+ reason: 'unknown',
149
+ summary: 'OPA rejected the bundle for a reason this tool does not recognise',
150
+ };
151
+ }
152
+ //# sourceMappingURL=bundle-signatures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bundle-signatures.js","sourceRoot":"","sources":["../../src/lib/bundle-signatures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
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. It holds no cloud or
14
+ * repository token, which is what motivated the list, but it is not free of
15
+ * credentials: a proxy URL can embed one, and `HTTP_PROXY` and its siblings are
16
+ * on the list because dropping them breaks every user behind a corporate proxy.
17
+ * An operator who would rather lose proxy support than expose those credentials
18
+ * to evaluated policy can name them in `OPA_MCP_BLOCK_ENV`.
19
+ *
20
+ * Measured against the bundled OPA 1.19.0, `version`, `eval`, `check`, `test`
21
+ * and `build` all succeed with a completely empty environment, so the entries
22
+ * below exist for correctness in real-world setups (proxies, custom CA bundles,
23
+ * tool config discovery), not to make the binaries run.
24
+ *
25
+ * Operators who genuinely need a variable in policy can name it in
26
+ * `OPA_MCP_PASSTHROUGH_ENV`, which is opt-in precisely because it undoes this.
27
+ */
28
+ /**
29
+ * Build the child environment: the allow-list, plus any opted-in passthrough,
30
+ * plus `extra` from the caller (which wins, since it is explicit intent).
31
+ *
32
+ * @param extra Variables the calling command needs. Readable by evaluated
33
+ * policy like everything else here, so pass secrets only when the
34
+ * command actually requires them. `OPA_MCP_BLOCK_ENV` removes a
35
+ * name even when it appears here.
36
+ * @param source Environment to read from. Injectable for testing.
37
+ */
38
+ export declare function buildChildEnv(extra?: Record<string, string>, source?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
39
+ /** Exposed for tests and for documenting the surface. */
40
+ export declare const ALLOWED_CHILD_ENV_VARS: readonly string[];
41
+ //# 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;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AA0HH;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,MAAM,GAAE,MAAM,CAAC,UAAwB,GACtC,MAAM,CAAC,UAAU,CAyBnB;AAED,yDAAyD;AACzD,eAAO,MAAM,sBAAsB,EAAE,SAAS,MAAM,EAAY,CAAC"}
@@ -0,0 +1,176 @@
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. It holds no cloud or
14
+ * repository token, which is what motivated the list, but it is not free of
15
+ * credentials: a proxy URL can embed one, and `HTTP_PROXY` and its siblings are
16
+ * on the list because dropping them breaks every user behind a corporate proxy.
17
+ * An operator who would rather lose proxy support than expose those credentials
18
+ * to evaluated policy can name them in `OPA_MCP_BLOCK_ENV`.
19
+ *
20
+ * Measured against the bundled OPA 1.19.0, `version`, `eval`, `check`, `test`
21
+ * and `build` all succeed with a completely empty environment, so the entries
22
+ * below exist for correctness in real-world setups (proxies, custom CA bundles,
23
+ * tool config discovery), not to make the binaries run.
24
+ *
25
+ * Operators who genuinely need a variable in policy can name it in
26
+ * `OPA_MCP_PASSTHROUGH_ENV`, which is opt-in precisely because it undoes this.
27
+ */
28
+ /** Vars every platform benefits from. Locale keeps output formatting stable. */
29
+ const COMMON = ['PATH', 'LANG', 'LC_ALL', 'LC_CTYPE', 'LC_NUMERIC', 'TZ'];
30
+ /** POSIX home and temp. Regal and Conftest look under HOME for config. */
31
+ const POSIX = ['HOME', 'TMPDIR', 'SHELL', 'USER', 'LOGNAME'];
32
+ /**
33
+ * Windows equivalents. A Go binary starts without these, but process creation
34
+ * and temp-file handling are better behaved with them present.
35
+ */
36
+ const WINDOWS = [
37
+ 'PATHEXT',
38
+ 'SystemRoot',
39
+ 'SystemDrive',
40
+ 'COMSPEC',
41
+ 'windir',
42
+ 'TEMP',
43
+ 'TMP',
44
+ 'USERPROFILE',
45
+ 'HOMEDRIVE',
46
+ 'HOMEPATH',
47
+ 'APPDATA',
48
+ 'LOCALAPPDATA',
49
+ 'PROCESSOR_ARCHITECTURE',
50
+ 'NUMBER_OF_PROCESSORS',
51
+ ];
52
+ /**
53
+ * Proxy and TLS trust. Without these, `http.send`, bundle downloads and remote
54
+ * schema fetches fail behind a corporate proxy or a custom CA.
55
+ *
56
+ * A proxy URL can embed credentials, so these are the least inert entries here.
57
+ * They are included because breaking every corporate user is the worse trade,
58
+ * and they are the operator's own infrastructure credentials rather than the
59
+ * cloud and repository tokens that motivated this module.
60
+ */
61
+ const NETWORK = [
62
+ 'HTTP_PROXY',
63
+ 'HTTPS_PROXY',
64
+ 'NO_PROXY',
65
+ 'ALL_PROXY',
66
+ 'http_proxy',
67
+ 'https_proxy',
68
+ 'no_proxy',
69
+ 'all_proxy',
70
+ 'SSL_CERT_FILE',
71
+ 'SSL_CERT_DIR',
72
+ ];
73
+ /**
74
+ * Where the tools look for their own config and credentials. These name file
75
+ * locations; they are not themselves secrets. Conftest reads registry auth from
76
+ * the Docker config file, so `conftest_pull` and `conftest_push` need these.
77
+ */
78
+ const TOOL_CONFIG = [
79
+ 'XDG_CONFIG_HOME',
80
+ 'XDG_CACHE_HOME',
81
+ 'XDG_DATA_HOME',
82
+ 'DOCKER_CONFIG',
83
+ 'REGISTRY_AUTH_FILE',
84
+ ];
85
+ const ALLOWED = [...COMMON, ...POSIX, ...WINDOWS, ...NETWORK, ...TOOL_CONFIG];
86
+ /** Split a comma or semicolon separated list of variable names. */
87
+ function nameList(raw) {
88
+ if (!raw)
89
+ return [];
90
+ return raw
91
+ .split(/[;,]/)
92
+ .map((s) => s.trim())
93
+ .filter((s) => s.length > 0);
94
+ }
95
+ /** Names an operator opted into via OPA_MCP_PASSTHROUGH_ENV. */
96
+ function passthroughNames(source) {
97
+ return nameList(source['OPA_MCP_PASSTHROUGH_ENV']);
98
+ }
99
+ /**
100
+ * Names an operator withheld via OPA_MCP_BLOCK_ENV.
101
+ *
102
+ * Applied last, so it overrides the allow-list, the passthrough list and the
103
+ * caller's own `extra`. The point is to be able to say no to something this
104
+ * module would otherwise hand down, and a rule that could be overridden by the
105
+ * thing it is meant to restrain would not be worth having.
106
+ */
107
+ function blockedNames(source) {
108
+ return nameList(source['OPA_MCP_BLOCK_ENV']);
109
+ }
110
+ /**
111
+ * Windows treats variable names case-insensitively, and the casing in
112
+ * `process.env` follows whatever the parent used, so an exact-key lookup would
113
+ * silently miss `Path` where the list says `PATH`.
114
+ */
115
+ function lookup(source, name) {
116
+ const direct = source[name];
117
+ if (direct !== undefined)
118
+ return direct;
119
+ if (process.platform !== 'win32')
120
+ return undefined;
121
+ const wanted = name.toLowerCase();
122
+ for (const key of Object.keys(source)) {
123
+ if (key.toLowerCase() === wanted)
124
+ return source[key];
125
+ }
126
+ return undefined;
127
+ }
128
+ /**
129
+ * Windows-only identity variables that libuv copies from the parent no matter
130
+ * what environment is requested -- spawning with `{}` still delivers them, and
131
+ * measurably so. They are not secrets, but they name the operating-system user,
132
+ * the Windows domain and the domain controller, which is more than an untrusted
133
+ * policy needs to know about the machine evaluating it.
134
+ *
135
+ * They cannot be removed, only overwritten, so they are blanked. None of the
136
+ * three binaries reads them: every representative `opa` command succeeds with a
137
+ * completely empty environment on both platforms.
138
+ */
139
+ const WINDOWS_IDENTITY_TO_BLANK = ['USERNAME', 'USERDOMAIN', 'LOGONSERVER'];
140
+ /**
141
+ * Build the child environment: the allow-list, plus any opted-in passthrough,
142
+ * plus `extra` from the caller (which wins, since it is explicit intent).
143
+ *
144
+ * @param extra Variables the calling command needs. Readable by evaluated
145
+ * policy like everything else here, so pass secrets only when the
146
+ * command actually requires them. `OPA_MCP_BLOCK_ENV` removes a
147
+ * name even when it appears here.
148
+ * @param source Environment to read from. Injectable for testing.
149
+ */
150
+ export function buildChildEnv(extra, source = process.env) {
151
+ const env = {};
152
+ if (process.platform === 'win32') {
153
+ for (const name of WINDOWS_IDENTITY_TO_BLANK)
154
+ env[name] = '';
155
+ }
156
+ for (const name of [...ALLOWED, ...passthroughNames(source)]) {
157
+ const value = lookup(source, name);
158
+ if (value !== undefined)
159
+ env[name] = value;
160
+ }
161
+ const result = extra ? { ...env, ...extra } : env;
162
+ for (const name of blockedNames(source)) {
163
+ delete result[name];
164
+ if (process.platform === 'win32') {
165
+ const wanted = name.toLowerCase();
166
+ for (const key of Object.keys(result)) {
167
+ if (key.toLowerCase() === wanted)
168
+ delete result[key];
169
+ }
170
+ }
171
+ }
172
+ return result;
173
+ }
174
+ /** Exposed for tests and for documenting the surface. */
175
+ export const ALLOWED_CHILD_ENV_VARS = ALLOWED;
176
+ //# 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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"}