@guilz-dev/belay 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/README.md +7 -1
- package/dist/adapters/layouts/scope.js +3 -2
- package/dist/adapters/shared/gate-runtime.d.ts +1 -2
- package/dist/adapters/shared/gate-runtime.js +226 -28
- package/dist/bundle/claude-runtime.mjs +6967 -4078
- package/dist/bundle/codex-runtime.mjs +5951 -3064
- package/dist/bundle/cursor-runtime.mjs +5951 -3064
- package/dist/cli.js +43 -1
- package/dist/commands/classify-for-report.js +1 -1
- package/dist/commands/config.d.ts +8 -0
- package/dist/commands/config.js +134 -11
- package/dist/commands/doctor.js +36 -1
- package/dist/commands/explain.js +11 -1
- package/dist/commands/judge.js +3 -2
- package/dist/commands/session.d.ts +20 -0
- package/dist/commands/session.js +51 -0
- package/dist/config-io.js +49 -12
- package/dist/conformance/guarantee-posture.d.ts +21 -0
- package/dist/conformance/guarantee-posture.js +46 -0
- package/dist/conformance/guarantee-table.d.ts +1 -0
- package/dist/conformance/guarantee-table.js +14 -9
- package/dist/core/approval-service.js +6 -2
- package/dist/core/approval.d.ts +2 -0
- package/dist/core/approval.js +14 -3
- package/dist/core/capability/approval-state-mutation.d.ts +13 -0
- package/dist/core/capability/approval-state-mutation.js +92 -0
- package/dist/core/capability/approval-v3.d.ts +15 -0
- package/dist/core/capability/approval-v3.js +74 -0
- package/dist/core/capability/attestation.d.ts +13 -0
- package/dist/core/capability/attestation.js +55 -0
- package/dist/core/capability/boundary-attestation-sign.d.ts +21 -0
- package/dist/core/capability/boundary-attestation-sign.js +84 -0
- package/dist/core/capability/boundary-driver-container.d.ts +7 -0
- package/dist/core/capability/boundary-driver-container.js +122 -0
- package/dist/core/capability/boundary-driver.d.ts +29 -0
- package/dist/core/capability/boundary-driver.js +43 -0
- package/dist/core/capability/boundary-egress.d.ts +21 -0
- package/dist/core/capability/boundary-egress.js +109 -0
- package/dist/core/capability/boundary-grant-materialize.d.ts +13 -0
- package/dist/core/capability/boundary-grant-materialize.js +78 -0
- package/dist/core/capability/boundary-profile.d.ts +15 -0
- package/dist/core/capability/boundary-profile.js +25 -0
- package/dist/core/capability/boundary-run.d.ts +23 -0
- package/dist/core/capability/boundary-run.js +23 -0
- package/dist/core/capability/boundary-session.d.ts +57 -0
- package/dist/core/capability/boundary-session.js +129 -0
- package/dist/core/capability/capability-request-hash.d.ts +2 -0
- package/dist/core/capability/capability-request-hash.js +8 -0
- package/dist/core/capability/gate-latency-ratchet.d.ts +7 -0
- package/dist/core/capability/gate-latency-ratchet.js +18 -0
- package/dist/core/capability/gate-policy-shadow.d.ts +25 -0
- package/dist/core/capability/gate-policy-shadow.js +108 -0
- package/dist/core/capability/gate-shadow-audit.d.ts +1 -0
- package/dist/core/capability/gate-shadow-audit.js +1 -0
- package/dist/core/capability/gate-shadow-ratchet.d.ts +14 -0
- package/dist/core/capability/gate-shadow-ratchet.js +81 -0
- package/dist/core/capability/grant-lease.d.ts +9 -0
- package/dist/core/capability/grant-lease.js +46 -0
- package/dist/core/capability/grant-loader.d.ts +12 -0
- package/dist/core/capability/grant-loader.js +23 -0
- package/dist/core/capability/grant-match.d.ts +6 -0
- package/dist/core/capability/grant-match.js +93 -0
- package/dist/core/capability/grant.d.ts +16 -0
- package/dist/core/capability/grant.js +16 -0
- package/dist/core/capability/index.d.ts +8 -0
- package/dist/core/capability/index.js +7 -0
- package/dist/core/capability/limits.d.ts +7 -0
- package/dist/core/capability/limits.js +55 -0
- package/dist/core/capability/policy-bridge.d.ts +18 -0
- package/dist/core/capability/policy-bridge.js +51 -0
- package/dist/core/capability/policy-engine.d.ts +70 -0
- package/dist/core/capability/policy-engine.js +476 -0
- package/dist/core/capability/policy-types.d.ts +20 -0
- package/dist/core/capability/policy-types.js +1 -0
- package/dist/core/capability/request.d.ts +45 -0
- package/dist/core/capability/request.js +1 -0
- package/dist/core/capability/resolver.d.ts +4 -0
- package/dist/core/capability/resolver.js +4 -0
- package/dist/core/classify-subagent.d.ts +2 -1
- package/dist/core/classify-subagent.js +38 -2
- package/dist/core/classify-tool.js +126 -62
- package/dist/core/config.d.ts +14 -3
- package/dist/core/config.js +56 -3
- package/dist/core/gate-contract.d.ts +3 -0
- package/dist/core/gate-contract.js +3 -0
- package/dist/core/gate-engine.js +6 -1
- package/dist/core/judge-doctor.js +39 -14
- package/dist/core/judge-fallback-hints.d.ts +13 -0
- package/dist/core/judge-fallback-hints.js +108 -0
- package/dist/core/path-utils.d.ts +1 -0
- package/dist/core/path-utils.js +5 -2
- package/dist/core/recovery/capability.d.ts +3 -0
- package/dist/core/recovery/capability.js +22 -0
- package/dist/core/recovery/fail-closed.d.ts +6 -0
- package/dist/core/recovery/fail-closed.js +25 -0
- package/dist/core/recovery/index.d.ts +3 -0
- package/dist/core/recovery/index.js +2 -0
- package/dist/core/recovery/types.d.ts +20 -0
- package/dist/core/recovery/types.js +1 -0
- package/dist/core/standing-allow.js +3 -0
- package/dist/core/transactional/diff-evaluator.js +4 -1
- package/dist/core/transactional/eligibility.js +4 -0
- package/dist/core/transactional/git-worktree.d.ts +10 -3
- package/dist/core/transactional/git-worktree.js +81 -11
- package/dist/core/transactional/reasons.d.ts +1 -0
- package/dist/core/transactional/reasons.js +4 -0
- package/dist/core/transactional/runner.js +22 -6
- package/dist/core/transactional/types.d.ts +4 -0
- package/dist/core/types.d.ts +21 -3
- package/dist/core/verdict/adapter.d.ts +3 -4
- package/dist/core/verdict/adapter.js +16 -12
- package/dist/core/verdict/cursor-acp-client.d.ts +67 -0
- package/dist/core/verdict/cursor-acp-client.js +345 -0
- package/dist/core/verdict/egress-classify.d.ts +2 -2
- package/dist/core/verdict/egress-classify.js +2 -2
- package/dist/core/verdict/git-classifier.d.ts +26 -0
- package/dist/core/verdict/git-classifier.js +533 -0
- package/dist/core/verdict/judge-audit.js +1 -0
- package/dist/core/verdict/judge-baseline.d.ts +1 -1
- package/dist/core/verdict/judge-baseline.js +1 -0
- package/dist/core/verdict/judge-broker-service.js +12 -0
- package/dist/core/verdict/judge-catalog.js +1 -0
- package/dist/core/verdict/judge-cli-fingerprint.d.ts +1 -0
- package/dist/core/verdict/judge-cli-fingerprint.js +6 -0
- package/dist/core/verdict/judge-cli.js +5 -3
- package/dist/core/verdict/judge-provider-matrix.js +1 -1
- package/dist/core/verdict/judge-runtime-config.d.ts +2 -0
- package/dist/core/verdict/judge-runtime-config.js +10 -6
- package/dist/core/verdict/judge-session-broker.d.ts +1 -1
- package/dist/core/verdict/judge-session-broker.js +10 -5
- package/dist/core/verdict/judge-transport.d.ts +5 -1
- package/dist/core/verdict/judge-transport.js +24 -14
- package/dist/core/verdict/judge.d.ts +2 -15
- package/dist/core/verdict/judge.js +5 -68
- package/dist/core/verdict/launcher-resolve.d.ts +2 -0
- package/dist/core/verdict/launcher-resolve.js +16 -0
- package/dist/core/verdict/prescan.d.ts +15 -0
- package/dist/core/verdict/prescan.js +68 -0
- package/dist/core/verdict/shell-policy.d.ts +25 -0
- package/dist/core/verdict/shell-policy.js +40 -0
- package/dist/core/verdict/shell-semantics.d.ts +24 -0
- package/dist/core/verdict/shell-semantics.js +225 -0
- package/dist/core/verdict/types.d.ts +13 -1
- package/dist/core/verdict/verdict.js +296 -178
- package/dist/corpus/evaluate.js +3 -7
- package/dist/corpus/gate-latency-budget.d.ts +39 -0
- package/dist/corpus/gate-latency-budget.js +33 -0
- package/dist/corpus/mutators.d.ts +1 -1
- package/dist/corpus/mutators.js +11 -0
- package/dist/corpus/runtime-match.js +1 -3
- package/dist/corpus/standing-allow-catalog.generated.js +29 -9
- package/dist/judge-broker-daemon.js +15 -1
- package/dist/services/sandbox-service.d.ts +4 -0
- package/dist/services/sandbox-service.js +28 -2
- package/dist/types.d.ts +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { policyDecisionToLegacyReason } from '../capability/policy-bridge.js';
|
|
3
|
+
import { policyDecisionRequiresAsk } from '../capability/policy-engine.js';
|
|
4
|
+
import { canonicalPath, resolveMutationTarget } from '../path-utils.js';
|
|
2
5
|
import { extractRedirectTargets, isFdDuplication, isRedirectOperator, tokenizeShell, } from '../shell-tokenizer.js';
|
|
3
6
|
import { analyzePathTargets, cwdRelative, isDestructiveHighStakesMutation, resolveTrustedPath, touchesProtectedRoot, } from './containment.js';
|
|
4
7
|
import { classifyEgressTool } from './egress-classify.js';
|
|
5
8
|
import { verdictFingerprint } from './fingerprint.js';
|
|
6
|
-
import {
|
|
7
|
-
import { isRoutineLauncher, resolveLauncherRecipe } from './launcher-resolve.js';
|
|
9
|
+
import { isReadOnlyLauncherInvocation, isRoutineLauncher, resolveLauncherRecipe, } from './launcher-resolve.js';
|
|
8
10
|
import { allowFromCustomOverride, askFromCustomExternal, customAllowMatch, customExternalMatch, } from './overrides.js';
|
|
9
11
|
import { extractRecursiveScript, isBareInterpreter, isVariableIndirectHead, parseSegment, peelTransparentWrappers, redactCommand, segmentOpacity, splitTopLevelSegments, substitutionInners, } from './parser.js';
|
|
12
|
+
import { mutationPrescanRequiresAsk, prescanInterpreterCode, tier1RequiresAsk } from './prescan.js';
|
|
13
|
+
import { evaluateSegmentShellPolicy, shellPolicyMetadata, } from './shell-policy.js';
|
|
14
|
+
import { analyzeShellCommandSemantics, isPureReadOnlySemantics, LOCAL_MUTATION_KEYS, } from './shell-semantics.js';
|
|
10
15
|
const DEFAULT_MAX_DEPTH = 8;
|
|
11
16
|
const TIER0_EXTERNAL_KEYS = new Set([
|
|
12
17
|
'git push',
|
|
@@ -20,7 +25,6 @@ const TIER0_EXTERNAL_KEYS = new Set([
|
|
|
20
25
|
'supabase',
|
|
21
26
|
'scp',
|
|
22
27
|
'ssh',
|
|
23
|
-
'rsync',
|
|
24
28
|
]);
|
|
25
29
|
const TIER0_EXTERNAL_HEADS = new Set([
|
|
26
30
|
'dropdb',
|
|
@@ -30,66 +34,6 @@ const TIER0_EXTERNAL_HEADS = new Set([
|
|
|
30
34
|
'mongosh',
|
|
31
35
|
'redis-cli',
|
|
32
36
|
]);
|
|
33
|
-
const READ_ONLY_KEYS = new Set([
|
|
34
|
-
'cat',
|
|
35
|
-
'cd',
|
|
36
|
-
'echo',
|
|
37
|
-
'git diff',
|
|
38
|
-
'git log',
|
|
39
|
-
'git rev-parse',
|
|
40
|
-
'git show',
|
|
41
|
-
'git status',
|
|
42
|
-
'head',
|
|
43
|
-
'ls',
|
|
44
|
-
'pwd',
|
|
45
|
-
'rg',
|
|
46
|
-
'sort',
|
|
47
|
-
'tail',
|
|
48
|
-
'wc',
|
|
49
|
-
'which',
|
|
50
|
-
'find',
|
|
51
|
-
]);
|
|
52
|
-
const PURE_READ_ONLY_KEYS = new Set([
|
|
53
|
-
'echo',
|
|
54
|
-
'git diff',
|
|
55
|
-
'git log',
|
|
56
|
-
'git rev-parse',
|
|
57
|
-
'git show',
|
|
58
|
-
'git status',
|
|
59
|
-
'pwd',
|
|
60
|
-
'which',
|
|
61
|
-
]);
|
|
62
|
-
const LOCAL_MUTATION_KEYS = new Set([
|
|
63
|
-
'chmod',
|
|
64
|
-
'cp',
|
|
65
|
-
'git add',
|
|
66
|
-
'git clean',
|
|
67
|
-
'git commit',
|
|
68
|
-
'git mv',
|
|
69
|
-
'git reset',
|
|
70
|
-
'mkdir',
|
|
71
|
-
'mv',
|
|
72
|
-
'rm',
|
|
73
|
-
'sed',
|
|
74
|
-
'tee',
|
|
75
|
-
'touch',
|
|
76
|
-
'truncate',
|
|
77
|
-
]);
|
|
78
|
-
/** Routine local build/test runners resolved from launcher recipes. */
|
|
79
|
-
const LOCAL_ROUTINE_HEADS = new Set([
|
|
80
|
-
'tsc',
|
|
81
|
-
'vitest',
|
|
82
|
-
'vite',
|
|
83
|
-
'webpack',
|
|
84
|
-
'esbuild',
|
|
85
|
-
'rollup',
|
|
86
|
-
'jest',
|
|
87
|
-
'mocha',
|
|
88
|
-
'cargo',
|
|
89
|
-
'go',
|
|
90
|
-
'make',
|
|
91
|
-
'cmake',
|
|
92
|
-
]);
|
|
93
37
|
const BELAY_SELF_COMMANDS = new Set(['approve', 'revoke']);
|
|
94
38
|
const FIND_DANGEROUS_FLAGS = new Set(['-delete', '-exec', '-execdir', '-ok', '-okdir']);
|
|
95
39
|
function isFindDangerous(tokens) {
|
|
@@ -126,7 +70,7 @@ async function evaluateSubstitutions(command, context, depth) {
|
|
|
126
70
|
}
|
|
127
71
|
worst = worst ? combineInternal(worst, innerVerdict) : innerVerdict;
|
|
128
72
|
}
|
|
129
|
-
if (!worst) {
|
|
73
|
+
if (!worst || worst.permission === 'allow') {
|
|
130
74
|
return null;
|
|
131
75
|
}
|
|
132
76
|
return {
|
|
@@ -177,19 +121,39 @@ function combineInternal(left, right) {
|
|
|
177
121
|
: right.reason,
|
|
178
122
|
signals: [...new Set([...left.signals, ...right.signals])],
|
|
179
123
|
judgeTrace: right.judgeTrace ?? left.judgeTrace,
|
|
124
|
+
capabilityRequests: [...(left.capabilityRequests ?? []), ...(right.capabilityRequests ?? [])],
|
|
125
|
+
authorizationDecision: worsePermission(left.permission, right.permission) === 'ask'
|
|
126
|
+
? right.permission === 'ask'
|
|
127
|
+
? (right.authorizationDecision ?? left.authorizationDecision)
|
|
128
|
+
: left.authorizationDecision
|
|
129
|
+
: (right.authorizationDecision ?? left.authorizationDecision),
|
|
180
130
|
};
|
|
181
131
|
}
|
|
182
132
|
function askVerdict(params) {
|
|
183
133
|
return { ...params, permission: 'ask' };
|
|
184
134
|
}
|
|
185
|
-
function
|
|
186
|
-
|
|
135
|
+
function resolveShellPathTargets(pathArgs, context) {
|
|
136
|
+
if (!context.cwd) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
return pathArgs.map((target) => {
|
|
140
|
+
const resolved = (context.trustedCwd ? resolveTrustedPath(target, context.cwd, context.trustedCwd) : null) ??
|
|
141
|
+
resolveMutationTarget(target, context.cwd);
|
|
142
|
+
if (!resolved) {
|
|
143
|
+
const joined = path.isAbsolute(target) ? target : path.join(context.cwd, target);
|
|
144
|
+
return canonicalPath(joined);
|
|
145
|
+
}
|
|
146
|
+
return canonicalPath(resolved);
|
|
147
|
+
});
|
|
187
148
|
}
|
|
188
|
-
function
|
|
189
|
-
if (
|
|
190
|
-
return
|
|
149
|
+
function withShellPolicyMetadata(input, existing = {}) {
|
|
150
|
+
if (existing.capabilityRequests?.length) {
|
|
151
|
+
return existing;
|
|
191
152
|
}
|
|
192
|
-
return
|
|
153
|
+
return shellPolicyMetadata(input);
|
|
154
|
+
}
|
|
155
|
+
function allowVerdict(params) {
|
|
156
|
+
return { ...params, permission: 'allow' };
|
|
193
157
|
}
|
|
194
158
|
function extractPathArgs(tokens) {
|
|
195
159
|
const redirects = extractRedirectTargets(tokens);
|
|
@@ -209,8 +173,15 @@ function extractPathArgs(tokens) {
|
|
|
209
173
|
function isVariableOrOpaquePathToken(token) {
|
|
210
174
|
return token.includes('$') || token.includes('`');
|
|
211
175
|
}
|
|
212
|
-
function
|
|
213
|
-
|
|
176
|
+
function pathContextFromSemantics(context, semantics) {
|
|
177
|
+
const cwd = semantics.gitWorkTree ?? semantics.effectiveCwd;
|
|
178
|
+
if (!cwd || cwd === context.cwd) {
|
|
179
|
+
return context;
|
|
180
|
+
}
|
|
181
|
+
return { ...context, cwd };
|
|
182
|
+
}
|
|
183
|
+
function isPureReadOnlySegment(segment, semantics) {
|
|
184
|
+
return isPureReadOnlySemantics(segment, semantics);
|
|
214
185
|
}
|
|
215
186
|
function updateChainState(command, state) {
|
|
216
187
|
const segment = parseSegment(command);
|
|
@@ -233,10 +204,13 @@ function updateChainState(command, state) {
|
|
|
233
204
|
trustedCwd: true,
|
|
234
205
|
};
|
|
235
206
|
}
|
|
236
|
-
function tier0ExternalMatch(key, head, tokens) {
|
|
207
|
+
function tier0ExternalMatch(key, head, tokens, signals = []) {
|
|
237
208
|
if (TIER0_EXTERNAL_KEYS.has(key)) {
|
|
238
209
|
return true;
|
|
239
210
|
}
|
|
211
|
+
if (signals.includes('git.push')) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
240
214
|
if (TIER0_EXTERNAL_HEADS.has(head)) {
|
|
241
215
|
return true;
|
|
242
216
|
}
|
|
@@ -248,9 +222,6 @@ function tier0ExternalMatch(key, head, tokens) {
|
|
|
248
222
|
tokens.some((t) => t === '--push' || t.startsWith('--output=type=registry')))) {
|
|
249
223
|
return true;
|
|
250
224
|
}
|
|
251
|
-
if (head === 'git' && tokens[1] === 'push') {
|
|
252
|
-
return true;
|
|
253
|
-
}
|
|
254
225
|
if (head === 'terraform' && tokens[1] === 'apply') {
|
|
255
226
|
return true;
|
|
256
227
|
}
|
|
@@ -355,6 +326,39 @@ function tier0HighStakesRm(tokens, context) {
|
|
|
355
326
|
}
|
|
356
327
|
return null;
|
|
357
328
|
}
|
|
329
|
+
function verdictFromOpacityPolicy(params) {
|
|
330
|
+
const { request, decision } = evaluateSegmentShellPolicy({
|
|
331
|
+
command: params.command,
|
|
332
|
+
segmentHead: params.segmentHead,
|
|
333
|
+
effect: params.effect,
|
|
334
|
+
location: params.location,
|
|
335
|
+
opacity: params.opacity,
|
|
336
|
+
pathArgs: params.pathArgs,
|
|
337
|
+
signals: params.signals,
|
|
338
|
+
context: params.context,
|
|
339
|
+
});
|
|
340
|
+
const base = {
|
|
341
|
+
location: params.location,
|
|
342
|
+
opacity: params.opacity,
|
|
343
|
+
effect: params.effect,
|
|
344
|
+
confidence: 'deterministic',
|
|
345
|
+
signals: [...params.signals, ...decision.signals],
|
|
346
|
+
capabilityRequests: [request],
|
|
347
|
+
authorizationDecision: decision,
|
|
348
|
+
};
|
|
349
|
+
if (policyDecisionRequiresAsk(decision)) {
|
|
350
|
+
return {
|
|
351
|
+
permission: 'ask',
|
|
352
|
+
reason: policyDecisionToLegacyReason(decision),
|
|
353
|
+
...base,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
permission: 'allow',
|
|
358
|
+
reason: policyDecisionToLegacyReason(decision),
|
|
359
|
+
...base,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
358
362
|
async function evaluateSegment(command, context, depth) {
|
|
359
363
|
const maxDepth = context.maxRecursionDepth ?? DEFAULT_MAX_DEPTH;
|
|
360
364
|
if (depth > maxDepth) {
|
|
@@ -369,23 +373,15 @@ async function evaluateSegment(command, context, depth) {
|
|
|
369
373
|
}
|
|
370
374
|
const opacity = segmentOpacity(command);
|
|
371
375
|
if (opacity === 'unparseable') {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
opacity: 'unparseable',
|
|
376
|
-
effect: 'unknown',
|
|
377
|
-
confidence: 'deterministic',
|
|
378
|
-
reason: 'unparseable_shell',
|
|
379
|
-
signals: ['unparseable_shell'],
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
return allowVerdict({
|
|
383
|
-
location: 'unknown',
|
|
376
|
+
return verdictFromOpacityPolicy({
|
|
377
|
+
command,
|
|
378
|
+
segmentHead: parseSegment(command).head,
|
|
384
379
|
opacity: 'unparseable',
|
|
385
380
|
effect: 'unknown',
|
|
386
|
-
|
|
387
|
-
|
|
381
|
+
location: 'unknown',
|
|
382
|
+
pathArgs: [],
|
|
388
383
|
signals: ['unparseable_shell'],
|
|
384
|
+
context,
|
|
389
385
|
});
|
|
390
386
|
}
|
|
391
387
|
const substitutionResult = await evaluateSubstitutions(command, context, depth);
|
|
@@ -395,13 +391,15 @@ async function evaluateSegment(command, context, depth) {
|
|
|
395
391
|
const tokens = tokenizeShell(command);
|
|
396
392
|
const { tokens: peeled, xargsStdinOpaque } = peelTransparentWrappers(tokens);
|
|
397
393
|
if (xargsStdinOpaque || isBareInterpreter(tokens)) {
|
|
398
|
-
return
|
|
399
|
-
|
|
394
|
+
return verdictFromOpacityPolicy({
|
|
395
|
+
command,
|
|
396
|
+
segmentHead: parseSegment(command).head,
|
|
400
397
|
opacity: 'opaque',
|
|
401
398
|
effect: 'unknown',
|
|
402
|
-
|
|
403
|
-
|
|
399
|
+
location: 'unknown',
|
|
400
|
+
pathArgs: [],
|
|
404
401
|
signals: ['opaque_execution'],
|
|
402
|
+
context,
|
|
405
403
|
});
|
|
406
404
|
}
|
|
407
405
|
const segment = parseSegment(command);
|
|
@@ -430,13 +428,28 @@ async function evaluateSegment(command, context, depth) {
|
|
|
430
428
|
if (recursiveScript) {
|
|
431
429
|
const prescan = prescanInterpreterCode(recursiveScript);
|
|
432
430
|
if (prescan && tier1RequiresAsk(prescan)) {
|
|
431
|
+
const { request, decision } = evaluateSegmentShellPolicy({
|
|
432
|
+
command: recursiveScript,
|
|
433
|
+
segmentHead: segment.head,
|
|
434
|
+
effect: 'unknown',
|
|
435
|
+
location: 'unknown',
|
|
436
|
+
opacity: 'recursive',
|
|
437
|
+
pathArgs: [],
|
|
438
|
+
signals: ['interpreter_secret_prescan', prescan.reason],
|
|
439
|
+
context,
|
|
440
|
+
});
|
|
441
|
+
const legacyReason = policyDecisionRequiresAsk(decision)
|
|
442
|
+
? policyDecisionToLegacyReason(decision)
|
|
443
|
+
: 'interpreter_secret_prescan';
|
|
433
444
|
return askVerdict({
|
|
434
445
|
location: 'unknown',
|
|
435
446
|
opacity: 'recursive',
|
|
436
447
|
effect: 'unknown',
|
|
437
448
|
confidence: 'deterministic',
|
|
438
|
-
reason:
|
|
439
|
-
signals: ['interpreter_secret_prescan'],
|
|
449
|
+
reason: legacyReason,
|
|
450
|
+
signals: ['interpreter_secret_prescan', prescan.reason, ...decision.signals],
|
|
451
|
+
capabilityRequests: [request],
|
|
452
|
+
authorizationDecision: decision,
|
|
440
453
|
});
|
|
441
454
|
}
|
|
442
455
|
const innerVerdict = await evaluateSegment(recursiveScript, context, depth + 1);
|
|
@@ -481,8 +494,10 @@ async function evaluateSegment(command, context, depth) {
|
|
|
481
494
|
}
|
|
482
495
|
let innerVerdict = null;
|
|
483
496
|
for (const recipe of resolution.recipes) {
|
|
484
|
-
const
|
|
485
|
-
|
|
497
|
+
for (const sub of splitTopLevelSegments(recipe)) {
|
|
498
|
+
const evaluated = await evaluateSegment(sub, context, depth + 1);
|
|
499
|
+
innerVerdict = innerVerdict ? combineInternal(innerVerdict, evaluated) : evaluated;
|
|
500
|
+
}
|
|
486
501
|
}
|
|
487
502
|
if (!innerVerdict) {
|
|
488
503
|
return askVerdict({
|
|
@@ -500,38 +515,78 @@ async function evaluateSegment(command, context, depth) {
|
|
|
500
515
|
signals: [...innerVerdict.signals, resolution.reason],
|
|
501
516
|
};
|
|
502
517
|
}
|
|
503
|
-
const
|
|
518
|
+
const semantics = analyzeShellCommandSemantics(peeled, segment, context.cwd);
|
|
519
|
+
const pathContext = pathContextFromSemantics(context, semantics);
|
|
520
|
+
if (semantics.requiresAsk) {
|
|
521
|
+
const { request, decision } = evaluateSegmentShellPolicy({
|
|
522
|
+
command,
|
|
523
|
+
segmentHead: segment.head,
|
|
524
|
+
effect: 'local_mutation',
|
|
525
|
+
location: 'repo_local',
|
|
526
|
+
opacity: 'transparent',
|
|
527
|
+
pathArgs: semantics.pathTargets,
|
|
528
|
+
signals: semantics.requiresAsk.signals,
|
|
529
|
+
context: pathContext,
|
|
530
|
+
});
|
|
531
|
+
return askVerdict({
|
|
532
|
+
location: 'repo_local',
|
|
533
|
+
opacity: 'transparent',
|
|
534
|
+
effect: 'local_mutation',
|
|
535
|
+
confidence: 'deterministic',
|
|
536
|
+
reason: semantics.requiresAsk.reason,
|
|
537
|
+
signals: semantics.requiresAsk.signals,
|
|
538
|
+
capabilityRequests: [request],
|
|
539
|
+
authorizationDecision: decision,
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
const egressClass = semantics.egressClass ?? classifyEgressTool(segment.head, peeled);
|
|
504
543
|
if (egressClass === 'destructive') {
|
|
544
|
+
const { request, decision } = evaluateSegmentShellPolicy({
|
|
545
|
+
command,
|
|
546
|
+
segmentHead: segment.head,
|
|
547
|
+
effect: 'remote_mutation',
|
|
548
|
+
location: 'external',
|
|
549
|
+
opacity: 'transparent',
|
|
550
|
+
egressClass: 'destructive',
|
|
551
|
+
pathArgs: [],
|
|
552
|
+
signals: ['tier0_external', segment.head],
|
|
553
|
+
context,
|
|
554
|
+
});
|
|
505
555
|
return askVerdict({
|
|
506
556
|
location: 'external',
|
|
507
557
|
opacity: 'transparent',
|
|
508
558
|
effect: 'remote_mutation',
|
|
509
559
|
confidence: 'deterministic',
|
|
510
560
|
reason: 'tier0_external',
|
|
511
|
-
signals: ['tier0_external', segment.head],
|
|
561
|
+
signals: ['tier0_external', segment.head, ...decision.signals],
|
|
562
|
+
capabilityRequests: [request],
|
|
563
|
+
authorizationDecision: decision,
|
|
512
564
|
});
|
|
513
565
|
}
|
|
514
|
-
if (
|
|
515
|
-
|
|
566
|
+
if (tier0ExternalMatch(semantics.normalizedKey ?? segment.key, segment.head, peeled, semantics.signals)) {
|
|
567
|
+
const tier0Key = semantics.normalizedKey ?? segment.key;
|
|
568
|
+
const { request, decision } = evaluateSegmentShellPolicy({
|
|
569
|
+
command,
|
|
570
|
+
segmentHead: segment.head,
|
|
571
|
+
effect: 'remote_mutation',
|
|
516
572
|
location: 'external',
|
|
517
573
|
opacity: 'transparent',
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
signals: ['egress_read', segment.head],
|
|
574
|
+
pathArgs: [],
|
|
575
|
+
signals: ['tier0_external', tier0Key, ...semantics.signals],
|
|
576
|
+
context: pathContext,
|
|
522
577
|
});
|
|
523
|
-
}
|
|
524
|
-
if (tier0ExternalMatch(segment.key, segment.head, peeled)) {
|
|
525
578
|
return askVerdict({
|
|
526
579
|
location: 'external',
|
|
527
580
|
opacity: 'transparent',
|
|
528
581
|
effect: 'remote_mutation',
|
|
529
582
|
confidence: 'deterministic',
|
|
530
583
|
reason: 'tier0_external',
|
|
531
|
-
signals: ['tier0_external',
|
|
584
|
+
signals: ['tier0_external', tier0Key, ...decision.signals],
|
|
585
|
+
capabilityRequests: [request],
|
|
586
|
+
authorizationDecision: decision,
|
|
532
587
|
});
|
|
533
588
|
}
|
|
534
|
-
const rmVerdict = tier0HighStakesRm(peeled,
|
|
589
|
+
const rmVerdict = tier0HighStakesRm(peeled, pathContext);
|
|
535
590
|
if (rmVerdict) {
|
|
536
591
|
return rmVerdict;
|
|
537
592
|
}
|
|
@@ -545,29 +600,29 @@ async function evaluateSegment(command, context, depth) {
|
|
|
545
600
|
signals: ['belay_control_plane_command', segment.head],
|
|
546
601
|
});
|
|
547
602
|
}
|
|
548
|
-
let effect =
|
|
549
|
-
if (
|
|
603
|
+
let effect = semantics.effect;
|
|
604
|
+
if (isReadOnlyLauncherInvocation(peeled)) {
|
|
550
605
|
effect = 'read_only';
|
|
551
606
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
}
|
|
555
|
-
else if (LOCAL_ROUTINE_HEADS.has(segment.head)) {
|
|
556
|
-
effect = 'local_mutation';
|
|
557
|
-
}
|
|
558
|
-
const pathArgs = extractPathArgs(peeled);
|
|
559
|
-
if (extractRedirectTargets(peeled).length > 0 && effect === 'read_only') {
|
|
560
|
-
effect = 'local_mutation';
|
|
561
|
-
}
|
|
607
|
+
const pathArgs = semantics.pathTargets;
|
|
608
|
+
const policyPathArgs = [...new Set([...pathArgs, ...(semantics.scopeTargets ?? [])])];
|
|
562
609
|
const pathAnalysis = analyzePathTargets({
|
|
563
|
-
targets:
|
|
564
|
-
cwd:
|
|
565
|
-
repoRoot:
|
|
566
|
-
trustedCwd:
|
|
567
|
-
trustedWorkspaceRoots:
|
|
568
|
-
sensitivePaths:
|
|
569
|
-
protectedArtifactRoots:
|
|
610
|
+
targets: policyPathArgs,
|
|
611
|
+
cwd: pathContext.cwd,
|
|
612
|
+
repoRoot: pathContext.repoRoot,
|
|
613
|
+
trustedCwd: pathContext.trustedCwd,
|
|
614
|
+
trustedWorkspaceRoots: pathContext.trustedWorkspaceRoots,
|
|
615
|
+
sensitivePaths: pathContext.sensitivePaths,
|
|
616
|
+
protectedArtifactRoots: pathContext.protectedArtifactRoots,
|
|
570
617
|
});
|
|
618
|
+
// Path-less read/mutation in a trusted cwd: assume repo-local (e.g. git status, tsc --noEmit).
|
|
619
|
+
const effectiveLocation = policyPathArgs.length === 0 &&
|
|
620
|
+
pathContext.trustedCwd &&
|
|
621
|
+
pathContext.cwd &&
|
|
622
|
+
(effect === 'read_only' || effect === 'local_mutation') &&
|
|
623
|
+
pathAnalysis.location === 'unknown'
|
|
624
|
+
? 'repo_local'
|
|
625
|
+
: pathAnalysis.location;
|
|
571
626
|
if (!context.trustedCwd || !context.cwd) {
|
|
572
627
|
if (opacity === 'opaque' || effect === 'unknown' || effect === 'local_mutation') {
|
|
573
628
|
return askVerdict({
|
|
@@ -579,7 +634,7 @@ async function evaluateSegment(command, context, depth) {
|
|
|
579
634
|
signals: ['missing_trusted_cwd'],
|
|
580
635
|
});
|
|
581
636
|
}
|
|
582
|
-
if (effect === 'read_only' && !isPureReadOnlySegment(segment)) {
|
|
637
|
+
if (effect === 'read_only' && !isPureReadOnlySegment(segment, semantics)) {
|
|
583
638
|
return askVerdict({
|
|
584
639
|
location: 'unknown',
|
|
585
640
|
opacity,
|
|
@@ -594,9 +649,9 @@ async function evaluateSegment(command, context, depth) {
|
|
|
594
649
|
context.protectedArtifactRoots &&
|
|
595
650
|
context.protectedArtifactRoots.length > 0) {
|
|
596
651
|
for (const target of pathArgs) {
|
|
597
|
-
const resolved = resolveTrustedPath(target,
|
|
598
|
-
resolveMutationTarget(target,
|
|
599
|
-
if (resolved && touchesProtectedRoot(resolved,
|
|
652
|
+
const resolved = resolveTrustedPath(target, pathContext.cwd, pathContext.trustedCwd) ??
|
|
653
|
+
resolveMutationTarget(target, pathContext.cwd);
|
|
654
|
+
if (resolved && touchesProtectedRoot(resolved, pathContext.protectedArtifactRoots ?? [])) {
|
|
600
655
|
return askVerdict({
|
|
601
656
|
location: pathAnalysis.location,
|
|
602
657
|
opacity: 'transparent',
|
|
@@ -611,10 +666,10 @@ async function evaluateSegment(command, context, depth) {
|
|
|
611
666
|
if (effect === 'local_mutation' || effect === 'unknown') {
|
|
612
667
|
let destructiveHighStakes = false;
|
|
613
668
|
for (const target of pathArgs) {
|
|
614
|
-
const resolved = resolveTrustedPath(target,
|
|
615
|
-
resolveMutationTarget(target,
|
|
669
|
+
const resolved = resolveTrustedPath(target, pathContext.cwd, pathContext.trustedCwd) ??
|
|
670
|
+
resolveMutationTarget(target, pathContext.cwd);
|
|
616
671
|
if (resolved &&
|
|
617
|
-
isDestructiveHighStakesMutation(segment.head, resolved,
|
|
672
|
+
isDestructiveHighStakesMutation(segment.head, resolved, pathContext.repoRoot, pathContext.sensitivePaths, pathContext.protectedArtifactRoots, pathContext.trustedWorkspaceRoots)) {
|
|
618
673
|
destructiveHighStakes = true;
|
|
619
674
|
break;
|
|
620
675
|
}
|
|
@@ -663,18 +718,43 @@ async function evaluateSegment(command, context, depth) {
|
|
|
663
718
|
signals: ['unknown_location_mutation'],
|
|
664
719
|
});
|
|
665
720
|
}
|
|
721
|
+
const resolvedPathTargets = resolveShellPathTargets(policyPathArgs, pathContext);
|
|
722
|
+
const semanticSignals = semantics.signals;
|
|
723
|
+
const buildPolicyInput = (overrides) => ({
|
|
724
|
+
command: recursiveScript ?? command,
|
|
725
|
+
segmentHead: segment.head,
|
|
726
|
+
opacity,
|
|
727
|
+
egressClass: egressClass ?? undefined,
|
|
728
|
+
pathArgs: policyPathArgs,
|
|
729
|
+
resolvedPathTargets,
|
|
730
|
+
signals: [...semanticSignals, ...pathAnalysis.signals],
|
|
731
|
+
context: pathContext,
|
|
732
|
+
...overrides,
|
|
733
|
+
});
|
|
666
734
|
const outsideMutation = pathAnalysis.location === 'repo_outside' || pathAnalysis.location === 'mixed';
|
|
667
735
|
const mutationPrescan = (effect === 'local_mutation' || effect === 'unknown') && pathArgs.length > 0
|
|
668
736
|
? mutationPrescanRequiresAsk({
|
|
669
737
|
targets: pathArgs,
|
|
670
|
-
cwd:
|
|
671
|
-
repoRoot:
|
|
672
|
-
trustedCwd:
|
|
673
|
-
trustedWorkspaceRoots:
|
|
674
|
-
sensitivePaths:
|
|
738
|
+
cwd: pathContext.cwd,
|
|
739
|
+
repoRoot: pathContext.repoRoot,
|
|
740
|
+
trustedCwd: pathContext.trustedCwd,
|
|
741
|
+
trustedWorkspaceRoots: pathContext.trustedWorkspaceRoots,
|
|
742
|
+
sensitivePaths: pathContext.sensitivePaths,
|
|
675
743
|
})
|
|
676
744
|
: null;
|
|
677
745
|
if (mutationPrescan) {
|
|
746
|
+
const { request, decision } = evaluateSegmentShellPolicy(buildPolicyInput({
|
|
747
|
+
effect: 'local_mutation',
|
|
748
|
+
location: pathAnalysis.location === 'unknown'
|
|
749
|
+
? 'unknown'
|
|
750
|
+
: pathAnalysis.location === 'repo_outside' || pathAnalysis.location === 'mixed'
|
|
751
|
+
? pathAnalysis.location
|
|
752
|
+
: 'repo_local',
|
|
753
|
+
signals: ['tier1_catastrophic', mutationPrescan.reason, ...pathAnalysis.signals],
|
|
754
|
+
}));
|
|
755
|
+
const legacyReason = policyDecisionRequiresAsk(decision)
|
|
756
|
+
? policyDecisionToLegacyReason(decision, { outsideMutation, effect: 'local_mutation' })
|
|
757
|
+
: 'tier1_catastrophic';
|
|
678
758
|
return askVerdict({
|
|
679
759
|
location: pathAnalysis.location === 'unknown'
|
|
680
760
|
? 'unknown'
|
|
@@ -684,24 +764,28 @@ async function evaluateSegment(command, context, depth) {
|
|
|
684
764
|
opacity,
|
|
685
765
|
effect: 'local_mutation',
|
|
686
766
|
confidence: 'deterministic',
|
|
687
|
-
reason:
|
|
688
|
-
signals: ['tier1_catastrophic', mutationPrescan.reason],
|
|
767
|
+
reason: legacyReason,
|
|
768
|
+
signals: ['tier1_catastrophic', mutationPrescan.reason, ...decision.signals],
|
|
769
|
+
capabilityRequests: [request],
|
|
770
|
+
authorizationDecision: decision,
|
|
689
771
|
});
|
|
690
772
|
}
|
|
691
|
-
const
|
|
773
|
+
const needsPolicy = effect === 'unknown' ||
|
|
692
774
|
TIER0_EXTERNAL_HEADS.has(segment.head) ||
|
|
693
775
|
egressClass === 'ambiguous' ||
|
|
776
|
+
egressClass === 'read' ||
|
|
694
777
|
(outsideMutation && effect !== 'read_only');
|
|
695
|
-
let
|
|
696
|
-
if (
|
|
697
|
-
const
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
778
|
+
let policyMetadata = {};
|
|
779
|
+
if (needsPolicy) {
|
|
780
|
+
const { request, decision } = evaluateSegmentShellPolicy(buildPolicyInput({
|
|
781
|
+
effect,
|
|
782
|
+
location: pathAnalysis.location,
|
|
783
|
+
}));
|
|
784
|
+
policyMetadata = {
|
|
785
|
+
capabilityRequests: [request],
|
|
786
|
+
authorizationDecision: decision,
|
|
787
|
+
};
|
|
788
|
+
if (policyDecisionRequiresAsk(decision)) {
|
|
705
789
|
return askVerdict({
|
|
706
790
|
location: pathAnalysis.location === 'unknown'
|
|
707
791
|
? 'unknown'
|
|
@@ -709,72 +793,104 @@ async function evaluateSegment(command, context, depth) {
|
|
|
709
793
|
? pathAnalysis.location
|
|
710
794
|
: 'repo_local',
|
|
711
795
|
opacity,
|
|
712
|
-
effect:
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
796
|
+
effect: decision.reason === 'external_effect' || decision.reason === 'network_connect'
|
|
797
|
+
? 'remote_mutation'
|
|
798
|
+
: effect,
|
|
799
|
+
confidence: 'deterministic',
|
|
800
|
+
reason: policyDecisionToLegacyReason(decision, { outsideMutation, effect }),
|
|
801
|
+
signals: ['policy_required', ...decision.signals],
|
|
802
|
+
...policyMetadata,
|
|
717
803
|
});
|
|
718
804
|
}
|
|
719
805
|
}
|
|
720
806
|
if (outsideMutation && effect !== 'read_only') {
|
|
721
|
-
|
|
807
|
+
const policyAllowsOutside = policyMetadata.authorizationDecision?.outcome === 'allow' &&
|
|
808
|
+
(policyMetadata.authorizationDecision.matchedRule === 'grant.exact' ||
|
|
809
|
+
policyMetadata.authorizationDecision.matchedRule === 'boundary.verified' ||
|
|
810
|
+
policyMetadata.authorizationDecision.matchedRule === 'builtin.trusted_workspace');
|
|
811
|
+
if (!policyAllowsOutside) {
|
|
812
|
+
const legacyReason = policyMetadata.authorizationDecision &&
|
|
813
|
+
policyDecisionRequiresAsk(policyMetadata.authorizationDecision)
|
|
814
|
+
? policyDecisionToLegacyReason(policyMetadata.authorizationDecision, {
|
|
815
|
+
outsideMutation,
|
|
816
|
+
effect,
|
|
817
|
+
})
|
|
818
|
+
: 'outside_repo_mutation';
|
|
819
|
+
return askVerdict({
|
|
820
|
+
location: pathAnalysis.location,
|
|
821
|
+
opacity,
|
|
822
|
+
effect: 'local_mutation',
|
|
823
|
+
confidence: 'deterministic',
|
|
824
|
+
reason: legacyReason,
|
|
825
|
+
signals: ['outside_repo_mutation', ...pathAnalysis.signals],
|
|
826
|
+
...policyMetadata,
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
return allowVerdict({
|
|
722
830
|
location: pathAnalysis.location,
|
|
723
831
|
opacity,
|
|
724
832
|
effect: 'local_mutation',
|
|
725
|
-
confidence:
|
|
833
|
+
confidence: 'deterministic',
|
|
726
834
|
reason: 'repo_outside_local_mutation',
|
|
727
835
|
signals: ['repo_outside_local_mutation', ...pathAnalysis.signals],
|
|
728
|
-
|
|
836
|
+
...policyMetadata,
|
|
837
|
+
});
|
|
729
838
|
}
|
|
730
|
-
if (
|
|
839
|
+
if (effectiveLocation === 'repo_local' &&
|
|
731
840
|
(effect === 'read_only' || effect === 'local_mutation') &&
|
|
732
841
|
opacity !== 'opaque') {
|
|
733
|
-
return
|
|
842
|
+
return allowVerdict({
|
|
734
843
|
location: 'repo_local',
|
|
735
844
|
opacity,
|
|
736
845
|
effect,
|
|
737
846
|
confidence: 'assumed_repo_local',
|
|
738
847
|
reason: effect === 'read_only' ? 'read_only' : 'repo_local_mutation',
|
|
739
848
|
signals: effect === 'read_only' ? ['read_only'] : ['repo_local_mutation'],
|
|
740
|
-
|
|
849
|
+
...withShellPolicyMetadata(buildPolicyInput({ effect, location: 'repo_local' }), policyMetadata),
|
|
850
|
+
});
|
|
741
851
|
}
|
|
742
852
|
if (effect === 'read_only') {
|
|
743
|
-
const readOnlyLocation =
|
|
744
|
-
?
|
|
853
|
+
const readOnlyLocation = pathContext.trustedCwd && pathContext.cwd
|
|
854
|
+
? effectiveLocation === 'unknown'
|
|
745
855
|
? 'repo_local'
|
|
746
|
-
:
|
|
856
|
+
: effectiveLocation
|
|
747
857
|
: 'unknown';
|
|
748
|
-
return
|
|
858
|
+
return allowVerdict({
|
|
749
859
|
location: readOnlyLocation,
|
|
750
860
|
opacity,
|
|
751
861
|
effect: 'read_only',
|
|
752
862
|
confidence: context.trustedCwd && context.cwd ? 'assumed_repo_local' : 'deterministic',
|
|
753
863
|
reason: 'read_only',
|
|
754
864
|
signals: ['read_only'],
|
|
755
|
-
|
|
865
|
+
...withShellPolicyMetadata(buildPolicyInput({ effect: 'read_only', location: readOnlyLocation }), policyMetadata),
|
|
866
|
+
});
|
|
756
867
|
}
|
|
757
868
|
if (allowOverride) {
|
|
758
|
-
return
|
|
869
|
+
return allowFromCustomOverride(opacity);
|
|
759
870
|
}
|
|
760
871
|
if (context.unknownLocalEffect === 'allow_flagged') {
|
|
761
|
-
return
|
|
762
|
-
location:
|
|
872
|
+
return allowVerdict({
|
|
873
|
+
location: effectiveLocation === 'unknown' ? 'repo_local' : effectiveLocation,
|
|
763
874
|
opacity,
|
|
764
875
|
effect: 'unknown',
|
|
765
876
|
confidence: 'assumed_repo_local',
|
|
766
877
|
reason: 'unknown_local_effect',
|
|
767
878
|
signals: ['unknown_local_effect'],
|
|
768
|
-
|
|
879
|
+
...withShellPolicyMetadata(buildPolicyInput({
|
|
880
|
+
effect: 'unknown',
|
|
881
|
+
location: effectiveLocation === 'unknown' ? 'repo_local' : effectiveLocation,
|
|
882
|
+
}), policyMetadata),
|
|
883
|
+
});
|
|
769
884
|
}
|
|
770
|
-
return
|
|
771
|
-
location:
|
|
885
|
+
return askVerdict({
|
|
886
|
+
location: effectiveLocation,
|
|
772
887
|
opacity,
|
|
773
888
|
effect,
|
|
774
889
|
confidence: 'deterministic',
|
|
775
890
|
reason: 'unknown_local_effect',
|
|
776
891
|
signals: ['unknown_local_effect'],
|
|
777
|
-
|
|
892
|
+
...withShellPolicyMetadata(buildPolicyInput({ effect, location: pathAnalysis.location }), policyMetadata),
|
|
893
|
+
});
|
|
778
894
|
}
|
|
779
895
|
function toVerdictResult(internal, command, context, fingerprintCwd = context.cwd) {
|
|
780
896
|
const commandRedacted = redactCommand(command);
|
|
@@ -790,6 +906,8 @@ function toVerdictResult(internal, command, context, fingerprintCwd = context.cw
|
|
|
790
906
|
fingerprint: verdictFingerprint(relative, commandRedacted),
|
|
791
907
|
signals: internal.signals,
|
|
792
908
|
judgeTrace: internal.judgeTrace,
|
|
909
|
+
capabilityRequests: internal.capabilityRequests,
|
|
910
|
+
authorizationDecision: internal.authorizationDecision,
|
|
793
911
|
};
|
|
794
912
|
}
|
|
795
913
|
export async function verdict(command, context) {
|