@provos/ironcurtain 0.1.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 +202 -0
- package/README.md +311 -0
- package/dist/agent/index.d.ts +10 -0
- package/dist/agent/index.js +71 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/prompts.d.ts +5 -0
- package/dist/agent/prompts.js +26 -0
- package/dist/agent/prompts.js.map +1 -0
- package/dist/agent/tools.d.ts +13 -0
- package/dist/agent/tools.js +51 -0
- package/dist/agent/tools.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +78 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/constitution.md +16 -0
- package/dist/config/generated/compiled-policy.json +236 -0
- package/dist/config/generated/test-scenarios.json +765 -0
- package/dist/config/generated/tool-annotations.json +955 -0
- package/dist/config/index.d.ts +25 -0
- package/dist/config/index.js +151 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/mcp-servers.json +22 -0
- package/dist/config/model-provider.d.ts +49 -0
- package/dist/config/model-provider.js +78 -0
- package/dist/config/model-provider.js.map +1 -0
- package/dist/config/paths.d.ts +59 -0
- package/dist/config/paths.js +96 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/types.d.ts +89 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/user-config.d.ts +93 -0
- package/dist/config/user-config.js +309 -0
- package/dist/config/user-config.js.map +1 -0
- package/dist/hash.d.ts +17 -0
- package/dist/hash.js +34 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +11 -0
- package/dist/logger.js +93 -0
- package/dist/logger.js.map +1 -0
- package/dist/pipeline/annotate.d.ts +9 -0
- package/dist/pipeline/annotate.js +136 -0
- package/dist/pipeline/annotate.js.map +1 -0
- package/dist/pipeline/compile.d.ts +23 -0
- package/dist/pipeline/compile.js +386 -0
- package/dist/pipeline/compile.js.map +1 -0
- package/dist/pipeline/constitution-compiler.d.ts +22 -0
- package/dist/pipeline/constitution-compiler.js +197 -0
- package/dist/pipeline/constitution-compiler.js.map +1 -0
- package/dist/pipeline/generate-with-repair.d.ts +22 -0
- package/dist/pipeline/generate-with-repair.js +64 -0
- package/dist/pipeline/generate-with-repair.js.map +1 -0
- package/dist/pipeline/handwritten-scenarios.d.ts +9 -0
- package/dist/pipeline/handwritten-scenarios.js +321 -0
- package/dist/pipeline/handwritten-scenarios.js.map +1 -0
- package/dist/pipeline/llm-logger.d.ts +42 -0
- package/dist/pipeline/llm-logger.js +78 -0
- package/dist/pipeline/llm-logger.js.map +1 -0
- package/dist/pipeline/pipeline-shared.d.ts +47 -0
- package/dist/pipeline/pipeline-shared.js +145 -0
- package/dist/pipeline/pipeline-shared.js.map +1 -0
- package/dist/pipeline/policy-verifier.d.ts +46 -0
- package/dist/pipeline/policy-verifier.js +277 -0
- package/dist/pipeline/policy-verifier.js.map +1 -0
- package/dist/pipeline/scenario-generator.d.ts +11 -0
- package/dist/pipeline/scenario-generator.js +128 -0
- package/dist/pipeline/scenario-generator.js.map +1 -0
- package/dist/pipeline/tool-annotator.d.ts +24 -0
- package/dist/pipeline/tool-annotator.js +201 -0
- package/dist/pipeline/tool-annotator.js.map +1 -0
- package/dist/pipeline/types.d.ts +122 -0
- package/dist/pipeline/types.js +10 -0
- package/dist/pipeline/types.js.map +1 -0
- package/dist/sandbox/index.d.ts +39 -0
- package/dist/sandbox/index.js +178 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/session/agent-session.d.ts +83 -0
- package/dist/session/agent-session.js +382 -0
- package/dist/session/agent-session.js.map +1 -0
- package/dist/session/cli-transport.d.ts +61 -0
- package/dist/session/cli-transport.js +320 -0
- package/dist/session/cli-transport.js.map +1 -0
- package/dist/session/errors.d.ts +19 -0
- package/dist/session/errors.js +33 -0
- package/dist/session/errors.js.map +1 -0
- package/dist/session/index.d.ts +29 -0
- package/dist/session/index.js +104 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/message-compactor.d.ts +32 -0
- package/dist/session/message-compactor.js +81 -0
- package/dist/session/message-compactor.js.map +1 -0
- package/dist/session/prompts.d.ts +5 -0
- package/dist/session/prompts.js +62 -0
- package/dist/session/prompts.js.map +1 -0
- package/dist/session/resource-budget-tracker.d.ts +124 -0
- package/dist/session/resource-budget-tracker.js +327 -0
- package/dist/session/resource-budget-tracker.js.map +1 -0
- package/dist/session/step-loop-detector.d.ts +63 -0
- package/dist/session/step-loop-detector.js +136 -0
- package/dist/session/step-loop-detector.js.map +1 -0
- package/dist/session/transport.d.ts +24 -0
- package/dist/session/transport.js +2 -0
- package/dist/session/transport.js.map +1 -0
- package/dist/session/truncate-result.d.ts +35 -0
- package/dist/session/truncate-result.js +71 -0
- package/dist/session/truncate-result.js.map +1 -0
- package/dist/session/types.d.ts +220 -0
- package/dist/session/types.js +6 -0
- package/dist/session/types.js.map +1 -0
- package/dist/trusted-process/audit-log.d.ts +7 -0
- package/dist/trusted-process/audit-log.js +21 -0
- package/dist/trusted-process/audit-log.js.map +1 -0
- package/dist/trusted-process/call-circuit-breaker.d.ts +33 -0
- package/dist/trusted-process/call-circuit-breaker.js +61 -0
- package/dist/trusted-process/call-circuit-breaker.js.map +1 -0
- package/dist/trusted-process/escalation.d.ts +7 -0
- package/dist/trusted-process/escalation.js +38 -0
- package/dist/trusted-process/escalation.js.map +1 -0
- package/dist/trusted-process/index.d.ts +32 -0
- package/dist/trusted-process/index.js +151 -0
- package/dist/trusted-process/index.js.map +1 -0
- package/dist/trusted-process/mcp-client-manager.d.ts +25 -0
- package/dist/trusted-process/mcp-client-manager.js +90 -0
- package/dist/trusted-process/mcp-client-manager.js.map +1 -0
- package/dist/trusted-process/mcp-proxy-server.d.ts +24 -0
- package/dist/trusted-process/mcp-proxy-server.js +451 -0
- package/dist/trusted-process/mcp-proxy-server.js.map +1 -0
- package/dist/trusted-process/path-utils.d.ts +50 -0
- package/dist/trusted-process/path-utils.js +158 -0
- package/dist/trusted-process/path-utils.js.map +1 -0
- package/dist/trusted-process/policy-engine.d.ts +88 -0
- package/dist/trusted-process/policy-engine.js +523 -0
- package/dist/trusted-process/policy-engine.js.map +1 -0
- package/dist/trusted-process/policy-roots.d.ts +50 -0
- package/dist/trusted-process/policy-roots.js +67 -0
- package/dist/trusted-process/policy-roots.js.map +1 -0
- package/dist/trusted-process/policy-types.d.ts +6 -0
- package/dist/trusted-process/policy-types.js +2 -0
- package/dist/trusted-process/policy-types.js.map +1 -0
- package/dist/trusted-process/sandbox-integration.d.ts +92 -0
- package/dist/trusted-process/sandbox-integration.js +184 -0
- package/dist/trusted-process/sandbox-integration.js.map +1 -0
- package/dist/types/argument-roles.d.ts +112 -0
- package/dist/types/argument-roles.js +344 -0
- package/dist/types/argument-roles.js.map +1 -0
- package/dist/types/audit.d.ts +18 -0
- package/dist/types/audit.js +2 -0
- package/dist/types/audit.js.map +1 -0
- package/dist/types/mcp.d.ts +20 -0
- package/dist/types/mcp.js +2 -0
- package/dist/types/mcp.js.map +1 -0
- package/package.json +83 -0
- package/src/config/constitution.md +16 -0
- package/src/config/generated/compiled-policy.json +236 -0
- package/src/config/generated/test-scenarios.json +765 -0
- package/src/config/generated/tool-annotations.json +955 -0
- package/src/config/mcp-servers.json +22 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PolicyEngine -- Two-phase declarative policy evaluation.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1: Hardcoded structural invariants (protected paths, unknown tools).
|
|
5
|
+
* These are never overridden by compiled rules. Sandbox containment
|
|
6
|
+
* is checked per-role: roles whose paths are all within the sandbox
|
|
7
|
+
* are resolved here and skipped in Phase 2.
|
|
8
|
+
*
|
|
9
|
+
* Phase 2: Compiled declarative rules loaded from compiled-policy.json.
|
|
10
|
+
* Each distinct role from the tool's annotation is evaluated
|
|
11
|
+
* independently through the rule chain (first-match-wins per role).
|
|
12
|
+
* The most restrictive result wins: deny > escalate > allow.
|
|
13
|
+
* Roles already resolved by Phase 1 sandbox containment are skipped.
|
|
14
|
+
*/
|
|
15
|
+
import { getPathRoles, getUrlRoles, getRoleDefinition, resolveRealPath, SANDBOX_SAFE_PATH_ROLES, } from '../types/argument-roles.js';
|
|
16
|
+
/**
|
|
17
|
+
* Heuristically extracts filesystem paths from tool call arguments.
|
|
18
|
+
* Any string value starting with '/', '.', or '~' is treated as a path.
|
|
19
|
+
* Handles both single string values and arrays of strings.
|
|
20
|
+
*/
|
|
21
|
+
function extractPathsHeuristic(args) {
|
|
22
|
+
const paths = [];
|
|
23
|
+
for (const value of Object.values(args)) {
|
|
24
|
+
if (typeof value === 'string' && (value.startsWith('/') || value.startsWith('.') || value.startsWith('~'))) {
|
|
25
|
+
paths.push(value);
|
|
26
|
+
}
|
|
27
|
+
else if (Array.isArray(value)) {
|
|
28
|
+
for (const item of value) {
|
|
29
|
+
if (typeof item === 'string' && (item.startsWith('/') || item.startsWith('.') || item.startsWith('~'))) {
|
|
30
|
+
paths.push(item);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return paths;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Extracts paths from arguments based on annotation roles.
|
|
39
|
+
* Only returns paths for arguments whose annotated roles intersect
|
|
40
|
+
* with the target roles. Handles both string and string[] arguments.
|
|
41
|
+
*/
|
|
42
|
+
function extractAnnotatedPaths(args, annotation, targetRoles) {
|
|
43
|
+
const paths = [];
|
|
44
|
+
for (const [argName, roles] of Object.entries(annotation.args)) {
|
|
45
|
+
if (!roles.some(r => targetRoles.includes(r)))
|
|
46
|
+
continue;
|
|
47
|
+
const value = args[argName];
|
|
48
|
+
if (typeof value === 'string') {
|
|
49
|
+
paths.push(value);
|
|
50
|
+
}
|
|
51
|
+
else if (Array.isArray(value)) {
|
|
52
|
+
for (const item of value) {
|
|
53
|
+
if (typeof item === 'string')
|
|
54
|
+
paths.push(item);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return paths;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Checks whether a target path is contained within a directory.
|
|
62
|
+
* Both paths are resolved to their real canonical form (following symlinks)
|
|
63
|
+
* before comparison, which neutralizes both path traversal and symlink attacks.
|
|
64
|
+
*/
|
|
65
|
+
function isWithinDirectory(targetPath, directory) {
|
|
66
|
+
const resolved = resolveRealPath(targetPath);
|
|
67
|
+
const resolvedDir = resolveRealPath(directory);
|
|
68
|
+
return resolved === resolvedDir || resolved.startsWith(resolvedDir + '/');
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Checks whether a resolved path matches any protected path.
|
|
72
|
+
* A path is protected if it equals a protected path exactly
|
|
73
|
+
* or is contained within a protected directory.
|
|
74
|
+
* Both sides are resolved through symlinks for accurate comparison.
|
|
75
|
+
*/
|
|
76
|
+
function isProtectedPath(resolvedPath, protectedPaths) {
|
|
77
|
+
const realPath = resolveRealPath(resolvedPath);
|
|
78
|
+
return protectedPaths.some(pp => {
|
|
79
|
+
const resolvedPP = resolveRealPath(pp);
|
|
80
|
+
return realPath === resolvedPP || realPath.startsWith(resolvedPP + '/');
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
const NO_ROLES_RESOLVED = new Set();
|
|
84
|
+
/** Wraps a final EvaluationResult into a StructuralResult. */
|
|
85
|
+
function finalDecision(decision) {
|
|
86
|
+
return { decision, sandboxResolvedRoles: NO_ROLES_RESOLVED };
|
|
87
|
+
}
|
|
88
|
+
/** Higher severity = more restrictive. Used to pick the strictest per-role result. */
|
|
89
|
+
const DECISION_SEVERITY = {
|
|
90
|
+
allow: 0,
|
|
91
|
+
escalate: 1,
|
|
92
|
+
deny: 2,
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Collects all distinct non-"none" roles from a tool annotation's arguments.
|
|
96
|
+
* Returns an empty array if the tool has no role-bearing arguments.
|
|
97
|
+
*/
|
|
98
|
+
function collectDistinctRoles(annotation) {
|
|
99
|
+
const roles = new Set();
|
|
100
|
+
for (const argRoles of Object.values(annotation.args)) {
|
|
101
|
+
for (const role of argRoles) {
|
|
102
|
+
if (getRoleDefinition(role).isResourceIdentifier)
|
|
103
|
+
roles.add(role);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return [...roles];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Extracts URL values from arguments based on annotation roles.
|
|
110
|
+
* Returns an array of { value, role, roleDef } for each URL-category argument.
|
|
111
|
+
*/
|
|
112
|
+
function extractAnnotatedUrls(args, annotation, targetRoles) {
|
|
113
|
+
const urls = [];
|
|
114
|
+
for (const [argName, roles] of Object.entries(annotation.args)) {
|
|
115
|
+
const matchingRole = roles.find(r => targetRoles.includes(r));
|
|
116
|
+
if (!matchingRole)
|
|
117
|
+
continue;
|
|
118
|
+
const value = args[argName];
|
|
119
|
+
const roleDef = getRoleDefinition(matchingRole);
|
|
120
|
+
if (typeof value === 'string') {
|
|
121
|
+
urls.push({ value, role: matchingRole, roleDef });
|
|
122
|
+
}
|
|
123
|
+
else if (Array.isArray(value)) {
|
|
124
|
+
for (const item of value) {
|
|
125
|
+
if (typeof item === 'string') {
|
|
126
|
+
urls.push({ value: item, role: matchingRole, roleDef });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return urls;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Applies the resolution pipeline for a URL-category value:
|
|
135
|
+
* 1. resolveForPolicy(value, allArgs) -- resolve named remote to URL
|
|
136
|
+
* 2. normalize(resolvedValue) -- canonicalize URL format
|
|
137
|
+
* 3. prepareForPolicy(normalizedValue) -- extract domain for allowlist check
|
|
138
|
+
*/
|
|
139
|
+
function resolveUrlForDomainCheck(value, roleDef, allArgs) {
|
|
140
|
+
const resolved = roleDef.resolveForPolicy?.(value, allArgs) ?? value;
|
|
141
|
+
const normalized = roleDef.normalize(resolved);
|
|
142
|
+
return roleDef.prepareForPolicy?.(normalized) ?? normalized;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Checks whether a domain matches any pattern in an allowlist.
|
|
146
|
+
* Supports exact match, `*` wildcard (matches everything),
|
|
147
|
+
* and `*.example.com` prefix wildcards (matches example.com and *.example.com).
|
|
148
|
+
*/
|
|
149
|
+
export function domainMatchesAllowlist(domain, allowedDomains) {
|
|
150
|
+
return allowedDomains.some(pattern => {
|
|
151
|
+
if (pattern === '*')
|
|
152
|
+
return true;
|
|
153
|
+
if (pattern.startsWith('*.')) {
|
|
154
|
+
const suffix = pattern.slice(1); // ".github.com"
|
|
155
|
+
return domain === pattern.slice(2) || domain.endsWith(suffix);
|
|
156
|
+
}
|
|
157
|
+
return domain === pattern;
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Checks whether a rule has role-related conditions (roles, paths, or domains).
|
|
162
|
+
* Rules without these conditions are role-agnostic and match any role.
|
|
163
|
+
*/
|
|
164
|
+
function hasRoleConditions(rule) {
|
|
165
|
+
return rule.if.roles !== undefined || rule.if.paths !== undefined || rule.if.domains !== undefined;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Checks whether a rule's role-related conditions include a specific role.
|
|
169
|
+
* For `roles` conditions: the role must be in the list.
|
|
170
|
+
* For `paths` conditions: the role must be in the paths.roles list.
|
|
171
|
+
* For `domains` conditions: the role must be in the domains.roles list.
|
|
172
|
+
*/
|
|
173
|
+
function ruleRelevantToRole(rule, role) {
|
|
174
|
+
const cond = rule.if;
|
|
175
|
+
if (cond.roles !== undefined && !cond.roles.includes(role))
|
|
176
|
+
return false;
|
|
177
|
+
if (cond.paths !== undefined && !cond.paths.roles.includes(role))
|
|
178
|
+
return false;
|
|
179
|
+
if (cond.domains !== undefined && !cond.domains.roles.includes(role))
|
|
180
|
+
return false;
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
export class PolicyEngine {
|
|
184
|
+
annotationMap;
|
|
185
|
+
compiledPolicy;
|
|
186
|
+
protectedPaths;
|
|
187
|
+
allowedDirectory;
|
|
188
|
+
serverDomainAllowlists;
|
|
189
|
+
constructor(compiledPolicy, toolAnnotations, protectedPaths, allowedDirectory, serverDomainAllowlists) {
|
|
190
|
+
this.compiledPolicy = compiledPolicy;
|
|
191
|
+
this.protectedPaths = protectedPaths;
|
|
192
|
+
this.allowedDirectory = allowedDirectory;
|
|
193
|
+
this.serverDomainAllowlists = serverDomainAllowlists ?? new Map();
|
|
194
|
+
this.annotationMap = this.buildAnnotationMap(toolAnnotations);
|
|
195
|
+
}
|
|
196
|
+
buildAnnotationMap(annotations) {
|
|
197
|
+
const map = new Map();
|
|
198
|
+
for (const [serverName, serverData] of Object.entries(annotations.servers)) {
|
|
199
|
+
for (const tool of serverData.tools) {
|
|
200
|
+
const key = `${serverName}__${tool.toolName}`;
|
|
201
|
+
map.set(key, tool);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return map;
|
|
205
|
+
}
|
|
206
|
+
/** Returns the annotation for a tool, or undefined if unknown. */
|
|
207
|
+
getAnnotation(serverName, toolName) {
|
|
208
|
+
return this.annotationMap.get(`${serverName}__${toolName}`);
|
|
209
|
+
}
|
|
210
|
+
evaluate(request) {
|
|
211
|
+
// Phase 1: Structural invariants (may resolve some roles via sandbox containment)
|
|
212
|
+
const structural = this.evaluateStructuralInvariants(request);
|
|
213
|
+
if (structural.decision)
|
|
214
|
+
return structural.decision;
|
|
215
|
+
// Phase 2: Compiled rules (skipping sandbox-resolved roles)
|
|
216
|
+
return this.evaluateCompiledRules(request, structural.sandboxResolvedRoles);
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Phase 1: Hardcoded structural invariants.
|
|
220
|
+
*
|
|
221
|
+
* 1a. Protected path check (deny)
|
|
222
|
+
* 1b. Sandbox containment for path-category roles (allow/partial)
|
|
223
|
+
* 1c. Domain allowlist for url-category roles (escalate)
|
|
224
|
+
* 1d. Unknown tool denial (deny)
|
|
225
|
+
*
|
|
226
|
+
* Uses the union of heuristic and annotation-based path extraction
|
|
227
|
+
* for defense-in-depth. Returns a StructuralResult with either a
|
|
228
|
+
* final decision or a set of roles resolved by sandbox containment.
|
|
229
|
+
*/
|
|
230
|
+
evaluateStructuralInvariants(request) {
|
|
231
|
+
// Extract paths using both methods for defense-in-depth
|
|
232
|
+
const heuristicPaths = extractPathsHeuristic(request.arguments);
|
|
233
|
+
const annotation = this.annotationMap.get(`${request.serverName}__${request.toolName}`);
|
|
234
|
+
// Phase 1a/1b use path-category roles only (not URL roles)
|
|
235
|
+
const pathRoles = getPathRoles();
|
|
236
|
+
const annotatedPaths = annotation
|
|
237
|
+
? extractAnnotatedPaths(request.arguments, annotation, pathRoles)
|
|
238
|
+
: [];
|
|
239
|
+
// Union of both extraction methods, deduplicated
|
|
240
|
+
const allPaths = [...new Set([...heuristicPaths, ...annotatedPaths])];
|
|
241
|
+
const resolvedPaths = allPaths.map(p => resolveRealPath(p));
|
|
242
|
+
// Phase 1a: Protected paths -- any match is an immediate deny
|
|
243
|
+
for (const rp of resolvedPaths) {
|
|
244
|
+
if (isProtectedPath(rp, this.protectedPaths)) {
|
|
245
|
+
return finalDecision({
|
|
246
|
+
decision: 'deny',
|
|
247
|
+
rule: 'structural-protected-path',
|
|
248
|
+
reason: `Access to protected path is forbidden: ${rp}`,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Phase 1b: Sandbox containment checks (path-category roles)
|
|
253
|
+
//
|
|
254
|
+
// The sandbox auto-allow decision uses annotated paths when annotations
|
|
255
|
+
// exist (not heuristic paths). An arg annotated as 'none' should not
|
|
256
|
+
// grant sandbox containment even if the value looks like a path.
|
|
257
|
+
// Heuristic paths are only used for defense-in-depth on the deny side
|
|
258
|
+
// (Phase 1a protected path check).
|
|
259
|
+
//
|
|
260
|
+
// Only SANDBOX_SAFE_PATH_ROLES (read-path, write-path, delete-path) can
|
|
261
|
+
// be auto-resolved by sandbox containment. Higher-risk path roles like
|
|
262
|
+
// write-history and delete-history always fall through to Phase 2.
|
|
263
|
+
const sandboxResolvedRoles = new Set();
|
|
264
|
+
const resolvedSandboxPaths = annotation
|
|
265
|
+
? annotatedPaths.map(p => resolveRealPath(p))
|
|
266
|
+
: resolvedPaths;
|
|
267
|
+
// Extract URL args once for use in both Phase 1b (fast-path guard) and Phase 1c
|
|
268
|
+
const urlArgs = annotation
|
|
269
|
+
? extractAnnotatedUrls(request.arguments, annotation, getUrlRoles())
|
|
270
|
+
: [];
|
|
271
|
+
// Determine if the tool has any non-sandbox-safe path roles
|
|
272
|
+
const toolHasUnsafePathRoles = annotation
|
|
273
|
+
? pathRoles.some(role => !SANDBOX_SAFE_PATH_ROLES.has(role) &&
|
|
274
|
+
Object.values(annotation.args).some(argRoles => argRoles.includes(role)))
|
|
275
|
+
: false;
|
|
276
|
+
if (this.allowedDirectory && resolvedSandboxPaths.length > 0) {
|
|
277
|
+
// Fast path: all annotated paths within sandbox -> auto-allow
|
|
278
|
+
// Only fires when ALL path roles are sandbox-safe and no URL roles need checking
|
|
279
|
+
const allWithinSandbox = resolvedSandboxPaths.every(rp => isWithinDirectory(rp, this.allowedDirectory));
|
|
280
|
+
if (allWithinSandbox && urlArgs.length === 0 && !toolHasUnsafePathRoles) {
|
|
281
|
+
return finalDecision({
|
|
282
|
+
decision: 'allow',
|
|
283
|
+
rule: 'structural-sandbox-allow',
|
|
284
|
+
reason: `All paths are within the sandbox directory: ${this.allowedDirectory}`,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
// Partial sandbox resolution: check each path role independently.
|
|
288
|
+
// A role is "sandbox-resolved" if every path for that role is within
|
|
289
|
+
// the sandbox. Only sandbox-safe roles can be resolved here.
|
|
290
|
+
// Roles with zero extracted paths are not resolved.
|
|
291
|
+
if (annotation) {
|
|
292
|
+
for (const role of pathRoles) {
|
|
293
|
+
if (!SANDBOX_SAFE_PATH_ROLES.has(role))
|
|
294
|
+
continue;
|
|
295
|
+
const pathsForRole = extractAnnotatedPaths(request.arguments, annotation, [role]);
|
|
296
|
+
if (pathsForRole.length > 0 && pathsForRole.every(p => isWithinDirectory(p, this.allowedDirectory))) {
|
|
297
|
+
sandboxResolvedRoles.add(role);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Phase 1c: Domain allowlist for URL-category roles
|
|
303
|
+
if (urlArgs.length > 0) {
|
|
304
|
+
const allowlist = this.serverDomainAllowlists.get(request.serverName);
|
|
305
|
+
if (allowlist) {
|
|
306
|
+
for (const { value, roleDef } of urlArgs) {
|
|
307
|
+
const domain = resolveUrlForDomainCheck(value, roleDef, request.arguments);
|
|
308
|
+
if (!domainMatchesAllowlist(domain, allowlist)) {
|
|
309
|
+
return finalDecision({
|
|
310
|
+
decision: 'escalate',
|
|
311
|
+
rule: 'structural-domain-escalate',
|
|
312
|
+
reason: `URL domain "${domain}" is not in the allowlist for server "${request.serverName}"`,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// All URLs passed domain check -- mark URL roles as resolved
|
|
317
|
+
for (const { role } of urlArgs) {
|
|
318
|
+
sandboxResolvedRoles.add(role);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// If no allowlist, URL roles are not structurally restricted -- fall through to Phase 2
|
|
322
|
+
}
|
|
323
|
+
// Phase 1d: Unknown tool check
|
|
324
|
+
if (!annotation) {
|
|
325
|
+
return finalDecision({
|
|
326
|
+
decision: 'deny',
|
|
327
|
+
rule: 'structural-unknown-tool',
|
|
328
|
+
reason: `Unknown tool: ${request.serverName}/${request.toolName}`,
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return { sandboxResolvedRoles: sandboxResolvedRoles.size > 0 ? sandboxResolvedRoles : NO_ROLES_RESOLVED };
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Phase 2: Evaluate compiled declarative rules.
|
|
335
|
+
*
|
|
336
|
+
* For tools with multiple roles (e.g., edit_file has read-path + write-path),
|
|
337
|
+
* each role is evaluated independently through the rule chain. The most
|
|
338
|
+
* restrictive result across all roles wins (deny > escalate > allow).
|
|
339
|
+
* Roles already resolved by Phase 1 sandbox containment are skipped.
|
|
340
|
+
*
|
|
341
|
+
* For tools with no roles (e.g., list_allowed_directories), the chain is
|
|
342
|
+
* evaluated once without role filtering.
|
|
343
|
+
*/
|
|
344
|
+
evaluateCompiledRules(request, sandboxResolvedRoles) {
|
|
345
|
+
const annotation = this.annotationMap.get(`${request.serverName}__${request.toolName}`);
|
|
346
|
+
const allRoles = collectDistinctRoles(annotation);
|
|
347
|
+
// Filter out roles already resolved by sandbox containment
|
|
348
|
+
const rolesToEvaluate = allRoles.filter(r => !sandboxResolvedRoles.has(r));
|
|
349
|
+
// All resource roles were sandbox-resolved → allow
|
|
350
|
+
if (allRoles.length > 0 && rolesToEvaluate.length === 0) {
|
|
351
|
+
return {
|
|
352
|
+
decision: 'allow',
|
|
353
|
+
rule: 'structural-sandbox-allow',
|
|
354
|
+
reason: 'All path roles resolved by sandbox containment',
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
if (rolesToEvaluate.length === 0) {
|
|
358
|
+
// No resource roles at all (e.g., list_allowed_directories)
|
|
359
|
+
return this.evaluateRulesForRole(request, annotation, undefined);
|
|
360
|
+
}
|
|
361
|
+
let mostRestrictive;
|
|
362
|
+
for (const role of rolesToEvaluate) {
|
|
363
|
+
const result = this.evaluateRulesForRole(request, annotation, role);
|
|
364
|
+
if (result.decision === 'deny')
|
|
365
|
+
return result;
|
|
366
|
+
if (!mostRestrictive || DECISION_SEVERITY[result.decision] > DECISION_SEVERITY[mostRestrictive.decision]) {
|
|
367
|
+
mostRestrictive = result;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return mostRestrictive;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Evaluates the rule chain for a single role (or all roles if undefined).
|
|
374
|
+
*
|
|
375
|
+
* When evaluatingRole is set, only rules that are either role-agnostic
|
|
376
|
+
* (no roles/paths conditions) or relevant to the specified role are
|
|
377
|
+
* considered. First matching rule wins; default deny if none match.
|
|
378
|
+
*
|
|
379
|
+
* For roles with multiple extracted paths, delegates to
|
|
380
|
+
* evaluateRulesForMultiPaths for per-element evaluation.
|
|
381
|
+
*/
|
|
382
|
+
evaluateRulesForRole(request, annotation, evaluatingRole) {
|
|
383
|
+
// Per-element evaluation: when a role has multiple paths, each path
|
|
384
|
+
// is independently discharged by the first matching rule.
|
|
385
|
+
if (evaluatingRole !== undefined) {
|
|
386
|
+
const rolePaths = extractAnnotatedPaths(request.arguments, annotation, [evaluatingRole]);
|
|
387
|
+
if (rolePaths.length > 1) {
|
|
388
|
+
return this.evaluateRulesForMultiPaths(request, annotation, evaluatingRole, rolePaths);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
for (const rule of this.compiledPolicy.rules) {
|
|
392
|
+
if (evaluatingRole !== undefined && hasRoleConditions(rule) && !ruleRelevantToRole(rule, evaluatingRole)) {
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (this.ruleMatches(rule, request, annotation)) {
|
|
396
|
+
return {
|
|
397
|
+
decision: rule.then,
|
|
398
|
+
rule: rule.name,
|
|
399
|
+
reason: rule.reason,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
decision: 'deny',
|
|
405
|
+
rule: 'default-deny',
|
|
406
|
+
reason: 'No matching policy rule -- denied by default',
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Per-element path evaluation for roles with multiple paths.
|
|
411
|
+
*
|
|
412
|
+
* Each path is independently "discharged" by the first rule whose
|
|
413
|
+
* paths.within contains it. Rules without path conditions match all
|
|
414
|
+
* remaining paths. The most restrictive decision across all discharged
|
|
415
|
+
* paths wins (deny > escalate > allow). Undischarged paths default-deny.
|
|
416
|
+
*/
|
|
417
|
+
evaluateRulesForMultiPaths(request, annotation, role, paths) {
|
|
418
|
+
const remainingPaths = new Set(paths);
|
|
419
|
+
let mostRestrictive;
|
|
420
|
+
for (const rule of this.compiledPolicy.rules) {
|
|
421
|
+
if (remainingPaths.size === 0)
|
|
422
|
+
break;
|
|
423
|
+
// Skip rules not relevant to this role
|
|
424
|
+
if (hasRoleConditions(rule) && !ruleRelevantToRole(rule, role))
|
|
425
|
+
continue;
|
|
426
|
+
// Skip rules whose non-path conditions don't match
|
|
427
|
+
if (!this.ruleMatchesNonPathConditions(rule, request, annotation))
|
|
428
|
+
continue;
|
|
429
|
+
const cond = rule.if;
|
|
430
|
+
let matched;
|
|
431
|
+
if (cond.paths !== undefined) {
|
|
432
|
+
// Rule has a path condition: discharge only paths within the directory
|
|
433
|
+
matched = [];
|
|
434
|
+
for (const p of remainingPaths) {
|
|
435
|
+
if (isWithinDirectory(p, cond.paths.within)) {
|
|
436
|
+
matched.push(p);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
if (matched.length === 0)
|
|
440
|
+
continue;
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
// Rule has no path condition: matches all remaining paths
|
|
444
|
+
matched = [...remainingPaths];
|
|
445
|
+
}
|
|
446
|
+
// Discharge matched paths and record decision
|
|
447
|
+
for (const p of matched)
|
|
448
|
+
remainingPaths.delete(p);
|
|
449
|
+
const result = {
|
|
450
|
+
decision: rule.then,
|
|
451
|
+
rule: rule.name,
|
|
452
|
+
reason: rule.reason,
|
|
453
|
+
};
|
|
454
|
+
if (!mostRestrictive || DECISION_SEVERITY[result.decision] > DECISION_SEVERITY[mostRestrictive.decision]) {
|
|
455
|
+
mostRestrictive = result;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
// Any undischarged paths -> default-deny (deny is the most restrictive decision)
|
|
459
|
+
if (remainingPaths.size > 0) {
|
|
460
|
+
return {
|
|
461
|
+
decision: 'deny',
|
|
462
|
+
rule: 'default-deny',
|
|
463
|
+
reason: 'No matching policy rule -- denied by default',
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
return mostRestrictive;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Checks non-path conditions in a rule's `if` block: roles, server, tool, sideEffects.
|
|
470
|
+
*/
|
|
471
|
+
ruleMatchesNonPathConditions(rule, request, annotation) {
|
|
472
|
+
const cond = rule.if;
|
|
473
|
+
if (cond.roles !== undefined) {
|
|
474
|
+
const toolHasMatchingRole = Object.values(annotation.args).some(argRoles => argRoles.some(r => cond.roles.includes(r)));
|
|
475
|
+
if (!toolHasMatchingRole)
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
if (cond.server !== undefined && !cond.server.includes(request.serverName)) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
if (cond.tool !== undefined && !cond.tool.includes(request.toolName)) {
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
if (cond.sideEffects !== undefined && annotation.sideEffects !== cond.sideEffects) {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
return true;
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* Checks whether all conditions in a rule's `if` block are satisfied.
|
|
491
|
+
*/
|
|
492
|
+
ruleMatches(rule, request, annotation) {
|
|
493
|
+
if (!this.ruleMatchesNonPathConditions(rule, request, annotation))
|
|
494
|
+
return false;
|
|
495
|
+
// Check paths condition
|
|
496
|
+
const cond = rule.if;
|
|
497
|
+
if (cond.paths !== undefined) {
|
|
498
|
+
const extracted = extractAnnotatedPaths(request.arguments, annotation, cond.paths.roles);
|
|
499
|
+
// Zero paths extracted = condition not satisfied, rule does not match
|
|
500
|
+
if (extracted.length === 0)
|
|
501
|
+
return false;
|
|
502
|
+
// isWithinDirectory resolves paths internally, no pre-resolution needed
|
|
503
|
+
const allWithin = extracted.every(p => isWithinDirectory(p, cond.paths.within));
|
|
504
|
+
if (!allWithin)
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
// Check domains condition
|
|
508
|
+
if (cond.domains !== undefined) {
|
|
509
|
+
const urlArgs = extractAnnotatedUrls(request.arguments, annotation, cond.domains.roles);
|
|
510
|
+
// Zero URL args extracted = condition not satisfied, rule does not match
|
|
511
|
+
if (urlArgs.length === 0)
|
|
512
|
+
return false;
|
|
513
|
+
const allMatch = urlArgs.every(({ value, roleDef }) => {
|
|
514
|
+
const domain = resolveUrlForDomainCheck(value, roleDef, request.arguments);
|
|
515
|
+
return domainMatchesAllowlist(domain, cond.domains.allowed);
|
|
516
|
+
});
|
|
517
|
+
if (!allMatch)
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
//# sourceMappingURL=policy-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-engine.js","sourceRoot":"","sources":["../../src/trusted-process/policy-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { CompiledPolicyFile } from '../pipeline/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Root entry for the MCP Roots protocol.
|
|
4
|
+
* Mirrors the MCP SDK's Root type without importing it directly,
|
|
5
|
+
* keeping this module free of SDK dependencies.
|
|
6
|
+
*/
|
|
7
|
+
export interface PolicyRoot {
|
|
8
|
+
/** Absolute directory path (not a file:// URI yet -- callers convert). */
|
|
9
|
+
readonly path: string;
|
|
10
|
+
/** Human-readable label for debugging and audit logs. */
|
|
11
|
+
readonly name: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Extracts the set of directories that the compiled policy references
|
|
15
|
+
* in `allow` or `escalate` rules with `paths.within` conditions.
|
|
16
|
+
* These directories, plus the sandbox, form the initial roots that
|
|
17
|
+
* MCP servers should accept.
|
|
18
|
+
*
|
|
19
|
+
* `deny` rules are excluded -- they never grant access, so exposing
|
|
20
|
+
* denied directories as roots would widen the server-side boundary
|
|
21
|
+
* without purpose.
|
|
22
|
+
*
|
|
23
|
+
* Catch-all rules without `paths.within` (like
|
|
24
|
+
* `escalate-read-outside-permitted-areas`) are also excluded from
|
|
25
|
+
* initial roots. Those are handled dynamically: when a human approves
|
|
26
|
+
* an escalation, the target directory is added as a root at that time.
|
|
27
|
+
*
|
|
28
|
+
* @param compiledPolicy - The loaded compiled policy artifact.
|
|
29
|
+
* @param allowedDirectory - The sandbox directory (always included).
|
|
30
|
+
* @returns Deduplicated array of PolicyRoot entries, sandbox first.
|
|
31
|
+
*/
|
|
32
|
+
export declare function extractPolicyRoots(compiledPolicy: CompiledPolicyFile, allowedDirectory: string): PolicyRoot[];
|
|
33
|
+
/**
|
|
34
|
+
* Converts PolicyRoot entries to MCP Root objects with `file://` URIs.
|
|
35
|
+
*/
|
|
36
|
+
export declare function toMcpRoots(policyRoots: PolicyRoot[]): Array<{
|
|
37
|
+
uri: string;
|
|
38
|
+
name: string;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* Extracts the containing directory for a filesystem path.
|
|
42
|
+
* Used to derive the root directory when a human approves an
|
|
43
|
+
* escalation -- the approved path's parent directory becomes a root
|
|
44
|
+
* so the filesystem server will accept the forwarded call.
|
|
45
|
+
*
|
|
46
|
+
* If the path ends with a trailing slash, it is treated as a directory
|
|
47
|
+
* and returned as-is (without the trailing slash) after resolution.
|
|
48
|
+
* Otherwise dirname is used to get the containing directory.
|
|
49
|
+
*/
|
|
50
|
+
export declare function directoryForPath(filePath: string): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { dirname } from 'node:path';
|
|
2
|
+
import { resolveRealPath } from '../types/argument-roles.js';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts the set of directories that the compiled policy references
|
|
5
|
+
* in `allow` or `escalate` rules with `paths.within` conditions.
|
|
6
|
+
* These directories, plus the sandbox, form the initial roots that
|
|
7
|
+
* MCP servers should accept.
|
|
8
|
+
*
|
|
9
|
+
* `deny` rules are excluded -- they never grant access, so exposing
|
|
10
|
+
* denied directories as roots would widen the server-side boundary
|
|
11
|
+
* without purpose.
|
|
12
|
+
*
|
|
13
|
+
* Catch-all rules without `paths.within` (like
|
|
14
|
+
* `escalate-read-outside-permitted-areas`) are also excluded from
|
|
15
|
+
* initial roots. Those are handled dynamically: when a human approves
|
|
16
|
+
* an escalation, the target directory is added as a root at that time.
|
|
17
|
+
*
|
|
18
|
+
* @param compiledPolicy - The loaded compiled policy artifact.
|
|
19
|
+
* @param allowedDirectory - The sandbox directory (always included).
|
|
20
|
+
* @returns Deduplicated array of PolicyRoot entries, sandbox first.
|
|
21
|
+
*/
|
|
22
|
+
export function extractPolicyRoots(compiledPolicy, allowedDirectory) {
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
const roots = [];
|
|
25
|
+
// Sandbox is always the first root.
|
|
26
|
+
const resolvedSandbox = resolveRealPath(allowedDirectory);
|
|
27
|
+
seen.add(resolvedSandbox);
|
|
28
|
+
roots.push({ path: resolvedSandbox, name: 'sandbox' });
|
|
29
|
+
for (const rule of compiledPolicy.rules) {
|
|
30
|
+
if (rule.then === 'deny')
|
|
31
|
+
continue;
|
|
32
|
+
if (!rule.if.paths?.within)
|
|
33
|
+
continue;
|
|
34
|
+
const dir = resolveRealPath(rule.if.paths.within);
|
|
35
|
+
if (seen.has(dir))
|
|
36
|
+
continue;
|
|
37
|
+
seen.add(dir);
|
|
38
|
+
roots.push({ path: dir, name: rule.name });
|
|
39
|
+
}
|
|
40
|
+
return roots;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Converts PolicyRoot entries to MCP Root objects with `file://` URIs.
|
|
44
|
+
*/
|
|
45
|
+
export function toMcpRoots(policyRoots) {
|
|
46
|
+
return policyRoots.map(r => ({
|
|
47
|
+
uri: `file://${r.path}`,
|
|
48
|
+
name: r.name,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Extracts the containing directory for a filesystem path.
|
|
53
|
+
* Used to derive the root directory when a human approves an
|
|
54
|
+
* escalation -- the approved path's parent directory becomes a root
|
|
55
|
+
* so the filesystem server will accept the forwarded call.
|
|
56
|
+
*
|
|
57
|
+
* If the path ends with a trailing slash, it is treated as a directory
|
|
58
|
+
* and returned as-is (without the trailing slash) after resolution.
|
|
59
|
+
* Otherwise dirname is used to get the containing directory.
|
|
60
|
+
*/
|
|
61
|
+
export function directoryForPath(filePath) {
|
|
62
|
+
// Check for trailing slash before resolving, since resolveRealPath() strips it.
|
|
63
|
+
if (filePath.endsWith('/'))
|
|
64
|
+
return resolveRealPath(filePath);
|
|
65
|
+
return dirname(resolveRealPath(filePath));
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=policy-roots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-roots.js","sourceRoot":"","sources":["../../src/trusted-process/policy-roots.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAc7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,kBAAkB,CAChC,cAAkC,EAClC,gBAAwB;IAExB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,KAAK,GAAiB,EAAE,CAAC;IAE/B,oCAAoC;IACpC,MAAM,eAAe,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;IAC1D,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;IAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;IAEvD,KAAK,MAAM,IAAI,IAAI,cAAc,CAAC,KAAK,EAAE,CAAC;QACxC,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM;YAAE,SAAS;QACnC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM;YAAE,SAAS;QAErC,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAClD,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAE5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CACxB,WAAyB;IAEzB,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3B,GAAG,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE;QACvB,IAAI,EAAE,CAAC,CAAC,IAAI;KACb,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,gFAAgF;IAChF,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAC;IAC7D,OAAO,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC5C,CAAC"}
|