@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.
- package/CHANGELOG.md +396 -3
- package/README.md +132 -98
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +8 -3
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +15 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +74 -14
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/lib/bundle-signatures.d.ts +23 -0
- package/dist/lib/bundle-signatures.d.ts.map +1 -0
- package/dist/lib/bundle-signatures.js +152 -0
- package/dist/lib/bundle-signatures.js.map +1 -0
- package/dist/lib/child-env.d.ts +41 -0
- package/dist/lib/child-env.d.ts.map +1 -0
- package/dist/lib/child-env.js +176 -0
- package/dist/lib/child-env.js.map +1 -0
- package/dist/lib/conftest-cli.d.ts +25 -10
- package/dist/lib/conftest-cli.d.ts.map +1 -1
- package/dist/lib/conftest-cli.js +153 -33
- package/dist/lib/conftest-cli.js.map +1 -1
- package/dist/lib/install-id.d.ts.map +1 -1
- package/dist/lib/install-id.js +18 -3
- package/dist/lib/install-id.js.map +1 -1
- package/dist/lib/json-stream.d.ts +26 -0
- package/dist/lib/json-stream.d.ts.map +1 -0
- package/dist/lib/json-stream.js +69 -0
- package/dist/lib/json-stream.js.map +1 -0
- package/dist/lib/opa-cli.d.ts +64 -16
- package/dist/lib/opa-cli.d.ts.map +1 -1
- package/dist/lib/opa-cli.js +99 -28
- package/dist/lib/opa-cli.js.map +1 -1
- package/dist/lib/opa-paths.d.ts +24 -0
- package/dist/lib/opa-paths.d.ts.map +1 -0
- package/dist/lib/opa-paths.js +97 -0
- package/dist/lib/opa-paths.js.map +1 -0
- package/dist/lib/regal-cli.d.ts +16 -1
- package/dist/lib/regal-cli.d.ts.map +1 -1
- package/dist/lib/regal-cli.js +41 -6
- package/dist/lib/regal-cli.js.map +1 -1
- package/dist/lib/rego-ast-walker.d.ts.map +1 -1
- package/dist/lib/rego-ast-walker.js +141 -17
- package/dist/lib/rego-ast-walker.js.map +1 -1
- package/dist/lib/rego-counterexample.d.ts +1 -1
- package/dist/lib/rego-counterexample.d.ts.map +1 -1
- package/dist/lib/rego-counterexample.js +23 -5
- package/dist/lib/rego-counterexample.js.map +1 -1
- package/dist/lib/rego-ir.d.ts +32 -2
- package/dist/lib/rego-ir.d.ts.map +1 -1
- package/dist/lib/rego-smt-encoder.d.ts +53 -5
- package/dist/lib/rego-smt-encoder.d.ts.map +1 -1
- package/dist/lib/rego-smt-encoder.js +192 -26
- package/dist/lib/rego-smt-encoder.js.map +1 -1
- package/dist/lib/rego-type-inferencer.d.ts +1 -1
- package/dist/lib/rego-type-inferencer.d.ts.map +1 -1
- package/dist/lib/rego-type-inferencer.js +3 -3
- package/dist/lib/rego-type-inferencer.js.map +1 -1
- package/dist/lib/rego-verify-engine.d.ts.map +1 -1
- package/dist/lib/rego-verify-engine.js +109 -77
- package/dist/lib/rego-verify-engine.js.map +1 -1
- package/dist/lib/rego-z3.d.ts +10 -0
- package/dist/lib/rego-z3.d.ts.map +1 -1
- package/dist/lib/rego-z3.js +38 -0
- package/dist/lib/rego-z3.js.map +1 -1
- package/dist/lib/security.d.ts +8 -0
- package/dist/lib/security.d.ts.map +1 -1
- package/dist/lib/security.js +130 -54
- package/dist/lib/security.js.map +1 -1
- package/dist/lib/subprocess.d.ts +36 -3
- package/dist/lib/subprocess.d.ts.map +1 -1
- package/dist/lib/subprocess.js +121 -20
- package/dist/lib/subprocess.js.map +1 -1
- package/dist/lib/tool-helpers.d.ts.map +1 -1
- package/dist/lib/tool-helpers.js +10 -0
- package/dist/lib/tool-helpers.js.map +1 -1
- package/dist/resources/patterns.d.ts +1 -1
- package/dist/resources/patterns.d.ts.map +1 -1
- package/dist/resources/patterns.js +40 -12
- package/dist/resources/patterns.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +10 -2
- package/dist/server.js.map +1 -1
- package/dist/tools/authoring/check-schema.js +1 -1
- package/dist/tools/authoring/check-schema.js.map +1 -1
- package/dist/tools/bundles/build.d.ts.map +1 -1
- package/dist/tools/bundles/build.js +12 -4
- package/dist/tools/bundles/build.js.map +1 -1
- package/dist/tools/bundles/sign.d.ts +9 -2
- package/dist/tools/bundles/sign.d.ts.map +1 -1
- package/dist/tools/bundles/sign.js +145 -19
- package/dist/tools/bundles/sign.js.map +1 -1
- package/dist/tools/bundles/verify.d.ts +1 -1
- package/dist/tools/bundles/verify.d.ts.map +1 -1
- package/dist/tools/bundles/verify.js +58 -19
- package/dist/tools/bundles/verify.js.map +1 -1
- package/dist/tools/conftest/pull.d.ts.map +1 -1
- package/dist/tools/conftest/pull.js +24 -10
- package/dist/tools/conftest/pull.js.map +1 -1
- package/dist/tools/conftest/push.d.ts.map +1 -1
- package/dist/tools/conftest/push.js +17 -7
- package/dist/tools/conftest/push.js.map +1 -1
- package/dist/tools/conftest/test.d.ts +10 -6
- package/dist/tools/conftest/test.d.ts.map +1 -1
- package/dist/tools/conftest/test.js +47 -24
- package/dist/tools/conftest/test.js.map +1 -1
- package/dist/tools/conftest/verify.d.ts +10 -6
- package/dist/tools/conftest/verify.d.ts.map +1 -1
- package/dist/tools/conftest/verify.js +30 -18
- package/dist/tools/conftest/verify.js.map +1 -1
- package/dist/tools/evaluation/bench.d.ts +16 -0
- package/dist/tools/evaluation/bench.d.ts.map +1 -1
- package/dist/tools/evaluation/bench.js +30 -5
- package/dist/tools/evaluation/bench.js.map +1 -1
- package/dist/tools/evaluation/exec.d.ts +16 -0
- package/dist/tools/evaluation/exec.d.ts.map +1 -1
- package/dist/tools/evaluation/exec.js +35 -2
- package/dist/tools/evaluation/exec.js.map +1 -1
- package/dist/tools/evaluation/test-multiroot.d.ts +4 -0
- package/dist/tools/evaluation/test-multiroot.d.ts.map +1 -1
- package/dist/tools/evaluation/test-multiroot.js +21 -14
- package/dist/tools/evaluation/test-multiroot.js.map +1 -1
- package/dist/tools/evaluation/test.d.ts +45 -0
- package/dist/tools/evaluation/test.d.ts.map +1 -1
- package/dist/tools/evaluation/test.js +146 -27
- package/dist/tools/evaluation/test.js.map +1 -1
- package/dist/tools/helpers/coverage-gaps.d.ts +11 -3
- package/dist/tools/helpers/coverage-gaps.d.ts.map +1 -1
- package/dist/tools/helpers/coverage-gaps.js +4 -3
- package/dist/tools/helpers/coverage-gaps.js.map +1 -1
- package/dist/tools/helpers/explain-undefined.d.ts +9 -3
- package/dist/tools/helpers/explain-undefined.d.ts.map +1 -1
- package/dist/tools/helpers/explain-undefined.js +83 -51
- package/dist/tools/helpers/explain-undefined.js.map +1 -1
- package/dist/tools/helpers/verify.d.ts.map +1 -1
- package/dist/tools/helpers/verify.js +5 -1
- package/dist/tools/helpers/verify.js.map +1 -1
- package/dist/tools/server-management/_shared.d.ts +15 -4
- package/dist/tools/server-management/_shared.d.ts.map +1 -1
- package/dist/tools/server-management/_shared.js +73 -12
- package/dist/tools/server-management/_shared.js.map +1 -1
- package/dist/tools/server-management/data.d.ts.map +1 -1
- package/dist/tools/server-management/data.js +88 -23
- package/dist/tools/server-management/data.js.map +1 -1
- package/dist/tools/server-management/decisions.d.ts.map +1 -1
- package/dist/tools/server-management/decisions.js +16 -3
- package/dist/tools/server-management/decisions.js.map +1 -1
- package/dist/tools/server-management/policies.d.ts.map +1 -1
- package/dist/tools/server-management/policies.js +44 -7
- package/dist/tools/server-management/policies.js.map +1 -1
- package/dist/tools/server-management/status.d.ts.map +1 -1
- package/dist/tools/server-management/status.js +80 -13
- package/dist/tools/server-management/status.js.map +1 -1
- package/dist/types.d.ts +10 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
|
@@ -28,14 +28,17 @@ const OpaBundleBuildInput = {
|
|
|
28
28
|
.array(z.string())
|
|
29
29
|
.optional()
|
|
30
30
|
.describe('Entrypoint refs (required when `target=wasm` or `optimize > 0`).'),
|
|
31
|
-
signingKey: z
|
|
31
|
+
signingKey: z
|
|
32
|
+
.string()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe('Path to a PEM private key for signing the built bundle (`--signing-key`). Implies `bundle: true`, which OPA requires for signing.'),
|
|
32
35
|
signingAlg: z.string().optional().describe('Signing algorithm (e.g. RS256).'),
|
|
33
36
|
claimsFile: z.string().optional().describe('Path to a claims file for inline signing.'),
|
|
34
37
|
capabilities: z.string().optional().describe('Path to a capabilities JSON file.'),
|
|
35
38
|
bundle: z
|
|
36
39
|
.boolean()
|
|
37
40
|
.optional()
|
|
38
|
-
.describe('Load `paths` as bundle files or root directories (`--bundle`).
|
|
41
|
+
.describe('Load `paths` as bundle files or root directories (`--bundle`). Implied by `signingKey` and `verificationKey`; set it explicitly to rebuild an existing bundle without signing.'),
|
|
39
42
|
pruneUnused: z
|
|
40
43
|
.boolean()
|
|
41
44
|
.optional()
|
|
@@ -51,7 +54,7 @@ const OpaBundleBuildInput = {
|
|
|
51
54
|
verificationKey: z
|
|
52
55
|
.string()
|
|
53
56
|
.optional()
|
|
54
|
-
.describe('Path to a PEM public key (or HMAC secret file) used to re-verify an existing signed bundle during the build (`--verification-key`).
|
|
57
|
+
.describe('Path to a PEM public key (or HMAC secret file) used to re-verify an existing signed bundle during the build (`--verification-key`). Implies `bundle: true`, which OPA requires for verification.'),
|
|
55
58
|
verificationKeyId: z
|
|
56
59
|
.string()
|
|
57
60
|
.optional()
|
|
@@ -117,7 +120,12 @@ export function registerOpaBundleBuild(server, config) {
|
|
|
117
120
|
signingAlg: input.signingAlg,
|
|
118
121
|
claimsFile: resolvedClaimsFile,
|
|
119
122
|
capabilities: resolvedCapabilities,
|
|
120
|
-
|
|
123
|
+
// opa build refuses --signing-key and --verification-key outside
|
|
124
|
+
// bundle mode ("enable bundle mode (ie. --bundle) to verify or sign
|
|
125
|
+
// bundle files or directories"), so either option implies it.
|
|
126
|
+
bundle: input.bundle === true ||
|
|
127
|
+
input.signingKey !== undefined ||
|
|
128
|
+
input.verificationKey !== undefined,
|
|
121
129
|
pruneUnused: input.pruneUnused,
|
|
122
130
|
ignore: input.ignore,
|
|
123
131
|
v1Compatible: input.v1Compatible,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/tools/bundles/build.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElG,MAAM,mBAAmB,GAAG;IAC1B,KAAK,EAAE,CAAC;SACL,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,kEAAkE,CAAC;IAC/E,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CAAC,wEAAwE,CAAC;IACrF,QAAQ,EAAE,CAAC;SACR,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;SACjD,QAAQ,EAAE;SACV,QAAQ,CAAC,gDAAgD,CAAC;IAC7D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,iDAAiD,CAAC;IAC3F,MAAM,EAAE,CAAC;SACN,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;SACtB,QAAQ,EAAE;SACV,QAAQ,CAAC,gEAAgE,CAAC;IAC7E,WAAW,EAAE,CAAC;SACX,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,QAAQ,EAAE;SACV,QAAQ,CAAC,kEAAkE,CAAC;IAC/E,UAAU,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../../src/tools/bundles/build.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { Config } from '../../config.js';
|
|
3
3
|
export interface OpaBundleSignOutput {
|
|
4
|
-
signed:
|
|
5
|
-
|
|
4
|
+
signed: true;
|
|
5
|
+
/** Absolute path of the `.signatures.json` that was written. */
|
|
6
|
+
signaturesPath: string;
|
|
7
|
+
/** JWT algorithm recorded in the signature header. */
|
|
8
|
+
algorithm: string;
|
|
9
|
+
/** Number of files the signature covers. */
|
|
10
|
+
filesSigned: number;
|
|
11
|
+
keyId?: string;
|
|
12
|
+
scope?: string;
|
|
6
13
|
}
|
|
7
14
|
export declare function registerOpaBundleSign(server: McpServer, config: Config): void;
|
|
8
15
|
//# sourceMappingURL=sign.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../../../src/tools/bundles/sign.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../../../src/tools/bundles/sign.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AA+C9C,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,IAAI,CAAC;IACb,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAWD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA+J7E"}
|
|
@@ -1,25 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* via `opa sign`.
|
|
4
|
-
*/
|
|
1
|
+
import { lstat, readlink, realpath, stat } from 'node:fs/promises';
|
|
2
|
+
import { basename, dirname, join } from 'node:path';
|
|
5
3
|
import { z } from 'zod';
|
|
6
|
-
import {
|
|
4
|
+
import { isKeyOrAlgorithmError, readSignaturesSummary } from '../../lib/bundle-signatures.js';
|
|
7
5
|
import { err, ok } from '../../lib/errors.js';
|
|
6
|
+
import { OpaCli } from '../../lib/opa-cli.js';
|
|
8
7
|
import { mapSubprocessFailure, validatePaths, withToolEnvelope } from '../../lib/tool-helpers.js';
|
|
8
|
+
const SIGNATURES_FILE = '.signatures.json';
|
|
9
|
+
/**
|
|
10
|
+
* How far before the call a pre-existing `.signatures.json` may have been
|
|
11
|
+
* modified and still count as written by this call. Filesystems with coarse
|
|
12
|
+
* timestamps (FAT: 2s, HFS+: 1s) can report an unchanged mtime for a rewrite
|
|
13
|
+
* inside the same tick, and a clock only ever compares against itself here.
|
|
14
|
+
*/
|
|
15
|
+
const MTIME_SLACK_MS = 2000;
|
|
9
16
|
const OpaBundleSignInput = {
|
|
10
17
|
bundle: z
|
|
11
18
|
.string()
|
|
12
19
|
.min(1)
|
|
13
|
-
.describe('Path to a bundle directory or archive. Must be
|
|
14
|
-
signingKey: z
|
|
15
|
-
|
|
16
|
-
|
|
20
|
+
.describe('Path to a bundle directory or `.tar.gz` archive. Must be inside an allowed root.'),
|
|
21
|
+
signingKey: z
|
|
22
|
+
.string()
|
|
23
|
+
.min(1)
|
|
24
|
+
.describe('Path to the PEM private key (RSA or ECDSA), or for HMAC algorithms a file holding the secret. Must be inside an allowed root.'),
|
|
25
|
+
signingAlg: z
|
|
26
|
+
.string()
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('Signing algorithm: RS256 (default), RS384, RS512, PS256, PS384, PS512, ES256, ES384, ES512, HS256, HS384, HS512.'),
|
|
29
|
+
claimsFile: z
|
|
30
|
+
.string()
|
|
31
|
+
.optional()
|
|
32
|
+
.describe('Path to a JSON file of extra claims to sign, such as {"keyid": "...", "scope": "..."}. Must be inside an allowed root.'),
|
|
33
|
+
outputDir: z
|
|
34
|
+
.string()
|
|
35
|
+
.optional()
|
|
36
|
+
.describe("For an archive, the directory that receives `.signatures.json`; defaults to the archive's own directory. Must exist and be inside an allowed root. Not accepted for a directory bundle, which is signed in place."),
|
|
17
37
|
};
|
|
38
|
+
/** lstat, so a symlink is reported as itself rather than as its target. */
|
|
39
|
+
async function lstatOrUndefined(path) {
|
|
40
|
+
try {
|
|
41
|
+
return await lstat(path);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
18
47
|
export function registerOpaBundleSign(server, config) {
|
|
19
48
|
const opa = new OpaCli(config);
|
|
20
49
|
server.registerTool('opa_bundle_sign', {
|
|
21
50
|
title: 'Sign OPA bundle',
|
|
22
|
-
description: 'Sign
|
|
51
|
+
description: 'Sign a bundle directory or `.tar.gz` archive with `opa sign`. ' +
|
|
52
|
+
'A directory is signed in place: `.signatures.json` is written into it and files are recorded as `<directory name>/<file>`, so the signed directory verifies wherever it is placed as long as its name is unchanged, with `opa_bundle_verify` or with `opa build` or `opa run --bundle <name>` from its parent. ' +
|
|
53
|
+
"For an archive the signature is written beside it, into `outputDir` or the archive's own directory, and the archive is not modified; a signed archive comes from `opa_bundle_build` with `signingKey`. " +
|
|
54
|
+
'The key is a PEM private key (RSA or ECDSA); for HMAC algorithms pass a file holding the secret. Extra claims such as `keyid` and `scope` come from `claimsFile`. ' +
|
|
55
|
+
'Returns the path written, the algorithm, and the number of files covered.',
|
|
23
56
|
inputSchema: OpaBundleSignInput,
|
|
24
57
|
annotations: {
|
|
25
58
|
readOnlyHint: false,
|
|
@@ -32,21 +65,114 @@ export function registerOpaBundleSign(server, config) {
|
|
|
32
65
|
const validation = validatePaths([input.bundle, input.signingKey, ...(input.claimsFile ? [input.claimsFile] : [])], config, { mustExist: true });
|
|
33
66
|
if (!validation.ok)
|
|
34
67
|
return validation.error;
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
68
|
+
const [resolvedBundle, resolvedKey, resolvedClaims] = validation.resolved;
|
|
69
|
+
// validatePath returns the path as given once its real target is
|
|
70
|
+
// inside an allowed root. OPA's directory walk does not descend a
|
|
71
|
+
// symlink or junction root, so a linked bundle would sign as an empty
|
|
72
|
+
// file list; the real path is what gets signed.
|
|
73
|
+
const realBundle = await realpath(resolvedBundle);
|
|
74
|
+
const isDirectory = (await stat(realBundle)).isDirectory();
|
|
75
|
+
let outputDir;
|
|
76
|
+
if (isDirectory) {
|
|
77
|
+
if (input.outputDir !== undefined) {
|
|
78
|
+
return err('INVALID_INPUT', `outputDir applies to archives only. A directory bundle is signed in place, since OPA reads ${SIGNATURES_FILE} from inside the bundle and cannot use a detached one.`, { details: { bundle: input.bundle } });
|
|
79
|
+
}
|
|
80
|
+
outputDir = realBundle;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// The archive's own directory is derived from the path as given,
|
|
84
|
+
// not from the real path: an allowed root that is itself a symlink
|
|
85
|
+
// or a Windows short name (a macOS /var, an 8.3 temp directory)
|
|
86
|
+
// would otherwise fail the syntactic containment check against its
|
|
87
|
+
// own canonical form. Validation resolves the symlink itself.
|
|
88
|
+
const requested = input.outputDir ?? dirname(resolvedBundle);
|
|
89
|
+
const outputValidation = validatePaths([requested], config, { mustExist: true });
|
|
90
|
+
if (!outputValidation.ok)
|
|
91
|
+
return outputValidation.error;
|
|
92
|
+
outputDir = outputValidation.resolved[0];
|
|
93
|
+
if (!(await stat(outputDir)).isDirectory()) {
|
|
94
|
+
return err('INVALID_INPUT', 'outputDir must be an existing directory.', {
|
|
95
|
+
details: { outputDir },
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const signaturesPath = join(outputDir, SIGNATURES_FILE);
|
|
100
|
+
// The directory is inside an allowed root, but the file inside it need
|
|
101
|
+
// not be: a pre-existing `.signatures.json` that is a symlink would
|
|
102
|
+
// have opa write through it to wherever it points.
|
|
103
|
+
const before = await lstatOrUndefined(signaturesPath);
|
|
104
|
+
if (before?.isSymbolicLink()) {
|
|
105
|
+
return err('PATH_NOT_ALLOWED', `${SIGNATURES_FILE} at the output location is a symbolic link; refusing to write through it.`, { details: { signaturesPath } });
|
|
106
|
+
}
|
|
107
|
+
const startedAt = Date.now();
|
|
108
|
+
const signInput = {
|
|
109
|
+
bundle: isDirectory ? basename(realBundle) : realBundle,
|
|
110
|
+
signingKey: resolvedKey,
|
|
111
|
+
outputDir,
|
|
112
|
+
};
|
|
113
|
+
if (isDirectory)
|
|
114
|
+
signInput.cwd = dirname(realBundle);
|
|
115
|
+
if (input.signingAlg !== undefined)
|
|
116
|
+
signInput.signingAlg = input.signingAlg;
|
|
117
|
+
if (resolvedClaims !== undefined)
|
|
118
|
+
signInput.claimsFile = resolvedClaims;
|
|
119
|
+
const result = await opa.sign(signInput, signal);
|
|
41
120
|
const subprocessFailure = mapSubprocessFailure(result, 'opa');
|
|
42
121
|
if (subprocessFailure)
|
|
43
122
|
return subprocessFailure;
|
|
123
|
+
// opa prints its own errors on stdout, so both streams are kept.
|
|
124
|
+
const streams = { stderr: result.stderr.trim(), stdout: result.stdout.trim() };
|
|
44
125
|
if (result.exitCode !== 0) {
|
|
45
|
-
|
|
46
|
-
|
|
126
|
+
if (isKeyOrAlgorithmError(`${result.stdout}\n${result.stderr}`)) {
|
|
127
|
+
return err('INVALID_INPUT', 'The signing key or algorithm could not be used.', {
|
|
128
|
+
hint: 'signingKey must be a PEM private key, or for HS256, HS384 and HS512 a file holding the secret, and signingAlg one OPA supports.',
|
|
129
|
+
details: streams,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
return err('INVALID_BUNDLE', 'opa sign failed.', { details: streams });
|
|
133
|
+
}
|
|
134
|
+
// Exit 0 is not the claim; the file is. A signature that was not
|
|
135
|
+
// written, or a stale one from an earlier run, must not be reported
|
|
136
|
+
// as this call's work.
|
|
137
|
+
const after = await lstatOrUndefined(signaturesPath);
|
|
138
|
+
if (after?.isSymbolicLink()) {
|
|
139
|
+
// A link planted between the check above and opa's write has
|
|
140
|
+
// already been followed. Say so, and do not report success.
|
|
141
|
+
const target = await readlink(signaturesPath).catch(() => undefined);
|
|
142
|
+
return err('PATH_NOT_ALLOWED', `${SIGNATURES_FILE} at the output location became a symbolic link while opa ran; the signature may have been written to its target. Not reporting success.`, { details: { signaturesPath, target } });
|
|
143
|
+
}
|
|
144
|
+
const written = after !== undefined &&
|
|
145
|
+
(before === undefined || after.mtimeMs >= startedAt - MTIME_SLACK_MS);
|
|
146
|
+
if (!written) {
|
|
147
|
+
return err('UNKNOWN_ERROR', `opa sign exited 0 but did not write ${SIGNATURES_FILE}.`, {
|
|
148
|
+
details: { signaturesPath, ...streams },
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
let summary;
|
|
152
|
+
try {
|
|
153
|
+
summary = await readSignaturesSummary(signaturesPath);
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
return err('UNKNOWN_ERROR', `opa sign wrote a ${SIGNATURES_FILE} this tool could not read.`, { details: { signaturesPath, error: e instanceof Error ? e.message : String(e) } });
|
|
157
|
+
}
|
|
158
|
+
// A signature over nothing is what an empty directory produces. It
|
|
159
|
+
// verifies nothing and is not worth reporting as success.
|
|
160
|
+
if (summary.filesSigned === 0) {
|
|
161
|
+
return err('INVALID_BUNDLE', 'opa sign covered no files: the bundle is empty.', {
|
|
162
|
+
details: { signaturesPath },
|
|
47
163
|
});
|
|
48
164
|
}
|
|
49
|
-
|
|
165
|
+
const output = {
|
|
166
|
+
signed: true,
|
|
167
|
+
signaturesPath,
|
|
168
|
+
algorithm: summary.algorithm,
|
|
169
|
+
filesSigned: summary.filesSigned,
|
|
170
|
+
};
|
|
171
|
+
if (summary.keyId !== undefined)
|
|
172
|
+
output.keyId = summary.keyId;
|
|
173
|
+
if (summary.scope !== undefined)
|
|
174
|
+
output.scope = summary.scope;
|
|
175
|
+
return ok(output);
|
|
50
176
|
});
|
|
51
177
|
});
|
|
52
178
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign.js","sourceRoot":"","sources":["../../../src/tools/bundles/sign.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sign.js","sourceRoot":"","sources":["../../../src/tools/bundles/sign.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
2
2
|
import type { Config } from '../../config.js';
|
|
3
3
|
export interface OpaBundleVerifyOutput {
|
|
4
4
|
bundle: string;
|
|
5
|
-
verified:
|
|
5
|
+
verified: true;
|
|
6
6
|
}
|
|
7
7
|
export declare function registerOpaBundleVerify(server: McpServer, config: Config): void;
|
|
8
8
|
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/tools/bundles/verify.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../../../src/tools/bundles/verify.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AA6C9C,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,IAAI,CAAC;CAChB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAiF/E"}
|
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `opa_bundle_verify` -- verify the
|
|
3
|
-
*
|
|
2
|
+
* `opa_bundle_verify` -- verify the signature of a signed OPA bundle.
|
|
3
|
+
*
|
|
4
|
+
* OPA has no standalone verify command. The check runs as
|
|
5
|
+
* `opa build --verification-key` into a temp file that is discarded; see
|
|
6
|
+
* `OpaCli.bundleVerify`. A directory is verified by name from its parent, the
|
|
7
|
+
* same way `opa_bundle_sign` signs it. OPA reports the first failure it hits,
|
|
8
|
+
* and the reason is classified into `details.reason` so a caller can tell a
|
|
9
|
+
* wrong key from a tampered file.
|
|
4
10
|
*/
|
|
11
|
+
import { realpath, stat } from 'node:fs/promises';
|
|
12
|
+
import { basename, dirname } from 'node:path';
|
|
5
13
|
import { z } from 'zod';
|
|
6
|
-
import {
|
|
14
|
+
import { classifyVerificationFailure } from '../../lib/bundle-signatures.js';
|
|
7
15
|
import { err, ok } from '../../lib/errors.js';
|
|
16
|
+
import { OpaCli } from '../../lib/opa-cli.js';
|
|
8
17
|
import { mapSubprocessFailure, validatePaths, withToolEnvelope } from '../../lib/tool-helpers.js';
|
|
9
18
|
const OpaBundleVerifyInput = {
|
|
10
19
|
bundle: z
|
|
@@ -14,11 +23,11 @@ const OpaBundleVerifyInput = {
|
|
|
14
23
|
verificationKey: z
|
|
15
24
|
.string()
|
|
16
25
|
.min(1)
|
|
17
|
-
.describe('Path to the PEM file containing the RSA or ECDSA public key, or
|
|
26
|
+
.describe('Path to the PEM file containing the RSA or ECDSA public key, or for HMAC algorithms a file holding the secret. Must be inside an allowed root.'),
|
|
18
27
|
verificationKeyId: z
|
|
19
28
|
.string()
|
|
20
29
|
.optional()
|
|
21
|
-
.describe('
|
|
30
|
+
.describe('Name the key is registered under for OPA (`--verification-key-id`, default `default`). With a single key OPA verifies against it regardless of the signature keyid claim, so this rarely needs setting.'),
|
|
22
31
|
signingAlg: z
|
|
23
32
|
.string()
|
|
24
33
|
.optional()
|
|
@@ -26,13 +35,24 @@ const OpaBundleVerifyInput = {
|
|
|
26
35
|
scope: z
|
|
27
36
|
.string()
|
|
28
37
|
.optional()
|
|
29
|
-
.describe('Expected `scope`
|
|
38
|
+
.describe('Expected `scope` claim in the signature. Pass exactly the value the bundle was signed with, and nothing if it was signed without one; the failure reason is scope_mismatch otherwise.'),
|
|
39
|
+
v0Compatible: z
|
|
40
|
+
.boolean()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe('Load the bundle as Rego v0 (`--v0-compatible`). A policy written before Rego v1 otherwise fails to load, after the signature and digests have already been checked.'),
|
|
30
43
|
};
|
|
31
44
|
export function registerOpaBundleVerify(server, config) {
|
|
32
45
|
const opa = new OpaCli(config);
|
|
33
46
|
server.registerTool('opa_bundle_verify', {
|
|
34
47
|
title: 'Verify OPA bundle signature',
|
|
35
|
-
description: 'Verify the
|
|
48
|
+
description: 'Verify the signature of a signed bundle directory or `.tar.gz` archive with the public key. ' +
|
|
49
|
+
'OPA has no standalone verify command, so this runs `opa build --verification-key` into a private temp file that is discarded. ' +
|
|
50
|
+
'A directory is verified by name from its parent, matching how `opa_bundle_sign` signs it. ' +
|
|
51
|
+
'OPA reads the key, checks the JWT in `.signatures.json`, compares the scope claim, then checks every file: Rego files by digest before parsing, data files and `.manifest` by parsed value, so an unparseable data file fails before its digest is compared. ' +
|
|
52
|
+
'Failures return `INVALID_BUNDLE` with `details.reason` set to one of signature_invalid, scope_mismatch, file_modified, file_added, file_missing, file_unparseable, unsigned, signatures_malformed, not_a_bundle, bundle_load_error, or unknown when the message is not recognised; the raw output is in `details`. ' +
|
|
53
|
+
'A key or algorithm OPA cannot use returns `INVALID_INPUT`. ' +
|
|
54
|
+
'Pass `scope` exactly as the bundle was signed with. With a single key OPA does not check `verificationKeyId` against the signature keyid claim. ' +
|
|
55
|
+
'`verified: true` is returned only when OPA loaded the bundle with its signature intact.',
|
|
36
56
|
inputSchema: OpaBundleVerifyInput,
|
|
37
57
|
annotations: {
|
|
38
58
|
readOnlyHint: true,
|
|
@@ -48,23 +68,42 @@ export function registerOpaBundleVerify(server, config) {
|
|
|
48
68
|
if (!validation.ok)
|
|
49
69
|
return validation.error;
|
|
50
70
|
const [resolvedBundle, resolvedKey] = validation.resolved;
|
|
51
|
-
|
|
52
|
-
|
|
71
|
+
// Same resolution as the sign tool: OPA does not descend a symlink or
|
|
72
|
+
// junction root, and a directory's signature names files under the
|
|
73
|
+
// directory name, so a directory is loaded by name from its parent.
|
|
74
|
+
const realBundle = await realpath(resolvedBundle);
|
|
75
|
+
const isDirectory = (await stat(realBundle)).isDirectory();
|
|
76
|
+
const verifyInput = {
|
|
77
|
+
bundle: isDirectory ? basename(realBundle) : realBundle,
|
|
53
78
|
verificationKey: resolvedKey,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
79
|
+
};
|
|
80
|
+
if (isDirectory)
|
|
81
|
+
verifyInput.cwd = dirname(realBundle);
|
|
82
|
+
if (input.verificationKeyId !== undefined) {
|
|
83
|
+
verifyInput.verificationKeyId = input.verificationKeyId;
|
|
84
|
+
}
|
|
85
|
+
if (input.signingAlg !== undefined)
|
|
86
|
+
verifyInput.signingAlg = input.signingAlg;
|
|
87
|
+
if (input.scope !== undefined)
|
|
88
|
+
verifyInput.scope = input.scope;
|
|
89
|
+
if (input.v0Compatible)
|
|
90
|
+
verifyInput.v0Compatible = true;
|
|
91
|
+
const result = await opa.bundleVerify(verifyInput, signal);
|
|
58
92
|
const subprocessFailure = mapSubprocessFailure(result, 'opa');
|
|
59
93
|
if (subprocessFailure)
|
|
60
94
|
return subprocessFailure;
|
|
61
95
|
if (result.exitCode !== 0) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
96
|
+
// opa build prints the reason on stdout, not stderr.
|
|
97
|
+
const failure = classifyVerificationFailure(`${result.stdout}\n${result.stderr}`);
|
|
98
|
+
const details = {
|
|
99
|
+
reason: failure.reason,
|
|
100
|
+
stdout: result.stdout.trim(),
|
|
101
|
+
stderr: result.stderr.trim(),
|
|
102
|
+
};
|
|
103
|
+
if (failure.reason === 'key_invalid') {
|
|
104
|
+
return err('INVALID_INPUT', `The verification key or algorithm could not be used: ${failure.summary}.`, { hint: failure.hint, details });
|
|
105
|
+
}
|
|
106
|
+
return err('INVALID_BUNDLE', `Bundle signature verification failed: ${failure.summary}.`, { hint: failure.hint, details });
|
|
68
107
|
}
|
|
69
108
|
return ok({ bundle: input.bundle, verified: true });
|
|
70
109
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/tools/bundles/verify.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/tools/bundles/verify.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/tools/conftest/pull.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../../src/tools/conftest/pull.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AA4B9C,MAAM,WAAW,kBAAkB;IACjC,+BAA+B;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAuE5E"}
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* 0 -- pull succeeded
|
|
18
18
|
* non-0 -- network / auth error
|
|
19
19
|
*/
|
|
20
|
+
import { mkdir } from 'node:fs/promises';
|
|
21
|
+
import { dirname, resolve } from 'node:path';
|
|
20
22
|
import { z } from 'zod';
|
|
21
23
|
import { ConftestCli } from '../../lib/conftest-cli.js';
|
|
22
24
|
import { err, ok } from '../../lib/errors.js';
|
|
@@ -35,8 +37,12 @@ const ConftestPullInput = {
|
|
|
35
37
|
.optional()
|
|
36
38
|
.describe('Local directory where the pulled policies will be written. ' +
|
|
37
39
|
'Must be inside an allowed root (OPA_MCP_ALLOWED_PATHS). ' +
|
|
38
|
-
|
|
40
|
+
'Omitted, it falls back to `policy` in the working directory of the server process, the conftest convention, which must itself sit inside an allowed root. ' +
|
|
41
|
+
'The directory is emptied before the pull, so do not point it at one holding anything ' +
|
|
42
|
+
'you want to keep.'),
|
|
39
43
|
};
|
|
44
|
+
/** Where conftest looks when `--policy` is not given, as an absolute path. */
|
|
45
|
+
const DEFAULT_POLICY_DIR = resolve('policy');
|
|
40
46
|
export function registerConftestPull(server, config) {
|
|
41
47
|
const conftest = new ConftestCli(config);
|
|
42
48
|
server.registerTool('conftest_pull', {
|
|
@@ -59,24 +65,32 @@ export function registerConftestPull(server, config) {
|
|
|
59
65
|
}, async (input, { signal }) => {
|
|
60
66
|
return withToolEnvelope(config, async () => {
|
|
61
67
|
// ── Path validation ──────────────────────────────────────────────
|
|
62
|
-
// The
|
|
63
|
-
//
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
// The directory does not have to exist yet; conftest creates it. An
|
|
69
|
+
// omitted path means conftest's own default, which it resolves against
|
|
70
|
+
// the working directory. This tool writes files, so that default is
|
|
71
|
+
// checked against the allow-list too rather than escaping it by being
|
|
72
|
+
// implicit.
|
|
73
|
+
const v = validatePaths([input.policy ?? DEFAULT_POLICY_DIR], config);
|
|
74
|
+
if (!v.ok) {
|
|
75
|
+
if (input.policy !== undefined)
|
|
68
76
|
return v.error;
|
|
69
|
-
|
|
77
|
+
return err('PATH_NOT_ALLOWED', 'The conftest default policy directory is outside the allowed roots.', {
|
|
78
|
+
hint: 'Pass `policy` with a directory inside OPA_MCP_ALLOWED_PATHS.',
|
|
79
|
+
details: { defaultPolicyDir: DEFAULT_POLICY_DIR },
|
|
80
|
+
});
|
|
70
81
|
}
|
|
82
|
+
const policyDir = v.resolved[0];
|
|
83
|
+
// conftest runs from the parent, so the parent has to exist.
|
|
84
|
+
await mkdir(dirname(policyDir), { recursive: true });
|
|
71
85
|
// ── Run conftest pull ────────────────────────────────────────────
|
|
72
|
-
const result = await conftest.pull({ url: input.url, policy:
|
|
86
|
+
const result = await conftest.pull({ url: input.url, policy: policyDir }, signal);
|
|
73
87
|
const subprocessFailure = mapSubprocessFailure(result, 'conftest');
|
|
74
88
|
if (subprocessFailure)
|
|
75
89
|
return subprocessFailure;
|
|
76
90
|
if (result.exitCode === 0) {
|
|
77
91
|
return ok({
|
|
78
92
|
url: input.url,
|
|
79
|
-
policyDir
|
|
93
|
+
policyDir,
|
|
80
94
|
});
|
|
81
95
|
}
|
|
82
96
|
const detail = result.stderr.trim() || result.stdout.trim();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../../src/tools/conftest/pull.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElG,MAAM,iBAAiB,GAAG;IACxB,GAAG,EAAE,CAAC;SACH,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,yCAAyC;QACvC,4CAA4C;QAC5C,qDAAqD;QACrD,uDAAuD;QACvD,gEAAgE,CACnE;IACH,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,6DAA6D;QAC3D,0DAA0D;QAC1D,
|
|
1
|
+
{"version":3,"file":"pull.js","sourceRoot":"","sources":["../../../src/tools/conftest/pull.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/tools/conftest/push.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/tools/conftest/push.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAyB9C,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,SAAS,EAAE,MAAM,CAAC;CACnB;AAKD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAkE5E"}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
* non-0 -- push error (auth failure, network error, etc.)
|
|
15
15
|
*/
|
|
16
16
|
import { z } from 'zod';
|
|
17
|
+
import { resolve } from 'node:path';
|
|
17
18
|
import { ConftestCli } from '../../lib/conftest-cli.js';
|
|
18
19
|
import { err, ok } from '../../lib/errors.js';
|
|
19
20
|
import { mapSubprocessFailure, validatePaths, withToolEnvelope } from '../../lib/tool-helpers.js';
|
|
@@ -30,8 +31,10 @@ const ConftestPushInput = {
|
|
|
30
31
|
.optional()
|
|
31
32
|
.describe('Path to the local directory containing Rego policies to push. ' +
|
|
32
33
|
'Must be inside an allowed root (OPA_MCP_ALLOWED_PATHS) and must exist. ' +
|
|
33
|
-
|
|
34
|
+
'Omitted, it falls back to `policy` in the working directory of the server process, the conftest convention, which must itself sit inside an allowed root.'),
|
|
34
35
|
};
|
|
36
|
+
/** Where conftest looks when `--policy` is not given, as an absolute path. */
|
|
37
|
+
const DEFAULT_POLICY_DIR = resolve('policy');
|
|
35
38
|
export function registerConftestPush(server, config) {
|
|
36
39
|
const conftest = new ConftestCli(config);
|
|
37
40
|
server.registerTool('conftest_push', {
|
|
@@ -51,21 +54,28 @@ export function registerConftestPush(server, config) {
|
|
|
51
54
|
}, async (input, { signal }) => {
|
|
52
55
|
return withToolEnvelope(config, async () => {
|
|
53
56
|
// ── Path validation ──────────────────────────────────────────────
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
// An omitted path means the conftest default, resolved against the
|
|
58
|
+
// working directory. This publishes whatever it finds there, so the
|
|
59
|
+
// default is checked against the allow-list like an explicit one.
|
|
60
|
+
const v = validatePaths([input.policy ?? DEFAULT_POLICY_DIR], config, { mustExist: true });
|
|
61
|
+
if (!v.ok) {
|
|
62
|
+
if (input.policy !== undefined)
|
|
57
63
|
return v.error;
|
|
58
|
-
|
|
64
|
+
return err('PATH_NOT_ALLOWED', 'The conftest default policy directory is outside the allowed roots.', {
|
|
65
|
+
hint: 'Pass `policy` with a directory inside OPA_MCP_ALLOWED_PATHS.',
|
|
66
|
+
details: { defaultPolicyDir: DEFAULT_POLICY_DIR },
|
|
67
|
+
});
|
|
59
68
|
}
|
|
69
|
+
const policyDir = v.resolved[0];
|
|
60
70
|
// ── Run conftest push ────────────────────────────────────────────
|
|
61
|
-
const result = await conftest.push({ repository: input.repository, policy:
|
|
71
|
+
const result = await conftest.push({ repository: input.repository, policy: policyDir }, signal);
|
|
62
72
|
const subprocessFailure = mapSubprocessFailure(result, 'conftest');
|
|
63
73
|
if (subprocessFailure)
|
|
64
74
|
return subprocessFailure;
|
|
65
75
|
if (result.exitCode === 0) {
|
|
66
76
|
return ok({
|
|
67
77
|
repository: input.repository,
|
|
68
|
-
policyDir
|
|
78
|
+
policyDir,
|
|
69
79
|
});
|
|
70
80
|
}
|
|
71
81
|
const detail = result.stderr.trim() || result.stdout.trim();
|