@det-acp/core 0.2.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/LICENSE +21 -0
- package/README.md +492 -0
- package/dist/cli/index.d.ts +15 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +308 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +32 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +234 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/templates.d.ts +27 -0
- package/dist/cli/templates.d.ts.map +1 -0
- package/dist/cli/templates.js +266 -0
- package/dist/cli/templates.js.map +1 -0
- package/dist/engine/action-registry.d.ts +49 -0
- package/dist/engine/action-registry.d.ts.map +1 -0
- package/dist/engine/action-registry.js +95 -0
- package/dist/engine/action-registry.js.map +1 -0
- package/dist/engine/gate.d.ts +57 -0
- package/dist/engine/gate.d.ts.map +1 -0
- package/dist/engine/gate.js +145 -0
- package/dist/engine/gate.js.map +1 -0
- package/dist/engine/runtime.d.ts +98 -0
- package/dist/engine/runtime.d.ts.map +1 -0
- package/dist/engine/runtime.js +138 -0
- package/dist/engine/runtime.js.map +1 -0
- package/dist/engine/session.d.ts +74 -0
- package/dist/engine/session.d.ts.map +1 -0
- package/dist/engine/session.js +343 -0
- package/dist/engine/session.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger/ledger.d.ts +58 -0
- package/dist/ledger/ledger.d.ts.map +1 -0
- package/dist/ledger/ledger.js +188 -0
- package/dist/ledger/ledger.js.map +1 -0
- package/dist/ledger/query.d.ts +29 -0
- package/dist/ledger/query.d.ts.map +1 -0
- package/dist/ledger/query.js +61 -0
- package/dist/ledger/query.js.map +1 -0
- package/dist/ledger/types.d.ts +27 -0
- package/dist/ledger/types.d.ts.map +1 -0
- package/dist/ledger/types.js +5 -0
- package/dist/ledger/types.js.map +1 -0
- package/dist/policy/evaluator.d.ts +21 -0
- package/dist/policy/evaluator.d.ts.map +1 -0
- package/dist/policy/evaluator.js +383 -0
- package/dist/policy/evaluator.js.map +1 -0
- package/dist/policy/loader.d.ts +27 -0
- package/dist/policy/loader.d.ts.map +1 -0
- package/dist/policy/loader.js +69 -0
- package/dist/policy/loader.js.map +1 -0
- package/dist/policy/schema.d.ts +168 -0
- package/dist/policy/schema.d.ts.map +1 -0
- package/dist/policy/schema.js +107 -0
- package/dist/policy/schema.js.map +1 -0
- package/dist/proxy/mcp-proxy.d.ts +43 -0
- package/dist/proxy/mcp-proxy.d.ts.map +1 -0
- package/dist/proxy/mcp-proxy.js +240 -0
- package/dist/proxy/mcp-proxy.js.map +1 -0
- package/dist/proxy/mcp-types.d.ts +79 -0
- package/dist/proxy/mcp-types.d.ts.map +1 -0
- package/dist/proxy/mcp-types.js +28 -0
- package/dist/proxy/mcp-types.js.map +1 -0
- package/dist/proxy/shell-proxy.d.ts +52 -0
- package/dist/proxy/shell-proxy.d.ts.map +1 -0
- package/dist/proxy/shell-proxy.js +92 -0
- package/dist/proxy/shell-proxy.js.map +1 -0
- package/dist/rollback/manager.d.ts +62 -0
- package/dist/rollback/manager.d.ts.map +1 -0
- package/dist/rollback/manager.js +151 -0
- package/dist/rollback/manager.js.map +1 -0
- package/dist/server/server.d.ts +24 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +200 -0
- package/dist/server/server.js.map +1 -0
- package/dist/tools/base.d.ts +58 -0
- package/dist/tools/base.d.ts.map +1 -0
- package/dist/tools/base.js +48 -0
- package/dist/tools/base.js.map +1 -0
- package/dist/tools/command-run.d.ts +30 -0
- package/dist/tools/command-run.d.ts.map +1 -0
- package/dist/tools/command-run.js +87 -0
- package/dist/tools/command-run.js.map +1 -0
- package/dist/tools/file-read.d.ts +34 -0
- package/dist/tools/file-read.d.ts.map +1 -0
- package/dist/tools/file-read.js +67 -0
- package/dist/tools/file-read.js.map +1 -0
- package/dist/tools/file-write.d.ts +39 -0
- package/dist/tools/file-write.d.ts.map +1 -0
- package/dist/tools/file-write.js +158 -0
- package/dist/tools/file-write.js.map +1 -0
- package/dist/tools/git.d.ts +48 -0
- package/dist/tools/git.d.ts.map +1 -0
- package/dist/tools/git.js +193 -0
- package/dist/tools/git.js.map +1 -0
- package/dist/tools/http-request.d.ts +48 -0
- package/dist/tools/http-request.d.ts.map +1 -0
- package/dist/tools/http-request.js +91 -0
- package/dist/tools/http-request.js.map +1 -0
- package/dist/types.d.ts +257 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/examples/coding-agent.policy.yaml +80 -0
- package/examples/devops-deploy.policy.yaml +107 -0
- package/examples/mcp-proxy.config.yaml +34 -0
- package/examples/simple-session.ts +161 -0
- package/examples/video-upscaler.policy.yaml +86 -0
- package/package.json +92 -0
- package/schemas/generate.ts +18 -0
- package/schemas/policy.schema.json +7 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Evaluator — runtime enforcement of policy rules.
|
|
3
|
+
*
|
|
4
|
+
* Given an ActionRequest and a loaded Policy, determines:
|
|
5
|
+
* - allow: action is permitted
|
|
6
|
+
* - deny: action is blocked
|
|
7
|
+
* - gate: action requires approval before execution
|
|
8
|
+
*
|
|
9
|
+
* Supports both stateless (single action) and stateful (session-aware) evaluation.
|
|
10
|
+
*/
|
|
11
|
+
import { minimatch } from 'minimatch';
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Main evaluation entry point (stateless — single action)
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
export function evaluateAction(request, policy, budget) {
|
|
16
|
+
// 1. Check forbidden patterns
|
|
17
|
+
const forbiddenCheck = checkForbiddenPatterns(request, policy);
|
|
18
|
+
if (forbiddenCheck) {
|
|
19
|
+
return {
|
|
20
|
+
verdict: 'deny',
|
|
21
|
+
tool: request.tool,
|
|
22
|
+
reasons: [forbiddenCheck],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// 2. Check capability exists for this tool
|
|
26
|
+
const capability = findCapability(request.tool, policy);
|
|
27
|
+
if (!capability) {
|
|
28
|
+
return {
|
|
29
|
+
verdict: 'deny',
|
|
30
|
+
tool: request.tool,
|
|
31
|
+
reasons: [`No capability defined for tool "${request.tool}"`],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// 3. Check scope constraints
|
|
35
|
+
const scopeViolations = checkScope(request, capability);
|
|
36
|
+
if (scopeViolations.length > 0) {
|
|
37
|
+
return {
|
|
38
|
+
verdict: 'deny',
|
|
39
|
+
tool: request.tool,
|
|
40
|
+
reasons: scopeViolations,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// 4. Check budget limits
|
|
44
|
+
if (budget) {
|
|
45
|
+
const budgetViolations = checkBudget(policy.limits, budget);
|
|
46
|
+
if (budgetViolations.length > 0) {
|
|
47
|
+
return {
|
|
48
|
+
verdict: 'deny',
|
|
49
|
+
tool: request.tool,
|
|
50
|
+
reasons: budgetViolations,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// 5. Check if a gate applies
|
|
55
|
+
const gate = findGate(request, policy);
|
|
56
|
+
if (gate) {
|
|
57
|
+
return {
|
|
58
|
+
verdict: 'gate',
|
|
59
|
+
tool: request.tool,
|
|
60
|
+
reasons: [`Gate required: ${gate.approval} approval for "${gate.action}" (risk: ${gate.risk_level ?? 'unspecified'})`],
|
|
61
|
+
gate,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
verdict: 'allow',
|
|
66
|
+
tool: request.tool,
|
|
67
|
+
reasons: ['Action permitted by policy'],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Session-aware evaluation (stateful)
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
/**
|
|
74
|
+
* Evaluate an action in the context of an active session.
|
|
75
|
+
* Checks session-level constraints on top of the standard policy checks.
|
|
76
|
+
*/
|
|
77
|
+
export function evaluateSessionAction(request, policy, session) {
|
|
78
|
+
const warnings = [];
|
|
79
|
+
// 1. Check if session is still active
|
|
80
|
+
if (session.state !== 'active') {
|
|
81
|
+
return {
|
|
82
|
+
verdict: 'deny',
|
|
83
|
+
tool: request.tool,
|
|
84
|
+
reasons: [`Session is ${session.state}, not accepting new actions`],
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
// 2. Check session constraints before standard policy checks
|
|
88
|
+
const sessionConstraints = policy.session;
|
|
89
|
+
if (sessionConstraints) {
|
|
90
|
+
// Max actions per session
|
|
91
|
+
if (sessionConstraints.max_actions != null) {
|
|
92
|
+
if (session.budget.actionsEvaluated >= sessionConstraints.max_actions) {
|
|
93
|
+
return {
|
|
94
|
+
verdict: 'deny',
|
|
95
|
+
tool: request.tool,
|
|
96
|
+
reasons: [`Session action limit reached: ${session.budget.actionsEvaluated} >= ${sessionConstraints.max_actions}`],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// Warn when approaching limit
|
|
100
|
+
const remaining = sessionConstraints.max_actions - session.budget.actionsEvaluated;
|
|
101
|
+
if (remaining <= 5) {
|
|
102
|
+
warnings.push(`Approaching action limit: ${remaining} actions remaining`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Max denials — terminate session if too many denials
|
|
106
|
+
if (sessionConstraints.max_denials != null) {
|
|
107
|
+
if (session.budget.actionsDenied >= sessionConstraints.max_denials) {
|
|
108
|
+
return {
|
|
109
|
+
verdict: 'deny',
|
|
110
|
+
tool: request.tool,
|
|
111
|
+
reasons: [`Session denial limit reached: ${session.budget.actionsDenied} >= ${sessionConstraints.max_denials}. Session should be terminated.`],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Rate limiting
|
|
116
|
+
if (sessionConstraints.rate_limit) {
|
|
117
|
+
const rateLimitViolation = checkRateLimit(session, sessionConstraints.rate_limit.max_per_minute);
|
|
118
|
+
if (rateLimitViolation) {
|
|
119
|
+
return {
|
|
120
|
+
verdict: 'deny',
|
|
121
|
+
tool: request.tool,
|
|
122
|
+
reasons: [rateLimitViolation],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// Escalation rules
|
|
127
|
+
if (sessionConstraints.escalation) {
|
|
128
|
+
const escalation = checkEscalation(session, sessionConstraints.escalation);
|
|
129
|
+
if (escalation) {
|
|
130
|
+
return {
|
|
131
|
+
verdict: 'gate',
|
|
132
|
+
tool: request.tool,
|
|
133
|
+
reasons: [escalation.reason],
|
|
134
|
+
gate: {
|
|
135
|
+
action: request.tool,
|
|
136
|
+
approval: 'human',
|
|
137
|
+
risk_level: 'medium',
|
|
138
|
+
condition: escalation.trigger,
|
|
139
|
+
},
|
|
140
|
+
warnings,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// 3. Run standard policy checks (with session budget)
|
|
146
|
+
const result = evaluateAction(request, policy, session.budget);
|
|
147
|
+
// Attach warnings
|
|
148
|
+
if (warnings.length > 0) {
|
|
149
|
+
return { ...result, warnings };
|
|
150
|
+
}
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
// ---------------------------------------------------------------------------
|
|
154
|
+
// Rate limiting
|
|
155
|
+
// ---------------------------------------------------------------------------
|
|
156
|
+
function checkRateLimit(session, maxPerMinute) {
|
|
157
|
+
const now = Date.now();
|
|
158
|
+
const oneMinuteAgo = now - 60_000;
|
|
159
|
+
// Count actions in the last minute
|
|
160
|
+
const recentActions = session.actions.filter((a) => {
|
|
161
|
+
const actionTime = new Date(a.timestamp).getTime();
|
|
162
|
+
return actionTime >= oneMinuteAgo;
|
|
163
|
+
});
|
|
164
|
+
if (recentActions.length >= maxPerMinute) {
|
|
165
|
+
return `Rate limit exceeded: ${recentActions.length} actions in the last minute (limit: ${maxPerMinute})`;
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
function checkEscalation(session, rules) {
|
|
170
|
+
const elapsedMinutes = (Date.now() - session.budget.startedAt) / 60_000;
|
|
171
|
+
for (const rule of rules) {
|
|
172
|
+
if (rule.after_actions != null && session.budget.actionsEvaluated >= rule.after_actions) {
|
|
173
|
+
// Check if a human check-in has already been done after this threshold
|
|
174
|
+
// by looking for a recent gate approval in the session history
|
|
175
|
+
const lastCheckinIndex = findLastCheckin(session, rule.after_actions);
|
|
176
|
+
if (lastCheckinIndex === -1) {
|
|
177
|
+
return {
|
|
178
|
+
reason: `Escalation: ${session.budget.actionsEvaluated} actions evaluated, human check-in required after ${rule.after_actions}`,
|
|
179
|
+
trigger: `after_actions:${rule.after_actions}`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (rule.after_minutes != null && elapsedMinutes >= rule.after_minutes) {
|
|
184
|
+
const lastTimeCheckin = findLastTimeCheckin(session, rule.after_minutes);
|
|
185
|
+
if (lastTimeCheckin === -1) {
|
|
186
|
+
return {
|
|
187
|
+
reason: `Escalation: ${Math.floor(elapsedMinutes)} minutes elapsed, human check-in required after ${rule.after_minutes} minutes`,
|
|
188
|
+
trigger: `after_minutes:${rule.after_minutes}`,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Find the last action index where a human check-in gate was approved
|
|
197
|
+
* after the given threshold was crossed.
|
|
198
|
+
*/
|
|
199
|
+
function findLastCheckin(session, threshold) {
|
|
200
|
+
// Look for any gated action (with human approval) that was resolved
|
|
201
|
+
// after the threshold was reached
|
|
202
|
+
for (let i = session.actions.length - 1; i >= 0; i--) {
|
|
203
|
+
const action = session.actions[i];
|
|
204
|
+
if (action.index >= threshold &&
|
|
205
|
+
action.validation.verdict === 'gate' &&
|
|
206
|
+
action.result !== undefined) {
|
|
207
|
+
return i;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
return -1;
|
|
211
|
+
}
|
|
212
|
+
function findLastTimeCheckin(session, _afterMinutes) {
|
|
213
|
+
// Look for any gated action that was a time-based escalation and was resolved
|
|
214
|
+
for (let i = session.actions.length - 1; i >= 0; i--) {
|
|
215
|
+
const action = session.actions[i];
|
|
216
|
+
if (action.validation.verdict === 'gate' &&
|
|
217
|
+
action.validation.gate?.condition?.startsWith('after_minutes:') &&
|
|
218
|
+
action.result !== undefined) {
|
|
219
|
+
return i;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return -1;
|
|
223
|
+
}
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
// Forbidden pattern checks
|
|
226
|
+
// ---------------------------------------------------------------------------
|
|
227
|
+
function checkForbiddenPatterns(request, policy) {
|
|
228
|
+
const input = request.input;
|
|
229
|
+
for (const forbidden of policy.forbidden) {
|
|
230
|
+
const pattern = forbidden.pattern;
|
|
231
|
+
// Check against path-like inputs
|
|
232
|
+
const pathValue = (input.path ?? input.file ?? input.target);
|
|
233
|
+
if (pathValue && minimatch(pathValue, pattern)) {
|
|
234
|
+
return `Path "${pathValue}" matches forbidden pattern "${pattern}"`;
|
|
235
|
+
}
|
|
236
|
+
// Check against command inputs
|
|
237
|
+
const command = (input.command ?? input.cmd);
|
|
238
|
+
if (command && command.includes(pattern)) {
|
|
239
|
+
return `Command contains forbidden pattern "${pattern}"`;
|
|
240
|
+
}
|
|
241
|
+
// Check against URL inputs
|
|
242
|
+
const url = (input.url ?? input.endpoint);
|
|
243
|
+
if (url && minimatch(url, pattern)) {
|
|
244
|
+
return `URL "${url}" matches forbidden pattern "${pattern}"`;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
// Capability lookup
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
function findCapability(tool, policy) {
|
|
253
|
+
return policy.capabilities.find((cap) => cap.tool === tool);
|
|
254
|
+
}
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Scope validation
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
function checkScope(request, capability) {
|
|
259
|
+
const violations = [];
|
|
260
|
+
const input = request.input;
|
|
261
|
+
const scope = capability.scope;
|
|
262
|
+
// Path scope
|
|
263
|
+
if (scope.paths) {
|
|
264
|
+
const pathValue = (input.path ?? input.file ?? input.target);
|
|
265
|
+
if (pathValue) {
|
|
266
|
+
const allowed = scope.paths.some((pattern) => minimatch(pathValue, pattern));
|
|
267
|
+
if (!allowed) {
|
|
268
|
+
violations.push(`Path "${pathValue}" is outside allowed scope: [${scope.paths.join(', ')}]`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Binary scope (for command:run)
|
|
273
|
+
if (scope.binaries) {
|
|
274
|
+
const binary = (input.binary ?? input.command);
|
|
275
|
+
if (binary) {
|
|
276
|
+
// Extract the base binary name (first word of the command)
|
|
277
|
+
const baseBinary = binary.split(/\s+/)[0];
|
|
278
|
+
const baseName = baseBinary.split('/').pop() ?? baseBinary;
|
|
279
|
+
if (!scope.binaries.includes(baseName)) {
|
|
280
|
+
violations.push(`Binary "${baseName}" is not in allowed list: [${scope.binaries.join(', ')}]`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Domain scope (for http:request)
|
|
285
|
+
if (scope.domains) {
|
|
286
|
+
const url = (input.url ?? input.endpoint);
|
|
287
|
+
if (url) {
|
|
288
|
+
try {
|
|
289
|
+
const parsed = new URL(url);
|
|
290
|
+
if (!scope.domains.includes(parsed.hostname)) {
|
|
291
|
+
violations.push(`Domain "${parsed.hostname}" is not in allowed list: [${scope.domains.join(', ')}]`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
violations.push(`Invalid URL: "${url}"`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Method scope (for http:request)
|
|
300
|
+
if (scope.methods) {
|
|
301
|
+
const method = (input.method ?? 'GET').toUpperCase();
|
|
302
|
+
if (!scope.methods.includes(method)) {
|
|
303
|
+
violations.push(`HTTP method "${method}" is not in allowed list: [${scope.methods.join(', ')}]`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
// Repo scope (for git operations)
|
|
307
|
+
if (scope.repos) {
|
|
308
|
+
const repo = (input.repo ?? input.repository);
|
|
309
|
+
if (repo) {
|
|
310
|
+
const allowed = scope.repos.some((pattern) => minimatch(repo, pattern));
|
|
311
|
+
if (!allowed) {
|
|
312
|
+
violations.push(`Repository "${repo}" is outside allowed scope: [${scope.repos.join(', ')}]`);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return violations;
|
|
317
|
+
}
|
|
318
|
+
// ---------------------------------------------------------------------------
|
|
319
|
+
// Budget checks
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
function checkBudget(limits, budget) {
|
|
322
|
+
const violations = [];
|
|
323
|
+
if (limits.max_runtime_ms != null) {
|
|
324
|
+
const elapsed = Date.now() - budget.startedAt;
|
|
325
|
+
if (elapsed > limits.max_runtime_ms) {
|
|
326
|
+
violations.push(`Runtime budget exceeded: ${elapsed}ms > ${limits.max_runtime_ms}ms`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
if (limits.max_files_changed != null && budget.filesChanged >= limits.max_files_changed) {
|
|
330
|
+
violations.push(`File change budget exceeded: ${budget.filesChanged} >= ${limits.max_files_changed}`);
|
|
331
|
+
}
|
|
332
|
+
if (limits.max_output_bytes != null && budget.totalOutputBytes >= limits.max_output_bytes) {
|
|
333
|
+
violations.push(`Output size budget exceeded: ${budget.totalOutputBytes} >= ${limits.max_output_bytes}`);
|
|
334
|
+
}
|
|
335
|
+
if (limits.max_retries != null && budget.retries >= limits.max_retries) {
|
|
336
|
+
violations.push(`Retry budget exceeded: ${budget.retries} >= ${limits.max_retries}`);
|
|
337
|
+
}
|
|
338
|
+
if (limits.max_cost_usd != null && budget.costUsd >= limits.max_cost_usd) {
|
|
339
|
+
violations.push(`Cost budget exceeded: $${budget.costUsd} >= $${limits.max_cost_usd}`);
|
|
340
|
+
}
|
|
341
|
+
return violations;
|
|
342
|
+
}
|
|
343
|
+
// ---------------------------------------------------------------------------
|
|
344
|
+
// Gate lookup
|
|
345
|
+
// ---------------------------------------------------------------------------
|
|
346
|
+
function findGate(request, policy) {
|
|
347
|
+
return policy.gates.find((gate) => {
|
|
348
|
+
if (gate.action !== request.tool)
|
|
349
|
+
return false;
|
|
350
|
+
// If gate has a condition, evaluate it
|
|
351
|
+
if (gate.condition === 'outside_scope') {
|
|
352
|
+
// This gate fires only if the action targets something outside scope
|
|
353
|
+
const capability = findCapability(request.tool, policy);
|
|
354
|
+
if (capability) {
|
|
355
|
+
const scopeViolations = checkScope(request, capability);
|
|
356
|
+
return scopeViolations.length > 0;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
// No condition or unknown condition — gate always applies
|
|
360
|
+
return !gate.condition;
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
// ---------------------------------------------------------------------------
|
|
364
|
+
// Utility: assess risk level for an action
|
|
365
|
+
// ---------------------------------------------------------------------------
|
|
366
|
+
export function assessRiskLevel(request, policy) {
|
|
367
|
+
// Check if there's an explicit gate with a risk level
|
|
368
|
+
const gate = policy.gates.find((g) => g.action === request.tool);
|
|
369
|
+
if (gate?.risk_level)
|
|
370
|
+
return gate.risk_level;
|
|
371
|
+
// Heuristic risk assessment
|
|
372
|
+
const tool = request.tool;
|
|
373
|
+
if (tool === 'file:delete' || tool === 'command:run')
|
|
374
|
+
return 'high';
|
|
375
|
+
if (tool === 'file:write' || tool === 'git:apply')
|
|
376
|
+
return 'medium';
|
|
377
|
+
if (tool === 'http:request')
|
|
378
|
+
return 'medium';
|
|
379
|
+
if (tool === 'file:read' || tool === 'git:diff')
|
|
380
|
+
return 'low';
|
|
381
|
+
return 'medium';
|
|
382
|
+
}
|
|
383
|
+
//# sourceMappingURL=evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../../src/policy/evaluator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Loader — reads a YAML policy file and validates it against the Zod schema.
|
|
3
|
+
*/
|
|
4
|
+
import type { Policy } from '../types.js';
|
|
5
|
+
export declare class PolicyValidationError extends Error {
|
|
6
|
+
readonly issues: Array<{
|
|
7
|
+
path: string;
|
|
8
|
+
message: string;
|
|
9
|
+
}>;
|
|
10
|
+
constructor(message: string, issues: Array<{
|
|
11
|
+
path: string;
|
|
12
|
+
message: string;
|
|
13
|
+
}>);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Load and validate a policy from a YAML file path.
|
|
17
|
+
*/
|
|
18
|
+
export declare function loadPolicyFromFile(filePath: string): Policy;
|
|
19
|
+
/**
|
|
20
|
+
* Parse and validate a policy from a YAML string.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parsePolicyYaml(yamlString: string): Policy;
|
|
23
|
+
/**
|
|
24
|
+
* Validate a policy object (already parsed).
|
|
25
|
+
*/
|
|
26
|
+
export declare function validatePolicy(policy: unknown): Policy;
|
|
27
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/policy/loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,qBAAa,qBAAsB,SAAQ,KAAK;aAG5B,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;gBADhE,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAKnE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ3D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CA8B1D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAatD"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Loader — reads a YAML policy file and validates it against the Zod schema.
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
import { PolicySchema } from './schema.js';
|
|
8
|
+
export class PolicyValidationError extends Error {
|
|
9
|
+
issues;
|
|
10
|
+
constructor(message, issues) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.issues = issues;
|
|
13
|
+
this.name = 'PolicyValidationError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Load and validate a policy from a YAML file path.
|
|
18
|
+
*/
|
|
19
|
+
export function loadPolicyFromFile(filePath) {
|
|
20
|
+
const absPath = path.resolve(filePath);
|
|
21
|
+
if (!fs.existsSync(absPath)) {
|
|
22
|
+
throw new Error(`Policy file not found: ${absPath}`);
|
|
23
|
+
}
|
|
24
|
+
const raw = fs.readFileSync(absPath, 'utf-8');
|
|
25
|
+
return parsePolicyYaml(raw);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parse and validate a policy from a YAML string.
|
|
29
|
+
*/
|
|
30
|
+
export function parsePolicyYaml(yamlString) {
|
|
31
|
+
let parsed;
|
|
32
|
+
try {
|
|
33
|
+
parsed = yaml.load(yamlString);
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
throw new PolicyValidationError('Invalid YAML syntax', [
|
|
37
|
+
{ path: '', message: err.message },
|
|
38
|
+
]);
|
|
39
|
+
}
|
|
40
|
+
if (parsed === null || parsed === undefined || typeof parsed !== 'object') {
|
|
41
|
+
throw new PolicyValidationError('Policy must be a YAML object', [
|
|
42
|
+
{ path: '', message: 'Expected an object, got ' + typeof parsed },
|
|
43
|
+
]);
|
|
44
|
+
}
|
|
45
|
+
const result = PolicySchema.safeParse(parsed);
|
|
46
|
+
if (!result.success) {
|
|
47
|
+
const issues = result.error.issues.map((issue) => ({
|
|
48
|
+
path: issue.path.join('.'),
|
|
49
|
+
message: issue.message,
|
|
50
|
+
}));
|
|
51
|
+
throw new PolicyValidationError(`Policy validation failed with ${issues.length} issue(s)`, issues);
|
|
52
|
+
}
|
|
53
|
+
return result.data;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Validate a policy object (already parsed).
|
|
57
|
+
*/
|
|
58
|
+
export function validatePolicy(policy) {
|
|
59
|
+
const result = PolicySchema.safeParse(policy);
|
|
60
|
+
if (!result.success) {
|
|
61
|
+
const issues = result.error.issues.map((issue) => ({
|
|
62
|
+
path: issue.path.join('.'),
|
|
63
|
+
message: issue.message,
|
|
64
|
+
}));
|
|
65
|
+
throw new PolicyValidationError(`Policy validation failed with ${issues.length} issue(s)`, issues);
|
|
66
|
+
}
|
|
67
|
+
return result.data;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/policy/loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAG5B;IAFlB,YACE,OAAe,EACC,MAAgD;QAEhE,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,WAAM,GAAN,MAAM,CAA0C;QAGhE,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACvC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,0BAA0B,OAAO,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9C,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,UAAkB;IAChD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACjC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,qBAAqB,CAAC,qBAAqB,EAAE;YACrD,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAG,GAAa,CAAC,OAAO,EAAE;SAC9C,CAAC,CAAC;IACL,CAAC;IAED,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC1E,MAAM,IAAI,qBAAqB,CAAC,8BAA8B,EAAE;YAC9D,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,0BAA0B,GAAG,OAAO,MAAM,EAAE;SAClE,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAE9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACjD,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC,CAAC,CAAC;QACJ,MAAM,IAAI,qBAAqB,CAC7B,iCAAiC,MAAM,CAAC,MAAM,WAAW,EACzD,MAAM,CACP,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,CAAC,IAAc,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,MAAe;IAC5C,MAAM,MAAM,GAAG,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACjD,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC,CAAC,CAAC;QACJ,MAAM,IAAI,qBAAqB,CAC7B,iCAAiC,MAAM,CAAC,MAAM,WAAW,EACzD,MAAM,CACP,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,IAAc,CAAC;AAC/B,CAAC"}
|