@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
|
@@ -3,7 +3,7 @@ import { inferTypes } from './rego-type-inferencer.js';
|
|
|
3
3
|
import { createInputVars, encodeRule } from './rego-smt-encoder.js';
|
|
4
4
|
import { extractCounterexample, formatCounterexample, } from './rego-counterexample.js';
|
|
5
5
|
import { describeProperty } from './rego-property-parser.js';
|
|
6
|
-
import { getZ3 } from './rego-z3.js';
|
|
6
|
+
import { getZ3, withZ3Lock } from './rego-z3.js';
|
|
7
7
|
// Monotonically increasing counter used to generate a unique prefix for all
|
|
8
8
|
// Z3 constant names within each runVerify call. This prevents sort conflicts
|
|
9
9
|
// when the same input path is inferred with different sorts across calls
|
|
@@ -59,98 +59,130 @@ export async function runVerify(ast, property, signal) {
|
|
|
59
59
|
warnings.push(conflict.reason);
|
|
60
60
|
}
|
|
61
61
|
signal?.throwIfAborted();
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
62
|
+
// Everything below touches the shared Z3 Context, which is one
|
|
63
|
+
// single-threaded WASM heap. Concurrent MCP tool calls must not overlap
|
|
64
|
+
// here: two solves interleaving on that heap degraded verdicts to
|
|
65
|
+
// inconclusive and crashed the WASM allocator. The lock also makes the
|
|
66
|
+
// callId counter race-free.
|
|
67
|
+
return await withZ3Lock(async () => {
|
|
68
|
+
const Z3 = await getZ3();
|
|
69
|
+
signal?.throwIfAborted();
|
|
70
|
+
const callId = `v${_verifyCallCounter++}`;
|
|
71
|
+
const { vars: inputVars, presence: presenceVars } = createInputVars(Z3, walked.inputPaths, typeResult.sorts, callId);
|
|
72
|
+
const ctx = { Z3, inputVars, presenceVars, sorts: typeResult.sorts, callId };
|
|
73
|
+
// The rule's default participates in "is it true", so it must reach the
|
|
74
|
+
// encoder. Consulting it only when there were no clauses at all is what made
|
|
75
|
+
// `default allow := true` plus a guard clause verify as never-true.
|
|
76
|
+
const encoded = encodeRule(targetClauses, ctx, walked.defaults.get(property.ruleName));
|
|
77
|
+
warnings.push(...encoded.warnings);
|
|
78
|
+
if (encoded.unencodable) {
|
|
79
|
+
return inconclusive(property, `Rule "${property.ruleName}" contains a value or operand the encoder cannot represent (for example a null literal). Verification is inconclusive rather than guessed.`, unsupportedInRule, warnings);
|
|
80
|
+
}
|
|
81
|
+
// Z3's high-level Solver and Model objects use FinalizationRegistry internally
|
|
82
|
+
// (solver_dec_ref / model_dec_ref) so they are cleaned up when GC'd.
|
|
83
|
+
// We avoid holding a reference to the Model beyond extractCounterexample so
|
|
84
|
+
// it becomes eligible for GC as soon as the call returns, reducing WASM heap
|
|
85
|
+
// pressure under high call volume.
|
|
86
|
+
const solver = new Z3.Solver();
|
|
87
|
+
// Set timeout to prevent hanging on complex policies
|
|
88
|
+
solver.set('timeout', SOLVER_TIMEOUT_MS);
|
|
89
|
+
switch (property.kind) {
|
|
90
|
+
case 'always_true':
|
|
91
|
+
// Prove rule is always true: check if NOT(rule) is satisfiable.
|
|
92
|
+
// SAT → counterexample (input where rule is false)
|
|
93
|
+
// UNSAT → proven always true
|
|
94
|
+
solver.add(Z3.Not(encoded.formula));
|
|
95
|
+
break;
|
|
96
|
+
case 'never_true':
|
|
97
|
+
// Prove rule is never true: check if rule IS satisfiable.
|
|
98
|
+
// SAT → counterexample (input where rule fires, violating "never")
|
|
99
|
+
// UNSAT → proven never true
|
|
100
|
+
solver.add(encoded.formula);
|
|
101
|
+
break;
|
|
102
|
+
case 'satisfiable':
|
|
103
|
+
// Check if any input satisfies the rule.
|
|
104
|
+
// SAT → witness found (not a bug, just a satisfying input)
|
|
105
|
+
// UNSAT → rule is vacuously false / dead code
|
|
106
|
+
solver.add(encoded.formula);
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
signal?.throwIfAborted();
|
|
110
|
+
let solverResult = await solver.check();
|
|
111
|
+
// Prefer a witness in which every field the rule reads is PRESENT.
|
|
112
|
+
//
|
|
113
|
+
// With absence modelled, Z3 will happily satisfy NOT(rule) by omitting a
|
|
114
|
+
// field, which is sound but unhelpful: "it fails when the input is empty"
|
|
115
|
+
// is rarely the case a reviewer is chasing. Re-ask with presence forced and
|
|
116
|
+
// keep that model when one exists, otherwise fall back to the original.
|
|
117
|
+
// Soundness is unaffected either way, since both are real models.
|
|
118
|
+
if (solverResult === 'sat' && presenceVars.size > 0) {
|
|
119
|
+
solver.push();
|
|
120
|
+
for (const present of presenceVars.values())
|
|
121
|
+
solver.add(present);
|
|
122
|
+
const withPresence = await solver.check();
|
|
123
|
+
if (withPresence === 'sat') {
|
|
124
|
+
solverResult = withPresence;
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
solver.pop();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (solverResult === 'unknown') {
|
|
131
|
+
return inconclusive(property, `Z3 solver returned "unknown" (timeout or resource limit reached after ${SOLVER_TIMEOUT_MS}ms). The policy may be too complex for automated verification.`, unsupportedInRule, warnings);
|
|
132
|
+
}
|
|
133
|
+
if (solverResult === 'unsat') {
|
|
134
|
+
if (property.kind === 'satisfiable') {
|
|
135
|
+
// No satisfying input exists -- rule is dead code or has contradictory conditions.
|
|
136
|
+
return {
|
|
137
|
+
verdict: 'unsatisfiable',
|
|
138
|
+
property: describeProperty(property),
|
|
139
|
+
rule: property.ruleName,
|
|
140
|
+
unsupportedConstructs: unsupportedInRule,
|
|
141
|
+
warnings,
|
|
142
|
+
message: `UNSATISFIABLE: No input can make "${property.ruleName}" true. The rule may be dead code or have contradictory conditions.`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// For always_true / never_true: UNSAT on the negation = property proven
|
|
146
|
+
return {
|
|
147
|
+
verdict: 'proven',
|
|
148
|
+
property: describeProperty(property),
|
|
149
|
+
rule: property.ruleName,
|
|
150
|
+
unsupportedConstructs: unsupportedInRule,
|
|
151
|
+
warnings,
|
|
152
|
+
message: `PROVEN: ${describeProperty(property)}.`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// SAT: pass solver.model() inline so the Model object is not kept alive
|
|
156
|
+
// beyond extractCounterexample -- it becomes GC-eligible immediately after.
|
|
157
|
+
const ce = extractCounterexample(solver.model(), inputVars, typeResult.sorts, presenceVars);
|
|
158
|
+
const ceFormatted = formatCounterexample(ce);
|
|
103
159
|
if (property.kind === 'satisfiable') {
|
|
104
|
-
// No satisfying input exists -- rule is dead code or has contradictory conditions.
|
|
105
160
|
return {
|
|
106
|
-
verdict: '
|
|
161
|
+
verdict: 'proven',
|
|
107
162
|
property: describeProperty(property),
|
|
108
163
|
rule: property.ruleName,
|
|
164
|
+
counterexample: ce,
|
|
165
|
+
counterexampleFormatted: ceFormatted,
|
|
109
166
|
unsupportedConstructs: unsupportedInRule,
|
|
110
167
|
warnings,
|
|
111
|
-
message: `
|
|
168
|
+
message: `SATISFIABLE: Found an input that makes "${property.ruleName}" true.\n\nWitness input:\n${ceFormatted}`,
|
|
112
169
|
};
|
|
113
170
|
}
|
|
114
|
-
//
|
|
171
|
+
// always_true / never_true: SAT means we found a violation
|
|
172
|
+
const ceLabel = property.kind === 'always_true'
|
|
173
|
+
? 'input where rule is FALSE'
|
|
174
|
+
: 'input where rule is TRUE (violates "never")';
|
|
115
175
|
return {
|
|
116
|
-
verdict: '
|
|
117
|
-
property: describeProperty(property),
|
|
118
|
-
rule: property.ruleName,
|
|
119
|
-
unsupportedConstructs: unsupportedInRule,
|
|
120
|
-
warnings,
|
|
121
|
-
message: `PROVEN: ${describeProperty(property)}.`,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
// SAT: pass solver.model() inline so the Model object is not kept alive
|
|
125
|
-
// beyond extractCounterexample -- it becomes GC-eligible immediately after.
|
|
126
|
-
const ce = extractCounterexample(solver.model(), inputVars, typeResult.sorts);
|
|
127
|
-
const ceFormatted = formatCounterexample(ce);
|
|
128
|
-
if (property.kind === 'satisfiable') {
|
|
129
|
-
return {
|
|
130
|
-
verdict: 'proven',
|
|
176
|
+
verdict: 'counterexample',
|
|
131
177
|
property: describeProperty(property),
|
|
132
178
|
rule: property.ruleName,
|
|
133
179
|
counterexample: ce,
|
|
134
180
|
counterexampleFormatted: ceFormatted,
|
|
135
181
|
unsupportedConstructs: unsupportedInRule,
|
|
136
182
|
warnings,
|
|
137
|
-
message: `
|
|
183
|
+
message: `COUNTEREXAMPLE: Property does NOT hold. Found ${ceLabel}:\n\n${ceFormatted}`,
|
|
138
184
|
};
|
|
139
|
-
}
|
|
140
|
-
// always_true / never_true: SAT means we found a violation
|
|
141
|
-
const ceLabel = property.kind === 'always_true'
|
|
142
|
-
? 'input where rule is FALSE'
|
|
143
|
-
: 'input where rule is TRUE (violates "never")';
|
|
144
|
-
return {
|
|
145
|
-
verdict: 'counterexample',
|
|
146
|
-
property: describeProperty(property),
|
|
147
|
-
rule: property.ruleName,
|
|
148
|
-
counterexample: ce,
|
|
149
|
-
counterexampleFormatted: ceFormatted,
|
|
150
|
-
unsupportedConstructs: unsupportedInRule,
|
|
151
|
-
warnings,
|
|
152
|
-
message: `COUNTEREXAMPLE: Property does NOT hold. Found ${ceLabel}:\n\n${ceFormatted}`,
|
|
153
|
-
};
|
|
185
|
+
});
|
|
154
186
|
}
|
|
155
187
|
catch (e) {
|
|
156
188
|
// A cancellation must propagate as a cancellation, not be masked as a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rego-verify-engine.js","sourceRoot":"","sources":["../../src/lib/rego-verify-engine.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACpE,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GAErB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,gBAAgB,EAAuB,MAAM,2BAA2B,CAAC;AAClF,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"rego-verify-engine.js","sourceRoot":"","sources":["../../src/lib/rego-verify-engine.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/rego-z3.d.ts
CHANGED
|
@@ -5,6 +5,16 @@ export type Z3Context = ReturnType<Awaited<ReturnType<typeof Z3Init>>['Context']
|
|
|
5
5
|
* Safe to call from concurrent async paths.
|
|
6
6
|
*/
|
|
7
7
|
export declare function getZ3(): Promise<Z3Context>;
|
|
8
|
+
/**
|
|
9
|
+
* Run `fn` with exclusive access to the shared Z3 Context.
|
|
10
|
+
*
|
|
11
|
+
* Serializes rather than parallelizes: an MCP client is free to issue several
|
|
12
|
+
* rego_verify calls at once, and letting them into the WASM heap together
|
|
13
|
+
* crashes it. Solve times are in the tens of milliseconds, so queueing is
|
|
14
|
+
* cheaper than the alternative of one Context per call, which would re-pay the
|
|
15
|
+
* WASM init cost every time.
|
|
16
|
+
*/
|
|
17
|
+
export declare function withZ3Lock<T>(fn: () => Promise<T>): Promise<T>;
|
|
8
18
|
/**
|
|
9
19
|
* Reset the singleton - intended for test teardown only.
|
|
10
20
|
* Do NOT call in production paths; re-init is expensive.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rego-z3.d.ts","sourceRoot":"","sources":["../../src/lib/rego-z3.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rego-z3.d.ts","sourceRoot":"","sources":["../../src/lib/rego-z3.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,IAAI,IAAI,MAAM,EAAE,MAAM,WAAW,CAAC;AAKhD,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;AAKlF;;;GAGG;AACH,wBAAsB,KAAK,IAAI,OAAO,CAAC,SAAS,CAAC,CAKhD;AASD;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAapE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC"}
|
package/dist/lib/rego-z3.js
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
* initialized once per process. Concurrent calls safely await the
|
|
6
6
|
* same promise. Each verification call creates its own fresh Solver
|
|
7
7
|
* from the shared Context, so contexts never accumulate stale state.
|
|
8
|
+
*
|
|
9
|
+
* A fresh Solver is NOT enough to make concurrent verification safe. The whole
|
|
10
|
+
* Context lives in one single-threaded WASM heap, and `check()` is async, so two
|
|
11
|
+
* overlapping solves interleave on that heap and corrupt each other: observed
|
|
12
|
+
* symptoms were verdicts degrading to `inconclusive` and an allocator crash
|
|
13
|
+
* inside the WASM module. Everything touching the Context must therefore run
|
|
14
|
+
* under `withZ3Lock`.
|
|
8
15
|
*/
|
|
9
16
|
import { init } from 'z3-solver';
|
|
10
17
|
// Module-level singleton
|
|
@@ -19,11 +26,42 @@ export async function getZ3() {
|
|
|
19
26
|
}
|
|
20
27
|
return z3InitPromise;
|
|
21
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Tail of the queue of pending Z3 critical sections. Each acquirer waits on the
|
|
31
|
+
* current tail and installs its own completion as the new tail, so sections run
|
|
32
|
+
* in call order with no overlap.
|
|
33
|
+
*/
|
|
34
|
+
let z3LockTail = Promise.resolve();
|
|
35
|
+
/**
|
|
36
|
+
* Run `fn` with exclusive access to the shared Z3 Context.
|
|
37
|
+
*
|
|
38
|
+
* Serializes rather than parallelizes: an MCP client is free to issue several
|
|
39
|
+
* rego_verify calls at once, and letting them into the WASM heap together
|
|
40
|
+
* crashes it. Solve times are in the tens of milliseconds, so queueing is
|
|
41
|
+
* cheaper than the alternative of one Context per call, which would re-pay the
|
|
42
|
+
* WASM init cost every time.
|
|
43
|
+
*/
|
|
44
|
+
export async function withZ3Lock(fn) {
|
|
45
|
+
let release;
|
|
46
|
+
const mine = new Promise((resolve) => {
|
|
47
|
+
release = resolve;
|
|
48
|
+
});
|
|
49
|
+
const predecessor = z3LockTail;
|
|
50
|
+
z3LockTail = predecessor.then(() => mine);
|
|
51
|
+
await predecessor;
|
|
52
|
+
try {
|
|
53
|
+
return await fn();
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
release();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
22
59
|
/**
|
|
23
60
|
* Reset the singleton - intended for test teardown only.
|
|
24
61
|
* Do NOT call in production paths; re-init is expensive.
|
|
25
62
|
*/
|
|
26
63
|
export function resetZ3ForTesting() {
|
|
27
64
|
z3InitPromise = null;
|
|
65
|
+
z3LockTail = Promise.resolve();
|
|
28
66
|
}
|
|
29
67
|
//# sourceMappingURL=rego-z3.js.map
|
package/dist/lib/rego-z3.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rego-z3.js","sourceRoot":"","sources":["../../src/lib/rego-z3.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"rego-z3.js","sourceRoot":"","sources":["../../src/lib/rego-z3.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAQjC,yBAAyB;AACzB,IAAI,aAAa,GAA4B,IAAI,CAAC;AAElD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK;IACzB,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,aAAa,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,aAAmC,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,IAAI,UAAU,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;AAElD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAI,EAAoB;IACtD,IAAI,OAAoB,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;QACzC,OAAO,GAAG,OAAO,CAAC;IACpB,CAAC,CAAC,CAAC;IACH,MAAM,WAAW,GAAG,UAAU,CAAC;IAC/B,UAAU,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,WAAW,CAAC;IAClB,IAAI,CAAC;QACH,OAAO,MAAM,EAAE,EAAE,CAAC;IACpB,CAAC;YAAS,CAAC;QACT,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAC/B,aAAa,GAAG,IAAI,CAAC;IACrB,UAAU,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;AACjC,CAAC"}
|
package/dist/lib/security.d.ts
CHANGED
|
@@ -7,6 +7,14 @@ export interface PathValidationResult {
|
|
|
7
7
|
/**
|
|
8
8
|
* Resolve `inputPath` and confirm it is contained within at least one
|
|
9
9
|
* `allowedRoots` entry. Returns the resolved absolute path on success.
|
|
10
|
+
*
|
|
11
|
+
* Two checks, both of which must pass. The first is syntactic: `..` segments
|
|
12
|
+
* are collapsed without touching the disk and the result must sit under a
|
|
13
|
+
* root as written. The second follows the filesystem: the path's real
|
|
14
|
+
* location, including the part of it that does not exist yet, must sit under
|
|
15
|
+
* a root's real location. A link is still followed at read or write time by
|
|
16
|
+
* the tool that receives the path, so the check is against the layout at the
|
|
17
|
+
* moment of validation.
|
|
10
18
|
*/
|
|
11
19
|
export declare function validatePath(inputPath: string, allowedRoots: string[], options?: {
|
|
12
20
|
mustExist?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/lib/security.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;CAC7B;
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/lib/security.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC;CAC7B;AA6ED;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EAAE,EACtB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAA;CAAO,GACpC,oBAAoB,CAsGtB;AAED,+EAA+E;AAC/E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAMjD"}
|
package/dist/lib/security.js
CHANGED
|
@@ -5,12 +5,90 @@
|
|
|
5
5
|
* `validatePath` to prevent reading or writing outside the
|
|
6
6
|
* agreed-upon roots.
|
|
7
7
|
*/
|
|
8
|
-
import {
|
|
9
|
-
import { isAbsolute, resolve, sep } from 'node:path';
|
|
8
|
+
import { lstatSync, realpathSync, statSync } from 'node:fs';
|
|
9
|
+
import { basename, dirname, isAbsolute, join, resolve, sep } from 'node:path';
|
|
10
10
|
import { err } from './errors.js';
|
|
11
|
+
const WINDOWS = process.platform === 'win32';
|
|
12
|
+
/**
|
|
13
|
+
* Canonical form of an existing path: every link resolved, and on Windows
|
|
14
|
+
* 8.3 short names expanded and on-disk casing restored. The JavaScript
|
|
15
|
+
* `realpathSync` resolves links but leaves short names and casing alone, so
|
|
16
|
+
* two spellings of one directory could compare unequal.
|
|
17
|
+
*/
|
|
18
|
+
function canonical(p) {
|
|
19
|
+
return realpathSync.native(p);
|
|
20
|
+
}
|
|
21
|
+
/** Does `p` fall under at least one of `roots`? */
|
|
22
|
+
function isUnderRoots(p, roots, caseInsensitive) {
|
|
23
|
+
const fold = (s) => (caseInsensitive ? s.toLowerCase() : s);
|
|
24
|
+
const candidate = fold(p);
|
|
25
|
+
return roots.some((root) => {
|
|
26
|
+
const r = fold(root);
|
|
27
|
+
const rootWithSep = r.endsWith(sep) ? r : r + sep;
|
|
28
|
+
return candidate === r || candidate.startsWith(rootWithSep);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Where a path really leads, whether or not it exists yet.
|
|
33
|
+
*
|
|
34
|
+
* Walks up from `p` to the nearest entry that exists, resolves that entry's
|
|
35
|
+
* real location, and re-attaches the segments below it. A write to `p` lands
|
|
36
|
+
* under that real location, so that is what has to be checked against the
|
|
37
|
+
* roots. Checking only paths that already exist left every not-yet-created
|
|
38
|
+
* write target unchecked, and a junction or symlink inside a root could point
|
|
39
|
+
* it anywhere.
|
|
40
|
+
*
|
|
41
|
+
* An entry that exists only as a link to something that does not exist (a
|
|
42
|
+
* dangling link) is reported on its own: a write through it would create the
|
|
43
|
+
* target wherever the link points.
|
|
44
|
+
*/
|
|
45
|
+
function realLocation(p) {
|
|
46
|
+
const below = [];
|
|
47
|
+
let cur = p;
|
|
48
|
+
for (;;) {
|
|
49
|
+
let entry;
|
|
50
|
+
try {
|
|
51
|
+
entry = lstatSync(cur);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
const code = e.code;
|
|
55
|
+
if (code === 'ENOENT' || code === 'ENOTDIR') {
|
|
56
|
+
const parent = dirname(cur);
|
|
57
|
+
if (parent === cur)
|
|
58
|
+
return { kind: 'unresolvable', reason: 'no existing ancestor' };
|
|
59
|
+
below.unshift(basename(cur));
|
|
60
|
+
cur = parent;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
return { kind: 'unresolvable', reason: code ?? String(e) };
|
|
64
|
+
}
|
|
65
|
+
let real;
|
|
66
|
+
try {
|
|
67
|
+
real = canonical(cur);
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
if (entry.isSymbolicLink())
|
|
71
|
+
return { kind: 'dangling', link: cur };
|
|
72
|
+
return { kind: 'unresolvable', reason: e.code ?? String(e) };
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
kind: 'real',
|
|
76
|
+
path: below.length === 0 ? real : join(real, ...below),
|
|
77
|
+
exists: below.length === 0,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
11
81
|
/**
|
|
12
82
|
* Resolve `inputPath` and confirm it is contained within at least one
|
|
13
83
|
* `allowedRoots` entry. Returns the resolved absolute path on success.
|
|
84
|
+
*
|
|
85
|
+
* Two checks, both of which must pass. The first is syntactic: `..` segments
|
|
86
|
+
* are collapsed without touching the disk and the result must sit under a
|
|
87
|
+
* root as written. The second follows the filesystem: the path's real
|
|
88
|
+
* location, including the part of it that does not exist yet, must sit under
|
|
89
|
+
* a root's real location. A link is still followed at read or write time by
|
|
90
|
+
* the tool that receives the path, so the check is against the layout at the
|
|
91
|
+
* moment of validation.
|
|
14
92
|
*/
|
|
15
93
|
export function validatePath(inputPath, allowedRoots, options = {}) {
|
|
16
94
|
if (typeof inputPath !== 'string' || inputPath.length === 0) {
|
|
@@ -28,24 +106,42 @@ export function validatePath(inputPath, allowedRoots, options = {}) {
|
|
|
28
106
|
};
|
|
29
107
|
}
|
|
30
108
|
const resolved = isAbsolute(inputPath) ? resolve(inputPath) : resolve(process.cwd(), inputPath);
|
|
31
|
-
// Helper: confirm p falls under at least one of the provided root strings.
|
|
32
|
-
const isUnderRoots = (p, roots) => roots.some((root) => {
|
|
33
|
-
const rootWithSep = root.endsWith(sep) ? root : root + sep;
|
|
34
|
-
return p === root || p.startsWith(rootWithSep);
|
|
35
|
-
});
|
|
36
|
-
// Phase 1: syntactic traversal check. path.resolve() collapses `..`
|
|
37
|
-
// segments without I/O, so this catches plain traversal attacks cheaply.
|
|
38
109
|
const resolvedRoots = allowedRoots.map((r) => resolve(r));
|
|
39
|
-
|
|
110
|
+
const outsideHint = 'Set OPA_MCP_ALLOWED_PATHS to a comma-separated list of directories, or use a path under the current allowed roots.';
|
|
111
|
+
// Phase 1: syntactic containment, cheap and free of I/O. It is not the
|
|
112
|
+
// decision on its own: a root spelled through a link (macOS /var, a Windows
|
|
113
|
+
// short name, a junction) and a path spelled through the target land in the
|
|
114
|
+
// same real place and must be accepted. It settles which message an
|
|
115
|
+
// escaping path gets. Windows compares without regard to case.
|
|
116
|
+
const syntacticallyInside = isUnderRoots(resolved, resolvedRoots, WINDOWS);
|
|
117
|
+
const outsideRoots = () => err('PATH_NOT_ALLOWED', `Path is outside allowed roots: ${inputPath}`, {
|
|
118
|
+
hint: outsideHint,
|
|
119
|
+
details: { resolved, allowedRoots },
|
|
120
|
+
});
|
|
121
|
+
// Phase 2: real location. This is the decision.
|
|
122
|
+
const location = realLocation(resolved);
|
|
123
|
+
if (location.kind === 'dangling') {
|
|
124
|
+
return {
|
|
125
|
+
ok: false,
|
|
126
|
+
error: err('PATH_NOT_ALLOWED', `Path goes through a link whose target does not exist: ${inputPath}`, {
|
|
127
|
+
hint: 'A write through a dangling link would create its target wherever the link points. Remove the link or use a path that does not go through it.',
|
|
128
|
+
details: { resolved, link: location.link },
|
|
129
|
+
}),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
if (location.kind === 'unresolvable') {
|
|
133
|
+
if (!syntacticallyInside)
|
|
134
|
+
return { ok: false, error: outsideRoots() };
|
|
40
135
|
return {
|
|
41
136
|
ok: false,
|
|
42
|
-
error: err('
|
|
43
|
-
|
|
44
|
-
details: { resolved, allowedRoots },
|
|
137
|
+
error: err('PATH_NOT_FOUND', `Path could not be fully resolved: ${inputPath}`, {
|
|
138
|
+
details: { resolved, reason: location.reason },
|
|
45
139
|
}),
|
|
46
140
|
};
|
|
47
141
|
}
|
|
48
|
-
if (options.mustExist && !
|
|
142
|
+
if (options.mustExist && !location.exists) {
|
|
143
|
+
if (!syntacticallyInside)
|
|
144
|
+
return { ok: false, error: outsideRoots() };
|
|
49
145
|
return {
|
|
50
146
|
ok: false,
|
|
51
147
|
error: err('PATH_NOT_FOUND', `Path does not exist: ${inputPath}`, {
|
|
@@ -53,51 +149,31 @@ export function validatePath(inputPath, allowedRoots, options = {}) {
|
|
|
53
149
|
}),
|
|
54
150
|
};
|
|
55
151
|
}
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
//
|
|
61
|
-
// We validate via realpath but return `resolved` (the syntactic path)
|
|
62
|
-
// for backward compatibility across OSes where system directories are
|
|
63
|
-
// themselves symlinks (e.g. macOS /var -> /private/var). Callers pass
|
|
64
|
-
// the returned path to OPA/regal; the OS follows any remaining symlinks
|
|
65
|
-
// at read time, which is safe because we have verified the real target.
|
|
66
|
-
if (existsSync(resolved)) {
|
|
67
|
-
let realPath;
|
|
152
|
+
// A root that is itself a link, or spelled with a short name, compares
|
|
153
|
+
// correctly only in canonical form. A root that does not exist stays as
|
|
154
|
+
// written; nothing under it can resolve to it anyway.
|
|
155
|
+
const realRoots = resolvedRoots.map((r) => {
|
|
68
156
|
try {
|
|
69
|
-
|
|
157
|
+
return canonical(r);
|
|
70
158
|
}
|
|
71
159
|
catch {
|
|
72
|
-
|
|
73
|
-
// insufficient permissions to read the symlink chain. Fail closed.
|
|
74
|
-
return {
|
|
75
|
-
ok: false,
|
|
76
|
-
error: err('PATH_NOT_FOUND', `Path could not be fully resolved: ${inputPath}`, {
|
|
77
|
-
details: { resolved },
|
|
78
|
-
}),
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
// Also realpath the roots themselves so that a root that is a symlink
|
|
82
|
-
// compares correctly against the realpath of the input.
|
|
83
|
-
const realRoots = resolvedRoots.map((r) => {
|
|
84
|
-
try {
|
|
85
|
-
return existsSync(r) ? realpathSync(r) : r;
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
return r;
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
if (!isUnderRoots(realPath, realRoots)) {
|
|
92
|
-
return {
|
|
93
|
-
ok: false,
|
|
94
|
-
error: err('PATH_NOT_ALLOWED', `Path resolves outside allowed roots via symlink: ${inputPath}`, {
|
|
95
|
-
hint: 'Set OPA_MCP_ALLOWED_PATHS to a comma-separated list of directories, or use a path under the current allowed roots.',
|
|
96
|
-
details: { resolved, realPath, allowedRoots },
|
|
97
|
-
}),
|
|
98
|
-
};
|
|
160
|
+
return r;
|
|
99
161
|
}
|
|
162
|
+
});
|
|
163
|
+
if (!isUnderRoots(location.path, realRoots, false)) {
|
|
164
|
+
if (!syntacticallyInside)
|
|
165
|
+
return { ok: false, error: outsideRoots() };
|
|
166
|
+
return {
|
|
167
|
+
ok: false,
|
|
168
|
+
error: err('PATH_NOT_ALLOWED', `Path resolves outside allowed roots via a link: ${inputPath}`, {
|
|
169
|
+
hint: outsideHint,
|
|
170
|
+
details: { resolved, realPath: location.path, allowedRoots },
|
|
171
|
+
}),
|
|
172
|
+
};
|
|
100
173
|
}
|
|
174
|
+
// The syntactic path is returned, not the canonical one: on macOS the
|
|
175
|
+
// system temp directory is itself a link, and tools compare and report
|
|
176
|
+
// paths as the caller spelled them.
|
|
101
177
|
return { ok: true, resolved };
|
|
102
178
|
}
|
|
103
179
|
/** Convenience: returns true if the path is a directory (after validation). */
|
package/dist/lib/security.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/lib/security.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/lib/security.ts"],"names":[],"mappings":"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"}
|