@orygn/opa-mcp 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +583 -4
- package/README.md +144 -98
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -3
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +14 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +91 -19
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +1 -6
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +17 -3
- package/dist/constants.js.map +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 +9 -2
- package/dist/lib/child-env.d.ts.map +1 -1
- package/dist/lib/child-env.js +38 -6
- package/dist/lib/child-env.js.map +1 -1
- 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 +233 -50
- package/dist/lib/conftest-cli.js.map +1 -1
- package/dist/lib/errors.d.ts +4 -5
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +15 -3
- package/dist/lib/errors.js.map +1 -1
- package/dist/lib/inline-paths.d.ts +37 -0
- package/dist/lib/inline-paths.d.ts.map +1 -0
- package/dist/lib/inline-paths.js +162 -0
- package/dist/lib/inline-paths.js.map +1 -0
- 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 +74 -16
- package/dist/lib/opa-cli.d.ts.map +1 -1
- package/dist/lib/opa-cli.js +107 -30
- package/dist/lib/opa-cli.js.map +1 -1
- package/dist/lib/opa-client.d.ts +27 -1
- package/dist/lib/opa-client.d.ts.map +1 -1
- package/dist/lib/opa-client.js +98 -14
- package/dist/lib/opa-client.js.map +1 -1
- package/dist/lib/opa-paths.d.ts +34 -0
- package/dist/lib/opa-paths.d.ts.map +1 -0
- package/dist/lib/opa-paths.js +101 -0
- package/dist/lib/opa-paths.js.map +1 -0
- package/dist/lib/output.d.ts.map +1 -1
- package/dist/lib/output.js +83 -10
- package/dist/lib/output.js.map +1 -1
- 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 +40 -6
- package/dist/lib/regal-cli.js.map +1 -1
- package/dist/lib/rego-ast-walker.d.ts +0 -6
- package/dist/lib/rego-ast-walker.d.ts.map +1 -1
- package/dist/lib/rego-ast-walker.js +181 -55
- package/dist/lib/rego-ast-walker.js.map +1 -1
- package/dist/lib/rego-counterexample.d.ts +15 -2
- package/dist/lib/rego-counterexample.d.ts.map +1 -1
- package/dist/lib/rego-counterexample.js +63 -15
- package/dist/lib/rego-counterexample.js.map +1 -1
- package/dist/lib/rego-input-path.d.ts +20 -0
- package/dist/lib/rego-input-path.d.ts.map +1 -0
- package/dist/lib/rego-input-path.js +70 -0
- package/dist/lib/rego-input-path.js.map +1 -0
- package/dist/lib/rego-ir.d.ts +34 -3
- package/dist/lib/rego-ir.d.ts.map +1 -1
- package/dist/lib/rego-smt-encoder.d.ts +88 -5
- package/dist/lib/rego-smt-encoder.d.ts.map +1 -1
- package/dist/lib/rego-smt-encoder.js +270 -31
- package/dist/lib/rego-smt-encoder.js.map +1 -1
- package/dist/lib/rego-type-inferencer.d.ts +27 -2
- package/dist/lib/rego-type-inferencer.d.ts.map +1 -1
- package/dist/lib/rego-type-inferencer.js +71 -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 +203 -90
- package/dist/lib/rego-verify-engine.js.map +1 -1
- package/dist/lib/rego-z3.d.ts +66 -0
- package/dist/lib/rego-z3.d.ts.map +1 -1
- package/dist/lib/rego-z3.js +312 -5
- 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 +13 -0
- package/dist/lib/subprocess.d.ts.map +1 -1
- package/dist/lib/subprocess.js +153 -18
- package/dist/lib/subprocess.js.map +1 -1
- package/dist/lib/tool-helpers.d.ts +12 -25
- package/dist/lib/tool-helpers.d.ts.map +1 -1
- package/dist/lib/tool-helpers.js +66 -40
- package/dist/lib/tool-helpers.js.map +1 -1
- package/dist/prompts/index.js +2 -2
- package/dist/resources/index.js +1 -1
- package/dist/resources/index.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 +42 -15
- package/dist/resources/patterns.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +50 -7
- package/dist/server.js.map +1 -1
- package/dist/tools/authoring/capabilities.d.ts +4 -0
- package/dist/tools/authoring/capabilities.d.ts.map +1 -1
- package/dist/tools/authoring/capabilities.js +41 -4
- package/dist/tools/authoring/capabilities.js.map +1 -1
- package/dist/tools/authoring/check-schema.d.ts +6 -0
- package/dist/tools/authoring/check-schema.d.ts.map +1 -1
- package/dist/tools/authoring/check-schema.js +56 -3
- package/dist/tools/authoring/check-schema.js.map +1 -1
- package/dist/tools/authoring/format.d.ts.map +1 -1
- package/dist/tools/authoring/format.js +2 -2
- package/dist/tools/authoring/format.js.map +1 -1
- package/dist/tools/authoring/lint.d.ts.map +1 -1
- package/dist/tools/authoring/lint.js +8 -6
- package/dist/tools/authoring/lint.js.map +1 -1
- package/dist/tools/authoring/migrate-v1.d.ts.map +1 -1
- package/dist/tools/authoring/migrate-v1.js +20 -4
- package/dist/tools/authoring/migrate-v1.js.map +1 -1
- package/dist/tools/authoring/parse.d.ts.map +1 -1
- package/dist/tools/authoring/parse.js +3 -3
- package/dist/tools/authoring/parse.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 +125 -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/index.d.ts +5 -0
- package/dist/tools/conftest/index.d.ts.map +1 -1
- package/dist/tools/conftest/index.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 +61 -39
- 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 +40 -32
- package/dist/tools/conftest/verify.js.map +1 -1
- package/dist/tools/evaluation/_shared.js +9 -12
- package/dist/tools/evaluation/_shared.js.map +1 -1
- package/dist/tools/evaluation/bench.d.ts +29 -2
- package/dist/tools/evaluation/bench.d.ts.map +1 -1
- package/dist/tools/evaluation/bench.js +53 -9
- package/dist/tools/evaluation/bench.js.map +1 -1
- package/dist/tools/evaluation/compile.js +4 -4
- package/dist/tools/evaluation/compile.js.map +1 -1
- package/dist/tools/evaluation/eval.d.ts.map +1 -1
- package/dist/tools/evaluation/eval.js +12 -16
- package/dist/tools/evaluation/eval.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 +38 -6
- package/dist/tools/evaluation/exec.js.map +1 -1
- package/dist/tools/evaluation/test-multiroot.d.ts +6 -0
- package/dist/tools/evaluation/test-multiroot.d.ts.map +1 -1
- package/dist/tools/evaluation/test-multiroot.js +35 -8
- 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 +153 -32
- 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 +7 -7
- package/dist/tools/helpers/coverage-gaps.js.map +1 -1
- package/dist/tools/helpers/describe-policy.d.ts.map +1 -1
- package/dist/tools/helpers/describe-policy.js +2 -2
- package/dist/tools/helpers/describe-policy.js.map +1 -1
- package/dist/tools/helpers/explain-decision.d.ts.map +1 -1
- package/dist/tools/helpers/explain-decision.js +3 -4
- package/dist/tools/helpers/explain-decision.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 +214 -73
- package/dist/tools/helpers/explain-undefined.js.map +1 -1
- package/dist/tools/helpers/fix.d.ts +11 -5
- package/dist/tools/helpers/fix.d.ts.map +1 -1
- package/dist/tools/helpers/fix.js +88 -45
- package/dist/tools/helpers/fix.js.map +1 -1
- package/dist/tools/helpers/generate-test-skeleton.d.ts.map +1 -1
- package/dist/tools/helpers/generate-test-skeleton.js +105 -21
- package/dist/tools/helpers/generate-test-skeleton.js.map +1 -1
- package/dist/tools/helpers/playground-share.d.ts +4 -2
- package/dist/tools/helpers/playground-share.d.ts.map +1 -1
- package/dist/tools/helpers/playground-share.js +13 -3
- package/dist/tools/helpers/playground-share.js.map +1 -1
- package/dist/tools/helpers/policy-diff.d.ts.map +1 -1
- package/dist/tools/helpers/policy-diff.js +3 -4
- package/dist/tools/helpers/policy-diff.js.map +1 -1
- package/dist/tools/helpers/security-audit.d.ts.map +1 -1
- package/dist/tools/helpers/security-audit.js +19 -19
- package/dist/tools/helpers/security-audit.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/meta/index.d.ts +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 +86 -13
- 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
|
@@ -67,10 +67,9 @@ export function registerRegoCoverageGaps(server, config) {
|
|
|
67
67
|
description: 'Run opa test --coverage and return a per-file breakdown of uncovered line ranges. Identifies which rules or branches are not yet exercised by tests. Files are sorted by coverage ascending so the worst-covered files appear first. Use threshold to limit the report to files below a target coverage percentage.',
|
|
68
68
|
inputSchema: RegoCoverageGapsInput,
|
|
69
69
|
annotations: {
|
|
70
|
-
readOnlyHint:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
openWorldHint: false,
|
|
70
|
+
readOnlyHint: false,
|
|
71
|
+
// Runs Rego supplied by the caller; a policy can reach the network through http.send.
|
|
72
|
+
openWorldHint: true,
|
|
74
73
|
},
|
|
75
74
|
}, async ({ paths, threshold, runPattern }, { signal }) => {
|
|
76
75
|
return withToolEnvelope(config, async () => {
|
|
@@ -100,6 +99,9 @@ export function registerRegoCoverageGaps(server, config) {
|
|
|
100
99
|
// passing records with any status field, so filtering for pass: true
|
|
101
100
|
// always produces zero. Subtraction is the correct approach.
|
|
102
101
|
const testsPassed = testRecords.length - testsFailed - testsSkipped;
|
|
102
|
+
// OPA emits no test records in coverage mode, so reporting three zeros
|
|
103
|
+
// stated that no tests ran when the truth is that it did not say.
|
|
104
|
+
const counts = testRecords.length > 0 ? { testsPassed, testsFailed, testsSkipped } : {};
|
|
103
105
|
const overallCoverage = coverageReport.coverage ?? 0;
|
|
104
106
|
const filesWithGaps = [];
|
|
105
107
|
for (const [file, report] of Object.entries(coverageReport.files ?? {})) {
|
|
@@ -124,9 +126,7 @@ export function registerRegoCoverageGaps(server, config) {
|
|
|
124
126
|
overallCoverage,
|
|
125
127
|
totalFiles: Object.keys(coverageReport.files ?? {}).length,
|
|
126
128
|
filesWithGaps,
|
|
127
|
-
|
|
128
|
-
testsFailed,
|
|
129
|
-
testsSkipped,
|
|
129
|
+
...counts,
|
|
130
130
|
});
|
|
131
131
|
});
|
|
132
132
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage-gaps.js","sourceRoot":"","sources":["../../../src/tools/helpers/coverage-gaps.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;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,EACL,oBAAoB,EACpB,YAAY,EACZ,aAAa,EACb,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,qBAAqB,GAAG;IAC5B,KAAK,EAAE,CAAC;SACL,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;SACjB,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,CACP,qFAAqF,CACtF;IACH,SAAS,EAAE,CAAC;SACT,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,GAAG,CAAC;SACR,QAAQ,EAAE;SACV,QAAQ,CACP,uHAAuH,CACxH;IACH,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,8CAA8C,CAAC;CAC3F,CAAC;
|
|
1
|
+
{"version":3,"file":"coverage-gaps.js","sourceRoot":"","sources":["../../../src/tools/helpers/coverage-gaps.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-policy.d.ts","sourceRoot":"","sources":["../../../src/tools/helpers/describe-policy.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"describe-policy.d.ts","sourceRoot":"","sources":["../../../src/tools/helpers/describe-policy.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAmB9C,UAAU,cAAc;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,OAAO,EAAE,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC;CAC3B;AAqBD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,SAAS,EAAE,OAAO,CAAC;IACnB,iEAAiE;IACjE,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,cAAc,CAAC;CAC9B;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,kBAAkB,CAAC,EAAE,cAAc,EAAE,CAAC;CACvC;AAUD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA6ElF"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { OpaCli } from '../../lib/opa-cli.js';
|
|
7
7
|
import { err, ok } from '../../lib/errors.js';
|
|
8
|
-
import { mapSubprocessFailure, tryParseJson, withToolEnvelope } from '../../lib/tool-helpers.js';
|
|
8
|
+
import { mapSubprocessFailure, sanitizeInlinePathsDeep, tryParseJson, withToolEnvelope, } from '../../lib/tool-helpers.js';
|
|
9
9
|
const RegoDescribePolicyInput = {
|
|
10
10
|
source: z.string().min(1).describe('Rego source to describe.'),
|
|
11
11
|
};
|
|
@@ -37,7 +37,7 @@ export function registerRegoDescribePolicy(server, config) {
|
|
|
37
37
|
return subprocessFailure;
|
|
38
38
|
if (result.exitCode !== 0) {
|
|
39
39
|
return err('INVALID_REGO', 'opa parse rejected the source.', {
|
|
40
|
-
details: { stderr: result.stderr.trim() },
|
|
40
|
+
details: sanitizeInlinePathsDeep({ stderr: result.stderr.trim() }),
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
43
|
const ast = tryParseJson(result.stdout);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-policy.js","sourceRoot":"","sources":["../../../src/tools/helpers/describe-policy.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,
|
|
1
|
+
{"version":3,"file":"describe-policy.js","sourceRoot":"","sources":["../../../src/tools/helpers/describe-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explain-decision.d.ts","sourceRoot":"","sources":["../../../src/tools/helpers/explain-decision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAM9C,UAAU,UAAU;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAwCD,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"explain-decision.d.ts","sourceRoot":"","sources":["../../../src/tools/helpers/explain-decision.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAM9C,UAAU,UAAU;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAwCD,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAmDnF"}
|
|
@@ -47,10 +47,9 @@ export function registerRegoExplainDecision(server, config) {
|
|
|
47
47
|
description: 'Evaluate a Rego query with full tracing and return a structured trace plus per-rule fired/not-fired summary. Use this when you need to answer "why was this denied?" -- the agent reads the structured trace and narrates the cause without re-implementing the trace parser.',
|
|
48
48
|
inputSchema: SharedEvalInput,
|
|
49
49
|
annotations: {
|
|
50
|
-
readOnlyHint:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
openWorldHint: false,
|
|
50
|
+
readOnlyHint: false,
|
|
51
|
+
// Runs Rego supplied by the caller; a policy can reach the network through http.send.
|
|
52
|
+
openWorldHint: true,
|
|
54
53
|
},
|
|
55
54
|
}, async (args, { signal }) => {
|
|
56
55
|
return withToolEnvelope(config, async () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explain-decision.js","sourceRoot":"","sources":["../../../src/tools/helpers/explain-decision.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,eAAe,EAAuB,MAAM,0BAA0B,CAAC;AA4BzF,SAAS,eAAe,CAAC,IAAa;IACpC,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/E,OAAQ,IAAqC,CAAC,IAAI,EAAE,IAAI,CAAC;AAC3D,CAAC;AAED,SAAS,cAAc,CAAC,KAA+B;IAIrD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACzC,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI,EAAE,EAAE,CAAC;QAChC,MAAM,EAAE,GAAG,KAAK,CAAC,EAAE,EAAE,WAAW,EAAE,CAAC;QACnC,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC;YACnB,WAAW,IAAI,CAAC,CAAC;YACjB,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,IAAI;gBAAE,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrC,CAAC;aAAM,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;YACzB,UAAU,IAAI,CAAC,CAAC;YAChB,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,IAAI;gBAAE,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;aAAM,IAAI,EAAE,KAAK,MAAM,EAAE,CAAC;YACzB,UAAU,IAAI,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO;QACL,WAAW,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC;QAC/B,WAAW;QACX,UAAU;QACV,UAAU;QACV,cAAc;QACd,UAAU;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,2BAA2B,CAAC,MAAiB,EAAE,MAAc;IAC3E,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;IAE/B,MAAM,CAAC,YAAY,CACjB,uBAAuB,EACvB;QACE,KAAK,EAAE,uBAAuB;QAC9B,WAAW,EACT,+QAA+Q;QACjR,WAAW,EAAE,eAAe;QAC5B,WAAW,EAAE;YACX,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"explain-decision.js","sourceRoot":"","sources":["../../../src/tools/helpers/explain-decision.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,9 +33,15 @@ export interface RuleAnalysis {
|
|
|
33
33
|
source: 'trace' | 'standalone-eval';
|
|
34
34
|
}
|
|
35
35
|
export interface RegoExplainUndefinedOutput {
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
/**
|
|
37
|
+
* Whether the query produced a value.
|
|
38
|
+
*
|
|
39
|
+
* `default` means it produced one only because a `default` rule supplied it:
|
|
40
|
+
* no other definition of the rule matched. The per-rule breakdown says why,
|
|
41
|
+
* which is the same question `undefined` asks.
|
|
42
|
+
*/
|
|
43
|
+
queryResult: 'undefined' | 'defined' | 'default';
|
|
44
|
+
/** Present when queryResult is "defined" or "default". */
|
|
39
45
|
value?: unknown;
|
|
40
46
|
/** Human-readable explanation. */
|
|
41
47
|
summary: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"explain-undefined.d.ts","sourceRoot":"","sources":["../../../src/tools/helpers/explain-undefined.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"explain-undefined.d.ts","sourceRoot":"","sources":["../../../src/tools/helpers/explain-undefined.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAGzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AA+E9C,MAAM,WAAW,eAAe;IAC9B,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,4BAA4B;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,aAAa,CAAC;IACzC,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,SAAS,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,sDAAsD;IACtD,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,sEAAsE;IACtE,iBAAiB,EAAE,eAAe,GAAG,IAAI,CAAC;IAC1C,0CAA0C;IAC1C,MAAM,EAAE,OAAO,GAAG,iBAAiB,CAAC;CACrC;AAED,MAAM,WAAW,0BAA0B;IACzC;;;;;;OAMG;IACH,WAAW,EAAE,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;IACjD,0DAA0D;IAC1D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,0BAA0B;IAC1B,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB;AAsPD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CA6SpF"}
|
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
* For rules that appear in the trace (OPA entered them), the blocking
|
|
16
16
|
* condition is identified by matching Fail-event rows against body-
|
|
17
17
|
* expression rows from the AST. For rules that do not appear in the trace
|
|
18
|
-
* (indexed out),
|
|
19
|
-
* to
|
|
18
|
+
* (indexed out), the body is evaluated as cumulative prefixes inside the
|
|
19
|
+
* rule's package, with its imports, to find the first condition that does
|
|
20
|
+
* not hold.
|
|
20
21
|
*/
|
|
21
22
|
import { readFile } from 'node:fs/promises';
|
|
22
23
|
import { z } from 'zod';
|
|
@@ -75,20 +76,35 @@ function queryToPackageAndRule(query) {
|
|
|
75
76
|
packagePath: parts.slice(0, -1).join('.'),
|
|
76
77
|
};
|
|
77
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* The source row a trace event refers to.
|
|
81
|
+
*
|
|
82
|
+
* OPA does not populate `Node.location` on trace events; the row lives on the
|
|
83
|
+
* event's own `Location`. Reading only the node meant every row comparison fell
|
|
84
|
+
* through to matching on the rule name alone, so all clauses of a multi-clause
|
|
85
|
+
* rule looked present in the trace as soon as any one of them was, and the ones
|
|
86
|
+
* the indexer had eliminated were never evaluated standalone. They came back as
|
|
87
|
+
* `unevaluable` with no blocking condition, which is the answer this tool
|
|
88
|
+
* exists to give.
|
|
89
|
+
*/
|
|
90
|
+
function traceEventRow(ev) {
|
|
91
|
+
return ev.Node?.location?.row ?? ev.Location?.row;
|
|
92
|
+
}
|
|
93
|
+
function matchesRule(ev, ruleName, ruleRow) {
|
|
94
|
+
if (ev.Op?.toLowerCase() !== 'enter')
|
|
95
|
+
return false;
|
|
96
|
+
if (!ev.Node)
|
|
97
|
+
return false;
|
|
98
|
+
if (extractRuleHeadName(ev.Node.head) !== ruleName)
|
|
99
|
+
return false;
|
|
100
|
+
const row = traceEventRow(ev);
|
|
101
|
+
// Disambiguate the clauses of an incremental rule by source row.
|
|
102
|
+
if (ruleRow !== undefined && row !== undefined)
|
|
103
|
+
return row === ruleRow;
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
78
106
|
function ruleAppearsInTrace(ruleName, ruleRow, trace) {
|
|
79
|
-
return trace.some((ev) =>
|
|
80
|
-
if (ev.Op?.toLowerCase() !== 'enter')
|
|
81
|
-
return false;
|
|
82
|
-
if (!ev.Node)
|
|
83
|
-
return false;
|
|
84
|
-
if (extractRuleHeadName(ev.Node.head) !== ruleName)
|
|
85
|
-
return false;
|
|
86
|
-
// When we have source row information, use it to disambiguate incremental rules.
|
|
87
|
-
if (ruleRow !== undefined && ev.Node.location?.row !== undefined) {
|
|
88
|
-
return ev.Node.location.row === ruleRow;
|
|
89
|
-
}
|
|
90
|
-
return true;
|
|
91
|
-
});
|
|
107
|
+
return trace.some((ev) => matchesRule(ev, ruleName, ruleRow));
|
|
92
108
|
}
|
|
93
109
|
function findBlockingRowFromTrace(rule, trace) {
|
|
94
110
|
const ruleName = extractRuleHeadName(rule.head);
|
|
@@ -97,16 +113,7 @@ function findBlockingRowFromTrace(rule, trace) {
|
|
|
97
113
|
if (bodyRows.size === 0)
|
|
98
114
|
return undefined;
|
|
99
115
|
// Locate the Enter event for this specific rule definition.
|
|
100
|
-
const enterIdx = trace.findIndex((ev) =>
|
|
101
|
-
if (ev.Op?.toLowerCase() !== 'enter')
|
|
102
|
-
return false;
|
|
103
|
-
if (extractRuleHeadName(ev.Node?.head) !== ruleName)
|
|
104
|
-
return false;
|
|
105
|
-
if (ruleRow !== undefined && ev.Node?.location?.row !== undefined) {
|
|
106
|
-
return ev.Node.location.row === ruleRow;
|
|
107
|
-
}
|
|
108
|
-
return true;
|
|
109
|
-
});
|
|
116
|
+
const enterIdx = trace.findIndex((ev) => matchesRule(ev, ruleName ?? '', ruleRow));
|
|
110
117
|
if (enterIdx < 0)
|
|
111
118
|
return undefined;
|
|
112
119
|
// Find the first Fail event after the Enter that falls on a body expression row.
|
|
@@ -126,24 +133,66 @@ function findBlockingRowFromTrace(rule, trace) {
|
|
|
126
133
|
}
|
|
127
134
|
return undefined;
|
|
128
135
|
}
|
|
129
|
-
|
|
136
|
+
/**
|
|
137
|
+
* The module's import statements as `--import` takes them: the path as
|
|
138
|
+
* written, plus the alias when there is one. `--package` alone does not bring
|
|
139
|
+
* them, and a body expression through an import is unsafe without them.
|
|
140
|
+
*/
|
|
141
|
+
function moduleImports(ast) {
|
|
142
|
+
const out = [];
|
|
143
|
+
for (const imp of ast.imports ?? []) {
|
|
144
|
+
const path = decodeBase64Text(imp.path?.location?.text);
|
|
145
|
+
if (!path)
|
|
146
|
+
continue;
|
|
147
|
+
out.push(imp.alias ? `${path} as ${imp.alias}` : path);
|
|
148
|
+
}
|
|
149
|
+
return out;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Ceiling on the query text handed to opa. Windows caps a command line at
|
|
153
|
+
* 32 KiB, and the prefixes grow with every condition that held.
|
|
154
|
+
*/
|
|
155
|
+
const MAX_PREFIX_CHARS = 16_000;
|
|
156
|
+
/**
|
|
157
|
+
* Evaluate a prefix of a rule body as a query inside the rule's package. The
|
|
158
|
+
* prefix carries the locals assigned earlier, and the package makes a bare
|
|
159
|
+
* reference to a sibling rule resolve, so the answer is the one the rule
|
|
160
|
+
* itself would get up to that point.
|
|
161
|
+
*/
|
|
162
|
+
async function evalPrefixStandalone(prefixText, pkg, imports, evalBase, opa, signal) {
|
|
130
163
|
const result = await opa.eval({
|
|
131
|
-
query:
|
|
164
|
+
query: prefixText,
|
|
132
165
|
source: evalBase.source,
|
|
133
166
|
paths: evalBase.paths,
|
|
134
167
|
input: evalBase.input,
|
|
135
168
|
inputPath: evalBase.inputPath,
|
|
169
|
+
// The parsed path carries the `data` root; the flag takes the package
|
|
170
|
+
// name as written in the module.
|
|
171
|
+
package: pkg.replace(/^data\./, '') || undefined,
|
|
172
|
+
imports,
|
|
136
173
|
}, signal);
|
|
174
|
+
// A binary that could not be run, or a run that was killed, is the tool's
|
|
175
|
+
// failure, not a fact about this condition.
|
|
176
|
+
const spawnFailure = mapSubprocessFailure(result, 'opa');
|
|
177
|
+
if (spawnFailure)
|
|
178
|
+
return { spawnFailure };
|
|
137
179
|
if (result.exitCode === null || result.exitCode !== 0) {
|
|
138
|
-
|
|
139
|
-
|
|
180
|
+
// With --format=json OPA writes its error document to stdout; the first
|
|
181
|
+
// line of that is a brace. Read the messages out of it.
|
|
182
|
+
const errors = tryParseJson(result.stdout)?.errors;
|
|
183
|
+
const detail = errors
|
|
184
|
+
?.map((e) => e.message)
|
|
185
|
+
.filter((m) => typeof m === 'string' && m.length > 0)
|
|
186
|
+
.join('; ') ||
|
|
187
|
+
(result.stderr.trim() || result.stdout.trim()).split('\n')[0] ||
|
|
188
|
+
'unknown error';
|
|
189
|
+
return { result: 'unevaluable', note: `Standalone eval failed: ${detail}` };
|
|
140
190
|
}
|
|
141
|
-
//
|
|
142
|
-
// false
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
// than merely the presence of a row (which would mark every comparison true).
|
|
191
|
+
// For a single-expression query OPA returns a row even when the value is
|
|
192
|
+
// `false`; with several expressions an unsatisfied one yields no row at
|
|
193
|
+
// all. Either way a condition is satisfied only when some row has every
|
|
194
|
+
// expression defined and not `false`, so inspect the values rather than the
|
|
195
|
+
// presence of a row.
|
|
147
196
|
const parsed = tryParseJson(result.stdout);
|
|
148
197
|
const satisfied = (parsed?.result ?? []).some((row) => {
|
|
149
198
|
const exprs = row.expressions ?? [];
|
|
@@ -151,14 +200,25 @@ async function evalConditionStandalone(exprText, evalBase, opa, signal) {
|
|
|
151
200
|
});
|
|
152
201
|
return satisfied ? { result: 'true' } : { result: 'false' };
|
|
153
202
|
}
|
|
154
|
-
|
|
203
|
+
/** Structural equality, enough for the scalars and small literals a default holds. */
|
|
204
|
+
function deepEqual(a, b) {
|
|
205
|
+
if (a === b)
|
|
206
|
+
return true;
|
|
207
|
+
if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object')
|
|
208
|
+
return false;
|
|
209
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
210
|
+
}
|
|
211
|
+
function buildSummary(query, rules, defaultValue, viaDefault) {
|
|
155
212
|
const lines = [];
|
|
213
|
+
const state = viaDefault
|
|
214
|
+
? `falls back to its default value ${JSON.stringify(defaultValue)} because no other definition matched`
|
|
215
|
+
: 'is undefined';
|
|
156
216
|
if (rules.length === 0) {
|
|
157
|
-
lines.push(`${query}
|
|
217
|
+
lines.push(`${query} ${state}: no matching rules found in the analysed source.`);
|
|
158
218
|
}
|
|
159
219
|
else {
|
|
160
220
|
const count = rules.length;
|
|
161
|
-
lines.push(`${query}
|
|
221
|
+
lines.push(`${query} ${state}. ${count} rule definition${count !== 1 ? 's' : ''} analysed:`);
|
|
162
222
|
for (const r of rules) {
|
|
163
223
|
const loc = r.location.row > 0 ? ` (row ${r.location.row})` : '';
|
|
164
224
|
const bc = r.blockingCondition;
|
|
@@ -171,13 +231,15 @@ function buildSummary(query, rules, defaultValue) {
|
|
|
171
231
|
lines.push(` Rule ${r.ruleIndex}${loc}: no analysable body conditions.`);
|
|
172
232
|
}
|
|
173
233
|
else {
|
|
174
|
-
|
|
234
|
+
// Conditions behind the one that stopped evaluation were never attempted
|
|
235
|
+
// and are not what the summary is warning about.
|
|
236
|
+
const unevalCount = r.conditions.filter((c) => c.result === 'unevaluable' && !/^Not (reached|evaluated):/.test(c.note ?? '')).length;
|
|
175
237
|
lines.push(` Rule ${r.ruleIndex}${loc}: blocking condition could not be determined ` +
|
|
176
238
|
`(${unevalCount} unevaluable expression${unevalCount !== 1 ? 's' : ''}).`);
|
|
177
239
|
}
|
|
178
240
|
}
|
|
179
241
|
}
|
|
180
|
-
if (defaultValue !== undefined) {
|
|
242
|
+
if (!viaDefault && defaultValue !== undefined) {
|
|
181
243
|
lines.push(`Default value: ${JSON.stringify(defaultValue)}.`);
|
|
182
244
|
}
|
|
183
245
|
return lines.join('\n');
|
|
@@ -188,16 +250,18 @@ export function registerRegoExplainUndefined(server, config) {
|
|
|
188
250
|
server.registerTool('rego_explain_undefined', {
|
|
189
251
|
title: 'Explain why a Rego query is undefined',
|
|
190
252
|
description: 'Diagnose why a fully-qualified Rego query (e.g. "data.authz.allow") produces no ' +
|
|
191
|
-
'value. Combines a plain eval, a full-trace eval, and
|
|
192
|
-
'identify the exact body expression blocking each rule.
|
|
193
|
-
'(trace-based) and indexer elimination (standalone
|
|
253
|
+
'value, or falls back to its default. Combines a plain eval, a full-trace eval, and ' +
|
|
254
|
+
'per-condition AST analysis to identify the exact body expression blocking each rule. ' +
|
|
255
|
+
'Handles both runtime failures (trace-based) and indexer elimination (standalone ' +
|
|
256
|
+
'condition eval). A rule written with `default allow := false` always has a value, so ' +
|
|
257
|
+
'`queryResult` reports `default` for it and the same per-rule breakdown follows: the ' +
|
|
258
|
+
'question "why is allow false" is the question this answers. Returns a structured ' +
|
|
194
259
|
'breakdown of which conditions blocked each rule plus a human-readable summary.',
|
|
195
260
|
inputSchema: RegoExplainUndefinedInput,
|
|
196
261
|
annotations: {
|
|
197
|
-
readOnlyHint:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
openWorldHint: false,
|
|
262
|
+
readOnlyHint: false,
|
|
263
|
+
// Runs Rego supplied by the caller; a policy can reach the network through http.send.
|
|
264
|
+
openWorldHint: true,
|
|
201
265
|
},
|
|
202
266
|
}, async (args, { signal }) => {
|
|
203
267
|
return withToolEnvelope(config, async () => {
|
|
@@ -209,17 +273,19 @@ export function registerRegoExplainUndefined(server, config) {
|
|
|
209
273
|
return pv.error;
|
|
210
274
|
resolvedPaths = pv.resolved;
|
|
211
275
|
}
|
|
276
|
+
let resolvedInputPath;
|
|
212
277
|
if (args.inputPath) {
|
|
213
278
|
const iv = validatePaths([args.inputPath], config, { mustExist: true });
|
|
214
279
|
if (!iv.ok)
|
|
215
280
|
return iv.error;
|
|
281
|
+
resolvedInputPath = iv.resolved[0];
|
|
216
282
|
}
|
|
217
283
|
const evalBase = {
|
|
218
284
|
query: args.query,
|
|
219
285
|
source: args.source,
|
|
220
286
|
paths: resolvedPaths.length > 0 ? resolvedPaths : undefined,
|
|
221
287
|
input: args.input,
|
|
222
|
-
inputPath:
|
|
288
|
+
inputPath: resolvedInputPath,
|
|
223
289
|
};
|
|
224
290
|
// ── Step 1: plain eval ────────────────────────────────────────────
|
|
225
291
|
const plainResult = await opa.eval(evalBase, signal);
|
|
@@ -237,27 +303,17 @@ export function registerRegoExplainUndefined(server, config) {
|
|
|
237
303
|
}
|
|
238
304
|
const plainJson = tryParseJson(plainResult.stdout);
|
|
239
305
|
const isDefined = (plainJson?.result?.length ?? 0) > 0;
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
summary: `${args.query} is defined with value: ${JSON.stringify(value)}.`,
|
|
246
|
-
rulesFound: 0,
|
|
247
|
-
rules: [],
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
// ── Step 2: eval with --explain=full ──────────────────────────────
|
|
251
|
-
const traceResult = await opa.eval({ ...evalBase, explain: 'full' }, signal);
|
|
252
|
-
const traceSpawnErr = mapSubprocessFailure(traceResult, 'opa');
|
|
253
|
-
if (traceSpawnErr)
|
|
254
|
-
return traceSpawnErr;
|
|
255
|
-
const traceJson = tryParseJson(traceResult.stdout);
|
|
256
|
-
const trace = traceJson?.explanation ?? [];
|
|
257
|
-
// ── Step 3: parse AST(s) ──────────────────────────────────────────
|
|
306
|
+
const definedValue = isDefined
|
|
307
|
+
? plainJson.result[0]
|
|
308
|
+
?.expressions?.[0]?.value
|
|
309
|
+
: undefined;
|
|
310
|
+
// ── Step 2: parse AST(s) ──────────────────────────────────────────
|
|
258
311
|
const asts = [];
|
|
259
312
|
if (args.source) {
|
|
260
313
|
const pr = await opa.parse({ source: args.source, includeLocations: true }, signal);
|
|
314
|
+
const parseFailure = mapSubprocessFailure(pr, 'opa');
|
|
315
|
+
if (parseFailure)
|
|
316
|
+
return parseFailure;
|
|
261
317
|
if (pr.exitCode === 0) {
|
|
262
318
|
const ast = tryParseJson(pr.stdout);
|
|
263
319
|
if (ast)
|
|
@@ -270,6 +326,9 @@ export function registerRegoExplainUndefined(server, config) {
|
|
|
270
326
|
try {
|
|
271
327
|
const src = await readFile(filePath, 'utf8');
|
|
272
328
|
const pr = await opa.parse({ source: src, includeLocations: true }, signal);
|
|
329
|
+
const parseFailure = mapSubprocessFailure(pr, 'opa');
|
|
330
|
+
if (parseFailure)
|
|
331
|
+
return parseFailure;
|
|
273
332
|
if (pr.exitCode === 0) {
|
|
274
333
|
const ast = tryParseJson(pr.stdout);
|
|
275
334
|
if (ast)
|
|
@@ -281,9 +340,12 @@ export function registerRegoExplainUndefined(server, config) {
|
|
|
281
340
|
}
|
|
282
341
|
}
|
|
283
342
|
}
|
|
284
|
-
// ── Step
|
|
343
|
+
// ── Step 3: find matching rules ───────────────────────────────────
|
|
285
344
|
const queryParsed = queryToPackageAndRule(args.query);
|
|
286
345
|
const matchedRules = [];
|
|
346
|
+
/** Package and imports of each matched rule, index-aligned with matchedRules. */
|
|
347
|
+
const matchedPackages = [];
|
|
348
|
+
const matchedImports = [];
|
|
287
349
|
for (const ast of asts) {
|
|
288
350
|
const pkgPath = extractPackagePath(ast);
|
|
289
351
|
if (queryParsed && pkgPath !== queryParsed.packagePath)
|
|
@@ -292,9 +354,35 @@ export function registerRegoExplainUndefined(server, config) {
|
|
|
292
354
|
const name = extractRuleHeadName(rule.head);
|
|
293
355
|
if (!queryParsed || name === queryParsed.ruleName) {
|
|
294
356
|
matchedRules.push(rule);
|
|
357
|
+
matchedPackages.push(pkgPath);
|
|
358
|
+
matchedImports.push(moduleImports(ast));
|
|
295
359
|
}
|
|
296
360
|
}
|
|
297
361
|
}
|
|
362
|
+
// A rule written with `default allow := false` always has a value, so the
|
|
363
|
+
// query is never undefined and the analysis below used to be skipped for
|
|
364
|
+
// the shape most policies are written in. When the value came from the
|
|
365
|
+
// default and nothing else matched, the interesting question is unchanged.
|
|
366
|
+
const defaultRule = matchedRules.find((r) => r.default === true);
|
|
367
|
+
const viaDefault = isDefined &&
|
|
368
|
+
defaultRule !== undefined &&
|
|
369
|
+
deepEqual(defaultRule.head?.value?.value, definedValue);
|
|
370
|
+
if (isDefined && !viaDefault) {
|
|
371
|
+
return ok({
|
|
372
|
+
queryResult: 'defined',
|
|
373
|
+
value: definedValue,
|
|
374
|
+
summary: `${args.query} is defined with value: ${JSON.stringify(definedValue)}.`,
|
|
375
|
+
rulesFound: 0,
|
|
376
|
+
rules: [],
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
// ── Step 4: eval with --explain=full ──────────────────────────────
|
|
380
|
+
const traceResult = await opa.eval({ ...evalBase, explain: 'full' }, signal);
|
|
381
|
+
const traceSpawnErr = mapSubprocessFailure(traceResult, 'opa');
|
|
382
|
+
if (traceSpawnErr)
|
|
383
|
+
return traceSpawnErr;
|
|
384
|
+
const traceJson = tryParseJson(traceResult.stdout);
|
|
385
|
+
const trace = traceJson?.explanation ?? [];
|
|
298
386
|
// ── Step 5: analyse each rule ─────────────────────────────────────
|
|
299
387
|
let defaultValue;
|
|
300
388
|
const rules = [];
|
|
@@ -353,18 +441,70 @@ export function registerRegoExplainUndefined(server, config) {
|
|
|
353
441
|
}
|
|
354
442
|
else {
|
|
355
443
|
// Standalone eval: OPA's indexer eliminated this rule before
|
|
356
|
-
// entering its body.
|
|
444
|
+
// entering its body. Each condition is judged inside the rule's
|
|
445
|
+
// package, with its imports, and with the conditions before it in
|
|
446
|
+
// scope, so a local assigned earlier and a bare reference to a
|
|
447
|
+
// sibling rule both resolve. Evaluation stops at the first
|
|
448
|
+
// condition that does not hold, as on the traced path.
|
|
449
|
+
// Evaluating each condition alone left the dependent ones
|
|
450
|
+
// unevaluable and named the first non-true one as the blocker,
|
|
451
|
+
// often a satisfied guard sitting ahead of the real one.
|
|
452
|
+
const pkg = matchedPackages[ruleIndex] ?? '';
|
|
453
|
+
const imports = matchedImports[ruleIndex] ?? [];
|
|
454
|
+
const held = [];
|
|
455
|
+
let stopped;
|
|
357
456
|
for (const cond of conditions) {
|
|
358
457
|
if (cond.text === '<expression>') {
|
|
359
458
|
cond.note = 'No location text available; cannot evaluate standalone.';
|
|
360
|
-
|
|
459
|
+
stopped = 'Not evaluated: an earlier condition has no location text.';
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
// Keep the query under the command-line limit by dropping the
|
|
463
|
+
// oldest held terms. A shorter prefix only widens the solutions,
|
|
464
|
+
// so a false stays a real blocker; a dropped assignment makes a
|
|
465
|
+
// later term unsafe, which comes back unevaluable, not guessed.
|
|
466
|
+
let prefix = [...held, cond.text];
|
|
467
|
+
while (prefix.length > 1 && prefix.join('; ').length > MAX_PREFIX_CHARS) {
|
|
468
|
+
prefix = prefix.slice(1);
|
|
361
469
|
}
|
|
362
|
-
|
|
470
|
+
let evalOut;
|
|
471
|
+
try {
|
|
472
|
+
evalOut = await evalPrefixStandalone(prefix.join('; '), pkg, imports, evalBase, opa, signal);
|
|
473
|
+
}
|
|
474
|
+
catch (e) {
|
|
475
|
+
if (signal?.aborted)
|
|
476
|
+
throw e;
|
|
477
|
+
evalOut = {
|
|
478
|
+
result: 'unevaluable',
|
|
479
|
+
note: `Standalone eval failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
if ('spawnFailure' in evalOut)
|
|
483
|
+
return evalOut.spawnFailure;
|
|
363
484
|
cond.result = evalOut.result;
|
|
364
485
|
if (evalOut.note)
|
|
365
486
|
cond.note = evalOut.note;
|
|
487
|
+
if (evalOut.result === 'true') {
|
|
488
|
+
held.push(cond.text);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
stopped =
|
|
492
|
+
evalOut.result === 'false'
|
|
493
|
+
? 'Not reached: an earlier condition does not hold.'
|
|
494
|
+
: 'Not evaluated: an earlier condition could not be evaluated.';
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
if (stopped !== undefined) {
|
|
498
|
+
for (const cond of conditions) {
|
|
499
|
+
if (cond.result === 'unevaluable' && cond.note === undefined)
|
|
500
|
+
cond.note = stopped;
|
|
501
|
+
}
|
|
366
502
|
}
|
|
367
|
-
|
|
503
|
+
// The blocker is the first condition that is not true, and only
|
|
504
|
+
// when it is a definite false; past one that could not be
|
|
505
|
+
// evaluated, naming a later condition would be a guess.
|
|
506
|
+
const firstNotTrue = conditions.find((c) => c.result !== 'true');
|
|
507
|
+
const blockingCondition = firstNotTrue?.result === 'false' ? firstNotTrue : null;
|
|
368
508
|
rules.push({
|
|
369
509
|
ruleIndex,
|
|
370
510
|
isDefault: false,
|
|
@@ -376,9 +516,10 @@ export function registerRegoExplainUndefined(server, config) {
|
|
|
376
516
|
}
|
|
377
517
|
}
|
|
378
518
|
const nonDefaultRules = rules.filter((r) => !r.isDefault);
|
|
379
|
-
const summary = buildSummary(args.query, nonDefaultRules, defaultValue);
|
|
519
|
+
const summary = buildSummary(args.query, nonDefaultRules, defaultValue, viaDefault);
|
|
380
520
|
return ok({
|
|
381
|
-
queryResult: 'undefined',
|
|
521
|
+
queryResult: viaDefault ? 'default' : 'undefined',
|
|
522
|
+
...(viaDefault ? { value: definedValue } : {}),
|
|
382
523
|
summary,
|
|
383
524
|
rulesFound: nonDefaultRules.length,
|
|
384
525
|
defaultValue,
|