@guilz-dev/belay 0.7.0 → 0.8.1
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 +19 -19
- package/dist/adapters/shared/gate-runtime.js +23 -5
- package/dist/bundle/claude-runtime.mjs +4909 -3504
- package/dist/bundle/codex-runtime.mjs +5050 -3645
- package/dist/bundle/cursor-runtime.mjs +5050 -3645
- package/dist/commands/doctor.js +24 -21
- package/dist/commands/dogfood.js +1 -1
- package/dist/commands/explain.js +2 -2
- package/dist/commands/harvest.d.ts +0 -1
- package/dist/commands/harvest.js +1 -4
- package/dist/commands/metrics.js +39 -4
- package/dist/commands/quality.js +3 -9
- package/dist/commands/status.js +1 -1
- package/dist/conformance/guarantee-table.js +133 -11
- package/dist/conformance/types.d.ts +1 -0
- package/dist/core/audit-metrics.d.ts +24 -0
- package/dist/core/audit-metrics.js +82 -16
- package/dist/core/audit-types.d.ts +3 -0
- package/dist/core/capability/index.d.ts +1 -1
- package/dist/core/capability/policy-engine.d.ts +31 -1
- package/dist/core/capability/policy-engine.js +186 -0
- package/dist/core/capability/request.d.ts +9 -0
- package/dist/core/capability/resolver.d.ts +0 -1
- package/dist/core/capability/resolver.js +0 -1
- package/dist/core/config.js +0 -2
- package/dist/core/effect-ir/audit.js +1 -0
- package/dist/core/effect-ir/build.js +5 -1
- package/dist/core/effect-ir/index.d.ts +6 -1
- package/dist/core/effect-ir/index.js +2 -0
- package/dist/core/effect-ir/normalize.js +9 -3
- package/dist/core/effect-ir/policy.d.ts +2 -1
- package/dist/core/effect-ir/policy.js +93 -6
- package/dist/core/effect-ir/shell-build.d.ts +43 -0
- package/dist/core/effect-ir/shell-build.js +77 -0
- package/dist/core/effect-ir/shell-lower.d.ts +14 -0
- package/dist/core/effect-ir/shell-lower.js +1509 -0
- package/dist/core/effect-ir/types.d.ts +9 -2
- package/dist/core/gate-engine.js +63 -23
- package/dist/core/git-resource-identity.d.ts +26 -0
- package/dist/core/git-resource-identity.js +284 -0
- package/dist/core/harvest.d.ts +3 -7
- package/dist/core/harvest.js +4 -27
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +1 -0
- package/dist/core/path-utils.js +42 -23
- package/dist/core/shell-substitution.d.ts +1 -0
- package/dist/core/shell-substitution.js +61 -0
- package/dist/core/standing-allow.d.ts +1 -2
- package/dist/core/standing-allow.js +3 -20
- package/dist/core/types.d.ts +4 -1
- package/dist/core/verdict/adapter.js +15 -15
- package/dist/core/verdict/containment.js +4 -0
- package/dist/core/verdict/egress-classify.d.ts +12 -0
- package/dist/core/verdict/egress-classify.js +746 -0
- package/dist/core/verdict/git-classifier.d.ts +12 -0
- package/dist/core/verdict/git-classifier.js +320 -0
- package/dist/core/verdict/launcher-resolve.js +77 -17
- package/dist/core/verdict/parser.d.ts +2 -0
- package/dist/core/verdict/parser.js +95 -1
- package/dist/core/verdict/types.d.ts +2 -3
- package/dist/core/verdict/verdict.js +103 -952
- package/dist/corpus/must-allow-commands.d.ts +2 -1
- package/dist/corpus/must-allow-commands.js +2 -1
- package/dist/corpus/runtime-match.d.ts +2 -1
- package/dist/corpus/runtime-match.js +2 -1
- package/dist/corpus/types.d.ts +2 -2
- package/dist/operational-insights.d.ts +3 -0
- package/dist/operational-insights.js +7 -3
- package/dist/runtime-provenance.d.ts +8 -0
- package/dist/runtime-provenance.js +34 -0
- package/dist/templates.js +7 -2
- package/dist/types.d.ts +3 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/dist/core/verdict/overrides.d.ts +0 -7
- package/dist/core/verdict/overrides.js +0 -37
- package/dist/core/verdict/shell-policy.d.ts +0 -26
- package/dist/core/verdict/shell-policy.js +0 -40
- package/dist/corpus/standing-allow-catalog.generated.d.ts +0 -13
- package/dist/corpus/standing-allow-catalog.generated.js +0 -99
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { policyDecisionToLegacyReason } from '../capability/policy-bridge.js';
|
|
3
|
-
import {
|
|
3
|
+
import { evaluateEffectRequirementPolicy, policyDecisionRequiresAsk, } from '../capability/policy-engine.js';
|
|
4
4
|
import { collectRequirements, flattenRequirementsToCapabilityRequests } from './build.js';
|
|
5
5
|
function shellPrincipal(repoRoot, cwd) {
|
|
6
6
|
return {
|
|
@@ -61,13 +61,100 @@ export function capabilityRequestsFromEffectPlan(plan, context) {
|
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
63
|
export function evaluateEffectPlanPolicy(plan, context) {
|
|
64
|
+
const requirements = collectRequirements(plan.root);
|
|
64
65
|
const capabilityRequests = capabilityRequestsFromEffectPlan(plan, context);
|
|
65
|
-
const
|
|
66
|
+
const decisions = requirements.map((requirement, index) => evaluateEffectRequirementPolicy({
|
|
67
|
+
tag: requirement.tag,
|
|
68
|
+
action: requirement.action,
|
|
69
|
+
resource: requirement.resource,
|
|
70
|
+
evidence: requirement.evidence,
|
|
71
|
+
provenance: requirement.provenance,
|
|
72
|
+
}, {
|
|
73
|
+
cwd: context.cwd,
|
|
74
|
+
repoRoot: context.repoRoot,
|
|
75
|
+
trustedWorkspaceRoots: context.trustedWorkspaceRoots,
|
|
76
|
+
sensitivePaths: context.sensitivePaths,
|
|
77
|
+
protectedArtifactRoots: context.protectedArtifactRoots,
|
|
66
78
|
grants: context.grants,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
79
|
+
capabilityRequest: capabilityRequests[index],
|
|
80
|
+
}));
|
|
81
|
+
const indeterminateIndexes = requirements.flatMap((requirement, index) => requirement.action === 'indeterminate' ||
|
|
82
|
+
requirement.tag === 'indeterminate' ||
|
|
83
|
+
requirement.evidence.level === 'indeterminate'
|
|
84
|
+
? [index]
|
|
85
|
+
: []);
|
|
86
|
+
const unresolvedPartial = indeterminateIndexes.length === 0 ||
|
|
87
|
+
indeterminateIndexes.some((index) => decisions[index]?.matchedRule !== 'grant.exact');
|
|
88
|
+
if (plan.completeness === 'partial' && unresolvedPartial) {
|
|
89
|
+
decisions.push({
|
|
90
|
+
effectDisposition: 'ask',
|
|
91
|
+
outcome: 'require_approval',
|
|
92
|
+
reason: 'indeterminate_effect',
|
|
93
|
+
signals: [...plan.signals, 'effect_plan_partial'],
|
|
94
|
+
matchedRule: 'effect.plan_partial',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
const worstDecision = worstEffectDecision(decisions);
|
|
98
|
+
const authorizationDecision = (worstDecision?.effectDisposition === 'ask'
|
|
99
|
+
? worstDecision
|
|
100
|
+
: (decisions.find((decision) => decision.matchedRule === 'grant.exact') ?? worstDecision)) ??
|
|
101
|
+
{
|
|
102
|
+
outcome: 'allow',
|
|
103
|
+
reason: 'read_only',
|
|
104
|
+
signals: [],
|
|
105
|
+
matchedRule: 'effect.effect_free',
|
|
106
|
+
};
|
|
107
|
+
const projection = projectEffectPlanPolicy(decisions, authorizationDecision);
|
|
108
|
+
return {
|
|
109
|
+
capabilityRequests,
|
|
110
|
+
decisions,
|
|
111
|
+
authorizationDecision,
|
|
112
|
+
projection,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function dispositionRank(disposition) {
|
|
116
|
+
switch (disposition) {
|
|
117
|
+
case 'ask':
|
|
118
|
+
return 2;
|
|
119
|
+
case 'allow_flagged':
|
|
120
|
+
return 1;
|
|
121
|
+
case 'allow':
|
|
122
|
+
return 0;
|
|
123
|
+
default:
|
|
124
|
+
return 2;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function projectEffectPlanPolicy(decisions, authorizationDecision) {
|
|
128
|
+
const worst = worstEffectDecision(decisions);
|
|
129
|
+
if (!worst) {
|
|
130
|
+
return {
|
|
131
|
+
permission: 'allow',
|
|
132
|
+
hookVerdict: 'allow',
|
|
133
|
+
reason: 'read_only',
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (worst.effectDisposition === 'ask') {
|
|
137
|
+
return {
|
|
138
|
+
permission: 'ask',
|
|
139
|
+
hookVerdict: 'deny_pending_approval',
|
|
140
|
+
reason: policyDecisionToLegacyReason(authorizationDecision),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
return {
|
|
144
|
+
permission: 'allow',
|
|
145
|
+
hookVerdict: worst.effectDisposition === 'allow_flagged' ? 'allow_flagged' : 'allow',
|
|
146
|
+
reason: policyDecisionToLegacyReason(worst),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function worstEffectDecision(decisions) {
|
|
150
|
+
let worst;
|
|
151
|
+
for (const decision of decisions) {
|
|
152
|
+
if (!worst ||
|
|
153
|
+
dispositionRank(decision.effectDisposition) > dispositionRank(worst.effectDisposition)) {
|
|
154
|
+
worst = decision;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return worst;
|
|
71
158
|
}
|
|
72
159
|
export function effectPlanPolicyRequiresAsk(decision) {
|
|
73
160
|
return policyDecisionRequiresAsk(decision);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { CapabilityResource } from '../capability/request.js';
|
|
2
|
+
import type { AnalysisCompleteness, EffectPlan, EffectRequirement } from './types.js';
|
|
3
|
+
type ShellNetworkResource = Extract<CapabilityResource, {
|
|
4
|
+
kind: 'network';
|
|
5
|
+
}> & Required<Pick<Extract<CapabilityResource, {
|
|
6
|
+
kind: 'network';
|
|
7
|
+
}>, 'mode' | 'payload'>>;
|
|
8
|
+
type ShellExecutableResource = Extract<CapabilityResource, {
|
|
9
|
+
kind: 'executable';
|
|
10
|
+
}> & Required<Pick<Extract<CapabilityResource, {
|
|
11
|
+
kind: 'executable';
|
|
12
|
+
}>, 'operation'>>;
|
|
13
|
+
type ShellGitRefResource = Extract<CapabilityResource, {
|
|
14
|
+
kind: 'git-ref';
|
|
15
|
+
}> & Required<Pick<Extract<CapabilityResource, {
|
|
16
|
+
kind: 'git-ref';
|
|
17
|
+
}>, 'scope'>>;
|
|
18
|
+
export type ShellEffectResource = Exclude<CapabilityResource, {
|
|
19
|
+
kind: 'network' | 'executable' | 'git-ref';
|
|
20
|
+
}> | ShellNetworkResource | ShellExecutableResource | ShellGitRefResource;
|
|
21
|
+
export type ShellEffectRequirement = Omit<EffectRequirement, 'resource'> & {
|
|
22
|
+
resource: ShellEffectResource;
|
|
23
|
+
};
|
|
24
|
+
export interface ShellEffectSegment {
|
|
25
|
+
commandRedacted: string;
|
|
26
|
+
segmentHead: string;
|
|
27
|
+
requirements: readonly ShellEffectRequirement[];
|
|
28
|
+
completeness: AnalysisCompleteness;
|
|
29
|
+
opacity: EffectPlan['opacity'];
|
|
30
|
+
signals: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
export interface BuildShellEffectPlanParams {
|
|
33
|
+
inputFingerprint: string;
|
|
34
|
+
segments: readonly ShellEffectSegment[];
|
|
35
|
+
signals?: readonly string[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build a canonical EffectPlan from shell segments that have already been
|
|
39
|
+
* semantically analyzed. This function only assembles and normalizes effects;
|
|
40
|
+
* command-specific lowering belongs to the segment producers.
|
|
41
|
+
*/
|
|
42
|
+
export declare function buildShellEffectPlan(params: BuildShellEffectPlanParams): EffectPlan;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { collectRequirements } from './build.js';
|
|
2
|
+
import { mergeRequirements } from './normalize.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build a canonical EffectPlan from shell segments that have already been
|
|
5
|
+
* semantically analyzed. This function only assembles and normalizes effects;
|
|
6
|
+
* command-specific lowering belongs to the segment producers.
|
|
7
|
+
*/
|
|
8
|
+
export function buildShellEffectPlan(params) {
|
|
9
|
+
const nodes = params.segments.map(buildSegmentNode);
|
|
10
|
+
const root = mergeNodes(nodes);
|
|
11
|
+
const requirements = collectRequirements(root);
|
|
12
|
+
const completeness = requirements.some(isIndeterminateRequirement)
|
|
13
|
+
? 'partial'
|
|
14
|
+
: 'complete';
|
|
15
|
+
return {
|
|
16
|
+
version: 1,
|
|
17
|
+
root,
|
|
18
|
+
inputFingerprint: params.inputFingerprint,
|
|
19
|
+
opacity: mergeOpacity(params.segments.map((segment) => segment.opacity)),
|
|
20
|
+
disposition: requirements.length === 0 ? 'effect_free' : 'effects',
|
|
21
|
+
completeness,
|
|
22
|
+
signals: [
|
|
23
|
+
...new Set([
|
|
24
|
+
...(params.signals ?? []),
|
|
25
|
+
...params.segments.flatMap((segment) => [...segment.signals]),
|
|
26
|
+
...requirements.flatMap((requirement) => [...requirement.evidence.signals]),
|
|
27
|
+
]),
|
|
28
|
+
].sort(),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function buildSegmentNode(segment) {
|
|
32
|
+
const requirements = mergeRequirements(segment.requirements);
|
|
33
|
+
const partial = segment.completeness === 'partial' ||
|
|
34
|
+
requirements.some((requirement) => requirement.evidence.level === 'indeterminate');
|
|
35
|
+
if (partial && !requirements.some(isIndeterminateRequirement)) {
|
|
36
|
+
const provenance = { segment: segment.commandRedacted };
|
|
37
|
+
requirements.push({
|
|
38
|
+
tag: 'indeterminate',
|
|
39
|
+
action: 'indeterminate',
|
|
40
|
+
resource: { kind: 'unknown' },
|
|
41
|
+
evidence: {
|
|
42
|
+
level: 'indeterminate',
|
|
43
|
+
signals: [...new Set([...segment.signals, 'effect_plan.segment_partial'])],
|
|
44
|
+
basis: [`shell_segment:${segment.segmentHead}`],
|
|
45
|
+
},
|
|
46
|
+
provenance,
|
|
47
|
+
provenances: [provenance],
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
kind: 'exec',
|
|
52
|
+
commandRedacted: segment.commandRedacted,
|
|
53
|
+
segmentHead: segment.segmentHead,
|
|
54
|
+
requirements,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function isIndeterminateRequirement(requirement) {
|
|
58
|
+
return requirement.tag === 'indeterminate' || requirement.action === 'indeterminate';
|
|
59
|
+
}
|
|
60
|
+
function mergeNodes(nodes) {
|
|
61
|
+
if (nodes.length === 1 && nodes[0]) {
|
|
62
|
+
return nodes[0];
|
|
63
|
+
}
|
|
64
|
+
return { kind: 'merge', children: nodes };
|
|
65
|
+
}
|
|
66
|
+
function mergeOpacity(opacities) {
|
|
67
|
+
if (opacities.includes('unparseable')) {
|
|
68
|
+
return 'unparseable';
|
|
69
|
+
}
|
|
70
|
+
if (opacities.includes('opaque')) {
|
|
71
|
+
return 'opaque';
|
|
72
|
+
}
|
|
73
|
+
if (opacities.includes('recursive')) {
|
|
74
|
+
return 'recursive';
|
|
75
|
+
}
|
|
76
|
+
return 'transparent';
|
|
77
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { EffectPlan } from './types.js';
|
|
2
|
+
export interface LowerShellEffectPlanParams {
|
|
3
|
+
command: string;
|
|
4
|
+
cwd: string;
|
|
5
|
+
repoRoot: string;
|
|
6
|
+
inputFingerprint: string;
|
|
7
|
+
env?: Readonly<Record<string, string | undefined>>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Parse and lower a general shell command into the canonical Task 2 model.
|
|
11
|
+
* This function only produces effects; it does not evaluate policy or return a
|
|
12
|
+
* permission/verdict.
|
|
13
|
+
*/
|
|
14
|
+
export declare function lowerShellEffectPlan(params: LowerShellEffectPlanParams): EffectPlan;
|