@guilz-dev/belay 0.4.0 → 0.7.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 +27 -2
- package/dist/adapters/claude/runtime-entry.js +54 -25
- package/dist/adapters/layouts/scope.js +3 -2
- package/dist/adapters/shared/gate-runtime.d.ts +8 -2
- package/dist/adapters/shared/gate-runtime.js +579 -181
- package/dist/bundle/claude-runtime.mjs +13574 -6334
- package/dist/bundle/codex-runtime.mjs +13108 -5903
- package/dist/bundle/cursor-runtime.mjs +12676 -5476
- package/dist/cli.js +80 -2
- package/dist/commands/approve.js +34 -10
- 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 +87 -1
- package/dist/commands/explain.js +11 -1
- package/dist/commands/judge.js +3 -2
- package/dist/commands/recovery-checkpoints.d.ts +164 -0
- package/dist/commands/recovery-checkpoints.js +347 -0
- package/dist/commands/revoke.js +19 -7
- package/dist/commands/session.d.ts +20 -0
- package/dist/commands/session.js +51 -0
- package/dist/commands/status.js +7 -0
- package/dist/config-io.js +91 -23
- 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 +20 -9
- package/dist/conformance/types.d.ts +1 -0
- package/dist/core/approval-replay.d.ts +1 -1
- package/dist/core/approval-replay.js +4 -1
- package/dist/core/approval-service.d.ts +6 -1
- package/dist/core/approval-service.js +87 -24
- package/dist/core/approval.d.ts +4 -0
- package/dist/core/approval.js +19 -5
- package/dist/core/audit-types.d.ts +13 -0
- package/dist/core/capability/approval-state-mutation.d.ts +32 -0
- package/dist/core/capability/approval-state-mutation.js +165 -0
- package/dist/core/capability/approval-v3.d.ts +32 -0
- package/dist/core/capability/approval-v3.js +114 -0
- package/dist/core/capability/attestation.d.ts +16 -0
- package/dist/core/capability/attestation.js +62 -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 +19 -0
- package/dist/core/capability/boundary-driver-container.js +189 -0
- package/dist/core/capability/boundary-driver.d.ts +29 -0
- package/dist/core/capability/boundary-driver.js +44 -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 +14 -0
- package/dist/core/capability/boundary-grant-materialize.js +93 -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 +48 -0
- package/dist/core/capability/boundary-run.js +51 -0
- package/dist/core/capability/boundary-session.d.ts +61 -0
- package/dist/core/capability/boundary-session.js +138 -0
- package/dist/core/capability/boundary-workspace-mount.d.ts +5 -0
- package/dist/core/capability/boundary-workspace-mount.js +68 -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 +130 -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-consumption.d.ts +4 -0
- package/dist/core/capability/grant-consumption.js +11 -0
- package/dist/core/capability/grant-lease.d.ts +44 -0
- package/dist/core/capability/grant-lease.js +243 -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 +96 -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 +77 -0
- package/dist/core/capability/policy-engine.js +663 -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 +48 -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 +34 -3
- package/dist/core/config.js +118 -3
- package/dist/core/effect-ir/audit.d.ts +9 -0
- package/dist/core/effect-ir/audit.js +86 -0
- package/dist/core/effect-ir/build.d.ts +31 -0
- package/dist/core/effect-ir/build.js +277 -0
- package/dist/core/effect-ir/index.d.ts +8 -0
- package/dist/core/effect-ir/index.js +7 -0
- package/dist/core/effect-ir/normalize.d.ts +4 -0
- package/dist/core/effect-ir/normalize.js +104 -0
- package/dist/core/effect-ir/package-exec-eval.d.ts +8 -0
- package/dist/core/effect-ir/package-exec-eval.js +152 -0
- package/dist/core/effect-ir/package-exec.d.ts +29 -0
- package/dist/core/effect-ir/package-exec.js +292 -0
- package/dist/core/effect-ir/policy.d.ts +13 -0
- package/dist/core/effect-ir/policy.js +77 -0
- package/dist/core/effect-ir/types.d.ts +55 -0
- package/dist/core/effect-ir/types.js +1 -0
- package/dist/core/egress/allowlist.d.ts +1 -0
- package/dist/core/egress/allowlist.js +8 -0
- package/dist/core/egress-approval.js +45 -51
- package/dist/core/gate-contract.d.ts +5 -0
- package/dist/core/gate-contract.js +5 -0
- package/dist/core/gate-engine.js +30 -8
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.js +2 -1
- package/dist/core/judge-cloud-consent.js +29 -15
- 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/network-endpoint.d.ts +10 -0
- package/dist/core/network-endpoint.js +51 -0
- package/dist/core/path-utils.d.ts +1 -0
- package/dist/core/path-utils.js +5 -2
- package/dist/core/process-runner.d.ts +10 -0
- package/dist/core/process-runner.js +124 -0
- package/dist/core/recover-advice.js +2 -1
- package/dist/core/recovery/artifact-store.d.ts +29 -0
- package/dist/core/recovery/artifact-store.js +336 -0
- package/dist/core/recovery/capability.d.ts +3 -0
- package/dist/core/recovery/capability.js +22 -0
- package/dist/core/recovery/checkpoint.d.ts +25 -0
- package/dist/core/recovery/checkpoint.js +260 -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 +5 -0
- package/dist/core/recovery/index.js +4 -0
- package/dist/core/recovery/operator-guidance.d.ts +7 -0
- package/dist/core/recovery/operator-guidance.js +39 -0
- package/dist/core/recovery/reconcile.d.ts +3 -0
- package/dist/core/recovery/reconcile.js +48 -0
- package/dist/core/recovery/resource-identity.d.ts +4 -0
- package/dist/core/recovery/resource-identity.js +34 -0
- package/dist/core/recovery/restore.d.ts +13 -0
- package/dist/core/recovery/restore.js +114 -0
- package/dist/core/recovery/snapshot-node.d.ts +27 -0
- package/dist/core/recovery/snapshot-node.js +261 -0
- package/dist/core/recovery/types.d.ts +104 -0
- package/dist/core/recovery/types.js +1 -0
- package/dist/core/standing-allow.js +3 -0
- package/dist/core/transactional/apply-observed-changes.d.ts +13 -0
- package/dist/core/transactional/apply-observed-changes.js +275 -0
- package/dist/core/transactional/backend-selector.d.ts +8 -0
- package/dist/core/transactional/backend-selector.js +118 -0
- package/dist/core/transactional/backend.d.ts +55 -0
- package/dist/core/transactional/backend.js +1 -0
- package/dist/core/transactional/diff-evaluator.js +4 -1
- package/dist/core/transactional/eligibility.js +4 -0
- package/dist/core/transactional/file-checkpoint-backend.d.ts +3 -0
- package/dist/core/transactional/file-checkpoint-backend.js +278 -0
- package/dist/core/transactional/file-checkpoint-git.d.ts +11 -0
- package/dist/core/transactional/file-checkpoint-git.js +217 -0
- package/dist/core/transactional/file-checkpoint-staging.d.ts +13 -0
- package/dist/core/transactional/file-checkpoint-staging.js +54 -0
- package/dist/core/transactional/file-clone.d.ts +15 -0
- package/dist/core/transactional/file-clone.js +139 -0
- package/dist/core/transactional/file-tree-path.d.ts +7 -0
- package/dist/core/transactional/file-tree-path.js +50 -0
- package/dist/core/transactional/file-tree.d.ts +41 -0
- package/dist/core/transactional/file-tree.js +186 -0
- package/dist/core/transactional/git-worktree-backend.d.ts +2 -0
- package/dist/core/transactional/git-worktree-backend.js +50 -0
- package/dist/core/transactional/git-worktree.d.ts +14 -9
- package/dist/core/transactional/git-worktree.js +62 -87
- package/dist/core/transactional/index.d.ts +11 -0
- package/dist/core/transactional/index.js +10 -0
- package/dist/core/transactional/reasons.d.ts +1 -0
- package/dist/core/transactional/reasons.js +5 -0
- package/dist/core/transactional/runner.js +171 -23
- package/dist/core/transactional/snapshot-node.d.ts +26 -0
- package/dist/core/transactional/snapshot-node.js +87 -0
- package/dist/core/transactional/types.d.ts +21 -1
- package/dist/core/types.d.ts +30 -3
- package/dist/core/verdict/adapter.d.ts +3 -4
- package/dist/core/verdict/adapter.js +24 -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 +14 -16
- 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 +26 -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 +18 -1
- package/dist/core/verdict/verdict.js +313 -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 +13 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/belay/SKILL.md +8 -3
- package/skills/belay/belay-approve.md +10 -7
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export declare const CAPABILITY_REQUEST_VERSION: 1;
|
|
2
|
+
export type CapabilityAction = 'fs.read' | 'fs.write' | 'process.exec' | 'network.connect' | 'secret.read' | 'git.ref.write' | 'control_plane.write' | 'indeterminate';
|
|
3
|
+
export type CapabilityEvidenceLevel = 'certain' | 'possible' | 'indeterminate';
|
|
4
|
+
export type CapabilityHookKind = 'shell' | 'tool' | 'subagent';
|
|
5
|
+
export interface CapabilityPrincipal {
|
|
6
|
+
adapter?: string;
|
|
7
|
+
repoRoot: string;
|
|
8
|
+
sessionHash?: string;
|
|
9
|
+
}
|
|
10
|
+
export type CapabilityResource = {
|
|
11
|
+
kind: 'path';
|
|
12
|
+
path: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: 'network';
|
|
15
|
+
host: string;
|
|
16
|
+
port?: number;
|
|
17
|
+
protocol?: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: 'executable';
|
|
20
|
+
command: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'package-cache';
|
|
23
|
+
manager: 'npm' | 'pnpm';
|
|
24
|
+
} | {
|
|
25
|
+
kind: 'git-ref';
|
|
26
|
+
ref: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: 'unknown';
|
|
29
|
+
};
|
|
30
|
+
export interface CapabilityRequestContext {
|
|
31
|
+
cwd: string;
|
|
32
|
+
inputFingerprint: string;
|
|
33
|
+
hookKind: CapabilityHookKind;
|
|
34
|
+
analysisBasis: string[];
|
|
35
|
+
boundaryProfile?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface CapabilityEvidence {
|
|
38
|
+
level: CapabilityEvidenceLevel;
|
|
39
|
+
signals: string[];
|
|
40
|
+
}
|
|
41
|
+
export interface CapabilityRequestV1 {
|
|
42
|
+
version: typeof CAPABILITY_REQUEST_VERSION;
|
|
43
|
+
principal: CapabilityPrincipal;
|
|
44
|
+
action: CapabilityAction;
|
|
45
|
+
resource: CapabilityResource;
|
|
46
|
+
context: CapabilityRequestContext;
|
|
47
|
+
evidence: CapabilityEvidence;
|
|
48
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const CAPABILITY_REQUEST_VERSION = 1;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { evaluateSegmentShellPolicy, type SegmentShellPolicyInput, } from '../verdict/shell-policy.js';
|
|
2
|
+
export { BOUNDARY_PROFILE_L3_POLICY, checkGatedActionLimits } from './limits.js';
|
|
3
|
+
export { type CapabilityAuthorizationMetadata, policyReasonToLegacyReason, singleCapabilityMetadata, } from './policy-bridge.js';
|
|
4
|
+
export { buildFileMutationCapabilityRequest, buildShellCapabilityRequest, buildSubagentCapabilityRequest, evaluateFileMutationPolicy, evaluateShellPolicy, evaluateSubagentPolicy, type FileMutationCapabilityAnalysis, policyDecisionRequiresAsk, type ShellCapabilityAnalysis, type SubagentCapabilityAnalysis, } from './policy-engine.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { evaluateSegmentShellPolicy, } from '../verdict/shell-policy.js';
|
|
2
|
+
export { BOUNDARY_PROFILE_L3_POLICY, checkGatedActionLimits } from './limits.js';
|
|
3
|
+
export { policyReasonToLegacyReason, singleCapabilityMetadata, } from './policy-bridge.js';
|
|
4
|
+
export { buildFileMutationCapabilityRequest, buildShellCapabilityRequest, buildSubagentCapabilityRequest, evaluateFileMutationPolicy, evaluateShellPolicy, evaluateSubagentPolicy, policyDecisionRequiresAsk, } from './policy-engine.js';
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
import type { BelayConfigV3 } from './config.js';
|
|
1
2
|
import type { ClassifierOptions, ClassifyResult } from './types.js';
|
|
2
|
-
export declare function classifySubagent(payload: Record<string, unknown>, repoRoot: string, options
|
|
3
|
+
export declare function classifySubagent(payload: Record<string, unknown>, repoRoot: string, options: ClassifierOptions | undefined, config: BelayConfigV3): ClassifyResult;
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { BOUNDARY_PROFILE_L3_L4_ONLY } from './capability/boundary-profile.js';
|
|
2
|
+
import { policyReasonToLegacyReason } from './capability/policy-bridge.js';
|
|
3
|
+
import { evaluateSubagentPolicy, policyDecisionRequiresAsk } from './capability/policy-engine.js';
|
|
1
4
|
import { canonicalStringify, subagentFingerprint } from './fingerprint.js';
|
|
2
5
|
import { subagentFingerprintSource } from './replay-scrub.js';
|
|
3
6
|
import { scrubValue } from './scrub.js';
|
|
@@ -25,7 +28,7 @@ function extractSubagentText(payload, options) {
|
|
|
25
28
|
function fingerprintSource(payload, options) {
|
|
26
29
|
return subagentFingerprintSource(payload, options.scrubOptions ?? {});
|
|
27
30
|
}
|
|
28
|
-
export function classifySubagent(payload, repoRoot, options = {}) {
|
|
31
|
+
export function classifySubagent(payload, repoRoot, options = {}, config) {
|
|
29
32
|
const kind = payload.tool_name === 'Task' ? 'Task' : String(payload.subagent_type ?? 'generalPurpose');
|
|
30
33
|
const scrubbed = fingerprintSource(payload, options);
|
|
31
34
|
const summary = extractSubagentText(payload, options);
|
|
@@ -35,6 +38,36 @@ export function classifySubagent(payload, repoRoot, options = {}) {
|
|
|
35
38
|
const pattern = new RegExp(`\\b${term}\\b`, 'i');
|
|
36
39
|
return pattern.test(lowered);
|
|
37
40
|
});
|
|
41
|
+
const signals = hasExternalTerm ? ['subagent_external_intent_hint'] : ['subagent_default_review'];
|
|
42
|
+
const { request, decision } = evaluateSubagentPolicy({
|
|
43
|
+
subagentType: kind,
|
|
44
|
+
summary,
|
|
45
|
+
repoRoot,
|
|
46
|
+
cwd: repoRoot,
|
|
47
|
+
inputFingerprint: fingerprint,
|
|
48
|
+
signals,
|
|
49
|
+
}, config, {
|
|
50
|
+
grants: options.grants,
|
|
51
|
+
attestation: options.attestation,
|
|
52
|
+
egressProxyActive: options.egressProxyActive,
|
|
53
|
+
});
|
|
54
|
+
if (policyDecisionRequiresAsk(decision)) {
|
|
55
|
+
return {
|
|
56
|
+
verdict: 'deny_pending_approval',
|
|
57
|
+
reason: policyReasonToLegacyReason(decision),
|
|
58
|
+
summary,
|
|
59
|
+
fingerprint,
|
|
60
|
+
assessment: {
|
|
61
|
+
reversibility: 'recoverable_with_cost',
|
|
62
|
+
external: hasExternalTerm,
|
|
63
|
+
blastRadius: 'subagent task scope',
|
|
64
|
+
confidence: hasExternalTerm ? 0.7 : 0.67,
|
|
65
|
+
signals: [...signals, ...decision.signals],
|
|
66
|
+
},
|
|
67
|
+
capabilityRequests: [request],
|
|
68
|
+
authorizationDecision: decision,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
38
71
|
return {
|
|
39
72
|
verdict: 'allow_flagged',
|
|
40
73
|
reason: 'subagent_review',
|
|
@@ -45,7 +78,10 @@ export function classifySubagent(payload, repoRoot, options = {}) {
|
|
|
45
78
|
external: false,
|
|
46
79
|
blastRadius: 'subagent task scope',
|
|
47
80
|
confidence: hasExternalTerm ? 0.7 : 0.67,
|
|
48
|
-
signals
|
|
81
|
+
signals,
|
|
49
82
|
},
|
|
83
|
+
capabilityRequests: [request],
|
|
84
|
+
authorizationDecision: decision,
|
|
85
|
+
boundaryProfile: options.boundaryProfile ?? BOUNDARY_PROFILE_L3_L4_ONLY,
|
|
50
86
|
};
|
|
51
87
|
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import { BOUNDARY_PROFILE_L3_L4_ONLY } from './capability/boundary-profile.js';
|
|
3
|
+
import { policyReasonToLegacyReason } from './capability/policy-bridge.js';
|
|
4
|
+
import { evaluateFileMutationPolicy, policyDecisionRequiresAsk, } from './capability/policy-engine.js';
|
|
2
5
|
import { canonicalStringify, toolFingerprint } from './fingerprint.js';
|
|
3
6
|
import { matchesSensitivePath } from './glob.js';
|
|
4
7
|
import { pathWithinRoot, resolveWorkspaceRootMatch } from './path-utils.js';
|
|
5
8
|
import { scrubValue } from './scrub.js';
|
|
6
9
|
import { classifyShell, resolveClassifierTrustedCwd } from './verdict/adapter.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
10
|
+
import { isGitPath } from './verdict/containment.js';
|
|
11
|
+
import { mutationPrescanRequiresAsk } from './verdict/prescan.js';
|
|
9
12
|
const DEFAULT_SENSITIVE_PATHS = ['.env', '.env.*', '**/credentials/**'];
|
|
10
13
|
const FILE_WRITE_TOOL_NAMES = new Set(['write']);
|
|
11
14
|
const FILE_EDIT_TOOL_NAMES = new Set([
|
|
@@ -18,6 +21,19 @@ const FILE_EDIT_TOOL_NAMES = new Set([
|
|
|
18
21
|
]);
|
|
19
22
|
const FILE_DELETE_TOOL_NAMES = new Set(['delete']);
|
|
20
23
|
const APPLY_PATCH_TOOL_NAMES = new Set(['apply_patch', 'applypatch']);
|
|
24
|
+
function policyAuth(options) {
|
|
25
|
+
if (!options.grants &&
|
|
26
|
+
options.attestation === undefined &&
|
|
27
|
+
options.egressProxyActive === undefined) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
grants: options.grants,
|
|
32
|
+
attestation: options.attestation,
|
|
33
|
+
egressProxyActive: options.egressProxyActive,
|
|
34
|
+
sensitivePaths: options.sensitivePaths,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
21
37
|
function scrubPayload(value, options) {
|
|
22
38
|
return scrubValue(value, options.scrubOptions);
|
|
23
39
|
}
|
|
@@ -76,11 +92,7 @@ function applyPatchTargets(patch) {
|
|
|
76
92
|
function normalizedToolName(toolName) {
|
|
77
93
|
return toolName.trim().toLowerCase();
|
|
78
94
|
}
|
|
79
|
-
function
|
|
80
|
-
return options.tier1Judge ?? createJudgeFromConfig(config, { repoRoot });
|
|
81
|
-
}
|
|
82
|
-
async function classifyFileMutationWithTier1(params) {
|
|
83
|
-
const judge = resolveFileTier1Judge(params.config, params.options, params.repoRoot);
|
|
95
|
+
function classifyFileMutationWithPolicy(params) {
|
|
84
96
|
const trustedCwd = resolveClassifierTrustedCwd(params.cwd, params.options);
|
|
85
97
|
const prescan = mutationPrescanRequiresAsk({
|
|
86
98
|
targets: [params.filePath],
|
|
@@ -91,11 +103,29 @@ async function classifyFileMutationWithTier1(params) {
|
|
|
91
103
|
sensitivePaths: params.options.sensitivePaths ?? params.config.classifier.sensitivePaths,
|
|
92
104
|
});
|
|
93
105
|
if (prescan) {
|
|
106
|
+
const fingerprint = toolFingerprint(params.toolName, { path: params.filePath }, params.repoRoot);
|
|
107
|
+
const { request, decision } = evaluateFileMutationPolicy({
|
|
108
|
+
hookKind: 'tool',
|
|
109
|
+
toolKind: params.toolKind,
|
|
110
|
+
filePath: params.filePath,
|
|
111
|
+
resolvedPath: params.resolvedPath,
|
|
112
|
+
repoRoot: params.repoRoot,
|
|
113
|
+
cwd: params.cwd,
|
|
114
|
+
inputFingerprint: fingerprint,
|
|
115
|
+
signals: [...params.signals, 'tier1_catastrophic', prescan.reason],
|
|
116
|
+
isDelete: params.isDelete,
|
|
117
|
+
locationLabel: params.locationLabel,
|
|
118
|
+
trustedWorkspaceRoots: params.options.trustedWorkspaceRoots,
|
|
119
|
+
sensitivePaths: params.options.sensitivePaths ?? params.config.classifier.sensitivePaths,
|
|
120
|
+
}, params.config, policyAuth(params.options));
|
|
121
|
+
const legacyReason = policyDecisionRequiresAsk(decision)
|
|
122
|
+
? policyReasonToLegacyReason(decision)
|
|
123
|
+
: 'tier1_catastrophic';
|
|
94
124
|
return {
|
|
95
125
|
verdict: 'deny_pending_approval',
|
|
96
|
-
reason:
|
|
126
|
+
reason: legacyReason,
|
|
97
127
|
summary: params.filePath,
|
|
98
|
-
fingerprint
|
|
128
|
+
fingerprint,
|
|
99
129
|
assessment: {
|
|
100
130
|
reversibility: 'irreversible',
|
|
101
131
|
external: params.locationLabel === 'outside_repo',
|
|
@@ -103,20 +133,32 @@ async function classifyFileMutationWithTier1(params) {
|
|
|
103
133
|
? 'outside the repository'
|
|
104
134
|
: 'sensitive repository file',
|
|
105
135
|
confidence: 0.95,
|
|
106
|
-
signals: [...params.signals, 'tier1_catastrophic', prescan.reason],
|
|
136
|
+
signals: [...params.signals, 'tier1_catastrophic', prescan.reason, ...decision.signals],
|
|
107
137
|
},
|
|
138
|
+
capabilityRequests: [request],
|
|
139
|
+
authorizationDecision: decision,
|
|
140
|
+
boundaryProfile: params.options.boundaryProfile ?? BOUNDARY_PROFILE_L3_L4_ONLY,
|
|
108
141
|
};
|
|
109
142
|
}
|
|
110
|
-
const tier1Text = `file_mutation: ${params.toolKind} path=${params.resolvedPath} location=${params.locationLabel}`;
|
|
111
|
-
const tier1 = await judge.evaluate({
|
|
112
|
-
text: tier1Text,
|
|
113
|
-
context: { cwd: params.cwd, repoRoot: params.repoRoot },
|
|
114
|
-
});
|
|
115
143
|
const fingerprint = toolFingerprint(params.toolName, { path: params.filePath }, params.repoRoot);
|
|
116
|
-
|
|
144
|
+
const { request, decision } = evaluateFileMutationPolicy({
|
|
145
|
+
hookKind: 'tool',
|
|
146
|
+
toolKind: params.toolKind,
|
|
147
|
+
filePath: params.filePath,
|
|
148
|
+
resolvedPath: params.resolvedPath,
|
|
149
|
+
repoRoot: params.repoRoot,
|
|
150
|
+
cwd: params.cwd,
|
|
151
|
+
inputFingerprint: fingerprint,
|
|
152
|
+
signals: params.signals,
|
|
153
|
+
isDelete: params.isDelete,
|
|
154
|
+
locationLabel: params.locationLabel,
|
|
155
|
+
trustedWorkspaceRoots: params.options.trustedWorkspaceRoots,
|
|
156
|
+
sensitivePaths: params.options.sensitivePaths ?? params.config.classifier.sensitivePaths,
|
|
157
|
+
}, params.config, policyAuth(params.options));
|
|
158
|
+
if (policyDecisionRequiresAsk(decision)) {
|
|
117
159
|
return {
|
|
118
160
|
verdict: 'deny_pending_approval',
|
|
119
|
-
reason:
|
|
161
|
+
reason: policyReasonToLegacyReason(decision),
|
|
120
162
|
summary: params.filePath,
|
|
121
163
|
fingerprint,
|
|
122
164
|
assessment: {
|
|
@@ -125,9 +167,12 @@ async function classifyFileMutationWithTier1(params) {
|
|
|
125
167
|
blastRadius: params.locationLabel === 'outside_repo'
|
|
126
168
|
? 'outside the repository'
|
|
127
169
|
: 'sensitive repository file',
|
|
128
|
-
confidence: 0.
|
|
129
|
-
signals: [...params.signals,
|
|
170
|
+
confidence: 0.95,
|
|
171
|
+
signals: [...params.signals, ...decision.signals],
|
|
130
172
|
},
|
|
173
|
+
capabilityRequests: [request],
|
|
174
|
+
authorizationDecision: decision,
|
|
175
|
+
boundaryProfile: params.options.boundaryProfile ?? BOUNDARY_PROFILE_L3_L4_ONLY,
|
|
131
176
|
};
|
|
132
177
|
}
|
|
133
178
|
const reason = params.isDelete ? 'file_delete' : 'file_mutation';
|
|
@@ -141,8 +186,11 @@ async function classifyFileMutationWithTier1(params) {
|
|
|
141
186
|
external: params.locationLabel === 'outside_repo',
|
|
142
187
|
blastRadius: params.locationLabel === 'outside_repo' ? 'outside the repository' : 'this repository',
|
|
143
188
|
confidence: 0.72,
|
|
144
|
-
signals: [...params.signals,
|
|
189
|
+
signals: [...params.signals, ...decision.signals],
|
|
145
190
|
},
|
|
191
|
+
capabilityRequests: [request],
|
|
192
|
+
authorizationDecision: decision,
|
|
193
|
+
boundaryProfile: params.options.boundaryProfile ?? BOUNDARY_PROFILE_L3_L4_ONLY,
|
|
146
194
|
};
|
|
147
195
|
}
|
|
148
196
|
export async function classifyToolUse(payload, repoRoot, cwd, config, options = {}) {
|
|
@@ -230,24 +278,24 @@ export async function classifyToolUse(payload, repoRoot, cwd, config, options =
|
|
|
230
278
|
const hitsProtectedRoot = protectedRoots.some((root) => pathWithinRoot(root, resolvedPath));
|
|
231
279
|
if (hitsProtectedRoot) {
|
|
232
280
|
signals.push('control_plane_path');
|
|
233
|
-
return {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
};
|
|
281
|
+
return classifyFileMutationWithPolicy({
|
|
282
|
+
toolName,
|
|
283
|
+
toolKind,
|
|
284
|
+
filePath,
|
|
285
|
+
resolvedPath,
|
|
286
|
+
repoRoot,
|
|
287
|
+
cwd,
|
|
288
|
+
config,
|
|
289
|
+
options,
|
|
290
|
+
signals,
|
|
291
|
+
isDelete: FILE_DELETE_TOOL_NAMES.has(toolKind),
|
|
292
|
+
locationLabel: 'control_plane',
|
|
293
|
+
});
|
|
246
294
|
}
|
|
247
295
|
const workspaceMatch = resolveWorkspaceRootMatch(repoRoot, options.trustedWorkspaceRoots, resolvedPath);
|
|
248
296
|
if (workspaceMatch === null) {
|
|
249
297
|
signals.push('outside_repo_path');
|
|
250
|
-
return
|
|
298
|
+
return classifyFileMutationWithPolicy({
|
|
251
299
|
toolName,
|
|
252
300
|
toolKind,
|
|
253
301
|
filePath,
|
|
@@ -274,7 +322,7 @@ export async function classifyToolUse(payload, repoRoot, cwd, config, options =
|
|
|
274
322
|
sensitivePaths,
|
|
275
323
|
});
|
|
276
324
|
if (workspacePrescan) {
|
|
277
|
-
return
|
|
325
|
+
return classifyFileMutationWithPolicy({
|
|
278
326
|
toolName,
|
|
279
327
|
toolKind,
|
|
280
328
|
filePath,
|
|
@@ -291,7 +339,7 @@ export async function classifyToolUse(payload, repoRoot, cwd, config, options =
|
|
|
291
339
|
const relativePath = workspaceMatch.relativePath;
|
|
292
340
|
if (matchesSensitivePath(relativePath, sensitivePaths)) {
|
|
293
341
|
signals.push('sensitive_path');
|
|
294
|
-
return
|
|
342
|
+
return classifyFileMutationWithPolicy({
|
|
295
343
|
toolName,
|
|
296
344
|
toolKind,
|
|
297
345
|
filePath,
|
|
@@ -306,35 +354,51 @@ export async function classifyToolUse(payload, repoRoot, cwd, config, options =
|
|
|
306
354
|
});
|
|
307
355
|
}
|
|
308
356
|
if (FILE_DELETE_TOOL_NAMES.has(toolKind)) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
357
|
+
if (isGitPath(resolvedPath, repoRoot)) {
|
|
358
|
+
signals.push('protected_artifact');
|
|
359
|
+
return classifyFileMutationWithPolicy({
|
|
360
|
+
toolName,
|
|
361
|
+
toolKind,
|
|
362
|
+
filePath,
|
|
363
|
+
resolvedPath,
|
|
364
|
+
repoRoot,
|
|
365
|
+
cwd,
|
|
366
|
+
config,
|
|
367
|
+
options,
|
|
320
368
|
signals,
|
|
321
|
-
|
|
322
|
-
|
|
369
|
+
isDelete: true,
|
|
370
|
+
locationLabel: 'sensitive_path',
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
signals.push('file_delete');
|
|
374
|
+
return classifyFileMutationWithPolicy({
|
|
375
|
+
toolName,
|
|
376
|
+
toolKind,
|
|
377
|
+
filePath,
|
|
378
|
+
resolvedPath,
|
|
379
|
+
repoRoot,
|
|
380
|
+
cwd,
|
|
381
|
+
config,
|
|
382
|
+
options,
|
|
383
|
+
signals,
|
|
384
|
+
isDelete: true,
|
|
385
|
+
locationLabel: 'repo_local',
|
|
386
|
+
});
|
|
323
387
|
}
|
|
324
388
|
signals.push('file_mutation');
|
|
325
|
-
return {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
};
|
|
389
|
+
return classifyFileMutationWithPolicy({
|
|
390
|
+
toolName,
|
|
391
|
+
toolKind,
|
|
392
|
+
filePath,
|
|
393
|
+
resolvedPath,
|
|
394
|
+
repoRoot,
|
|
395
|
+
cwd,
|
|
396
|
+
config,
|
|
397
|
+
options,
|
|
398
|
+
signals,
|
|
399
|
+
isDelete: false,
|
|
400
|
+
locationLabel: 'repo_local',
|
|
401
|
+
});
|
|
338
402
|
}
|
|
339
403
|
if (APPLY_PATCH_TOOL_NAMES.has(toolKind)) {
|
|
340
404
|
const patch = extractPatch(payload);
|
package/dist/core/config.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { BoundaryDriverId } from './capability/attestation.js';
|
|
2
|
+
import type { BelayMode, ClassifierOptions, ControlPlaneIntegrity, ScrubOptions, UnknownLocalEffectPolicy, UnparseableShellPolicy } from './types.js';
|
|
2
3
|
import { type BelayJudgeRuntimeConfig } from './verdict/judge-runtime-config.js';
|
|
3
|
-
export type BelayMode = 'enforce' | 'audit';
|
|
4
4
|
export type { UnknownLocalEffectPolicy };
|
|
5
5
|
export interface BelayConfigV1 {
|
|
6
6
|
version: 1;
|
|
@@ -46,6 +46,15 @@ export interface BelayModelAssistConfig {
|
|
|
46
46
|
model?: string;
|
|
47
47
|
timeoutMs?: number;
|
|
48
48
|
}
|
|
49
|
+
export interface BelayFileCheckpointConfig {
|
|
50
|
+
enabled: boolean;
|
|
51
|
+
allowNonGit: boolean;
|
|
52
|
+
maxFiles: number;
|
|
53
|
+
maxSourceBytes: number;
|
|
54
|
+
maxWorkspaceBytes: number;
|
|
55
|
+
prepareTimeoutMs: number;
|
|
56
|
+
copyConcurrency: number;
|
|
57
|
+
}
|
|
49
58
|
export interface BelayTransactionalConfig {
|
|
50
59
|
enabled: boolean;
|
|
51
60
|
minConfidence: number;
|
|
@@ -55,6 +64,14 @@ export interface BelayTransactionalConfig {
|
|
|
55
64
|
gates: {
|
|
56
65
|
shell: boolean;
|
|
57
66
|
};
|
|
67
|
+
fileCheckpoint: BelayFileCheckpointConfig;
|
|
68
|
+
checkpoint?: {
|
|
69
|
+
enabled: boolean;
|
|
70
|
+
appliedRetentionHours: number;
|
|
71
|
+
restoredRetentionHours: number;
|
|
72
|
+
maxCheckpoints: number;
|
|
73
|
+
maxBytes: number;
|
|
74
|
+
};
|
|
58
75
|
}
|
|
59
76
|
export interface BelayPolicyConfig {
|
|
60
77
|
unknownLocalEffect: UnknownLocalEffectPolicy;
|
|
@@ -94,7 +111,10 @@ export interface JudgeCloudConsent {
|
|
|
94
111
|
endpoint: string;
|
|
95
112
|
by: string;
|
|
96
113
|
}
|
|
114
|
+
export type BelayJudgeMode = 'shadow' | 'off';
|
|
97
115
|
export interface BelayJudgeConfig {
|
|
116
|
+
/** Gate uses PolicyEngine; judge runs async shadow/compare only. */
|
|
117
|
+
mode?: BelayJudgeMode;
|
|
98
118
|
provider: JudgeProvider;
|
|
99
119
|
providerId?: JudgeProviderId | DeprecatedJudgeProviderId;
|
|
100
120
|
model: string;
|
|
@@ -157,8 +177,14 @@ export interface BelayEgressConfig {
|
|
|
157
177
|
/** When true with egress enabled, L3 external command lists become hints only. */
|
|
158
178
|
demoteL3External: boolean;
|
|
159
179
|
}
|
|
180
|
+
export interface BelayCapabilityConfig {
|
|
181
|
+
grantsEnabled?: boolean;
|
|
182
|
+
boundaryDriver?: BoundaryDriverId;
|
|
183
|
+
attestationRelPath?: string;
|
|
184
|
+
}
|
|
185
|
+
export declare const DEFAULT_CAPABILITY_V5: BelayCapabilityConfig;
|
|
160
186
|
export interface BelayConfigV4 {
|
|
161
|
-
version: 4;
|
|
187
|
+
version: 4 | 5;
|
|
162
188
|
adapter?: 'cursor' | 'claude' | 'codex';
|
|
163
189
|
/** Where hooks/runtime/skill artifacts are installed. Defaults to project. */
|
|
164
190
|
installScope?: 'project' | 'global';
|
|
@@ -178,6 +204,7 @@ export interface BelayConfigV4 {
|
|
|
178
204
|
sandbox: BelaySandboxConfig;
|
|
179
205
|
audit: BelayConfigV2['audit'];
|
|
180
206
|
judge: BelayJudgeConfig;
|
|
207
|
+
capability?: BelayCapabilityConfig;
|
|
181
208
|
}
|
|
182
209
|
/** @deprecated Use BelayConfigV4 */
|
|
183
210
|
export type BelayConfigV3 = BelayConfigV4;
|
|
@@ -186,6 +213,9 @@ export type BelayConfig = BelayConfigV4;
|
|
|
186
213
|
export declare const DEFAULT_FENCE_WARN_THRESHOLD = 0.5;
|
|
187
214
|
export declare const DEFAULT_CONFIDENCE_THRESHOLDS: BelayConfidenceThresholds;
|
|
188
215
|
export declare const DEFAULT_MODEL_ASSIST: BelayModelAssistConfig;
|
|
216
|
+
export declare const DEFAULT_FILE_CHECKPOINT: BelayFileCheckpointConfig;
|
|
217
|
+
export declare function normalizeFileCheckpointConfig(raw: Partial<BelayFileCheckpointConfig> | undefined): BelayFileCheckpointConfig;
|
|
218
|
+
export declare const DEFAULT_RECOVERY_CHECKPOINT: NonNullable<BelayTransactionalConfig['checkpoint']>;
|
|
189
219
|
export declare const DEFAULT_TRANSACTIONAL_V3: BelayTransactionalConfig;
|
|
190
220
|
export declare const LEGACY_POLICY_V3: BelayPolicyConfig;
|
|
191
221
|
/** Fresh install defaults: recoverable-first with opaque/unparseable fail-closed. */
|
|
@@ -239,6 +269,7 @@ type RawConfigInput = Partial<{
|
|
|
239
269
|
sandbox: Partial<BelaySandboxConfig>;
|
|
240
270
|
audit: Partial<BelayConfigV2['audit']>;
|
|
241
271
|
installScope: 'project' | 'global';
|
|
272
|
+
capability: Partial<BelayCapabilityConfig>;
|
|
242
273
|
}>;
|
|
243
274
|
export declare function migrateConfig(loaded: unknown): BelayConfigV4;
|
|
244
275
|
export declare function normalizeConfigV2(config: BelayConfigV2): BelayConfigV2;
|