@solongate/policy-engine 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.
@@ -0,0 +1,246 @@
1
+ import { PolicySet, PolicyRule, ExecutionRequest, PolicyDecision, TrustLevel } from '@solongate/core';
2
+
3
+ interface ValidationResult {
4
+ readonly valid: boolean;
5
+ readonly errors: readonly string[];
6
+ readonly warnings: readonly string[];
7
+ }
8
+ declare function validatePolicyRule(input: unknown): ValidationResult;
9
+ declare function validatePolicySet(input: unknown): ValidationResult;
10
+
11
+ interface SecurityWarning {
12
+ readonly level: 'WARNING' | 'CRITICAL';
13
+ readonly code: string;
14
+ readonly message: string;
15
+ readonly ruleId?: string;
16
+ readonly recommendation: string;
17
+ }
18
+ /** Analyzes a policy set and returns security warnings. Pure function. */
19
+ declare function analyzeSecurityWarnings(policySet: PolicySet): readonly SecurityWarning[];
20
+
21
+ /**
22
+ * A versioned snapshot of a policy set.
23
+ * Immutable once created - modifications create new versions.
24
+ */
25
+ interface PolicyVersion {
26
+ readonly version: number;
27
+ readonly policySet: PolicySet;
28
+ readonly hash: string;
29
+ readonly reason: string;
30
+ readonly createdBy: string;
31
+ readonly createdAt: string;
32
+ }
33
+ /**
34
+ * Diff between two policy versions.
35
+ */
36
+ interface PolicyDiff {
37
+ readonly added: readonly PolicyRule[];
38
+ readonly removed: readonly PolicyRule[];
39
+ readonly modified: readonly {
40
+ readonly old: PolicyRule;
41
+ readonly new: PolicyRule;
42
+ }[];
43
+ }
44
+ /**
45
+ * In-memory versioned policy store.
46
+ * Stores complete history of policy changes with cryptographic hashes.
47
+ *
48
+ * Security properties:
49
+ * - Immutable versions: once saved, a version cannot be modified
50
+ * - Hash chain: each version includes SHA256 of the policy content
51
+ * - Full history: no version is ever deleted
52
+ */
53
+ declare class PolicyStore {
54
+ private readonly versions;
55
+ /**
56
+ * Saves a new version of a policy set.
57
+ * The version number auto-increments.
58
+ */
59
+ saveVersion(policySet: PolicySet, reason: string, createdBy: string): PolicyVersion;
60
+ /**
61
+ * Gets a specific version of a policy set.
62
+ */
63
+ getVersion(id: string, version: number): PolicyVersion | null;
64
+ /**
65
+ * Gets the latest version of a policy set.
66
+ */
67
+ getLatest(id: string): PolicyVersion | null;
68
+ /**
69
+ * Gets the full version history of a policy set.
70
+ */
71
+ getHistory(id: string): readonly PolicyVersion[];
72
+ /**
73
+ * Rolls back to a previous version by creating a new version
74
+ * with the same content as the target version.
75
+ */
76
+ rollback(id: string, toVersion: number): PolicyVersion;
77
+ /**
78
+ * Computes a diff between two policy versions.
79
+ */
80
+ diff(v1: PolicyVersion, v2: PolicyVersion): PolicyDiff;
81
+ /**
82
+ * Computes SHA256 hash of a policy set for integrity verification.
83
+ */
84
+ computeHash(policySet: PolicySet): string;
85
+ }
86
+
87
+ /**
88
+ * PolicyEngine is the primary interface for policy evaluation.
89
+ *
90
+ * Wraps pure evaluation functions with:
91
+ * - Policy set management (load, validate, swap)
92
+ * - Timeout protection
93
+ * - Warning aggregation
94
+ * - Optional versioned policy store
95
+ */
96
+ declare class PolicyEngine {
97
+ private policySet;
98
+ private readonly timeoutMs;
99
+ private readonly store;
100
+ constructor(options?: {
101
+ policySet?: PolicySet;
102
+ timeoutMs?: number;
103
+ store?: PolicyStore;
104
+ });
105
+ /**
106
+ * Evaluates an execution request against the current policy set.
107
+ * Never throws for denials - denial is a normal outcome, not an error.
108
+ */
109
+ evaluate(request: ExecutionRequest): PolicyDecision;
110
+ /**
111
+ * Loads a new policy set, replacing the current one.
112
+ * Validates before accepting. Auto-saves version when store is present.
113
+ */
114
+ loadPolicySet(policySet: PolicySet, options?: {
115
+ reason?: string;
116
+ createdBy?: string;
117
+ }): ValidationResult;
118
+ /**
119
+ * Rolls back to a previous policy version.
120
+ * Only available when a PolicyStore is configured.
121
+ */
122
+ rollback(version: number): PolicyVersion;
123
+ getPolicySet(): Readonly<PolicySet>;
124
+ getSecurityWarnings(): readonly SecurityWarning[];
125
+ getStore(): PolicyStore | null;
126
+ reset(): void;
127
+ }
128
+
129
+ /**
130
+ * Evaluates a policy set against an execution request.
131
+ *
132
+ * Pure function: no side effects, no I/O, fully deterministic.
133
+ *
134
+ * Algorithm:
135
+ * 1. Sort rules by priority (ascending - lower number = higher priority)
136
+ * 2. Find the first matching rule
137
+ * 3. If a rule matches, return its effect
138
+ * 4. If no rule matches, return DENY (default-deny)
139
+ */
140
+ declare function evaluatePolicy(policySet: PolicySet, request: ExecutionRequest): PolicyDecision;
141
+
142
+ /**
143
+ * Pure function: determines if a policy rule matches an execution request.
144
+ * No side effects. No I/O. Fully deterministic.
145
+ */
146
+ declare function ruleMatchesRequest(rule: PolicyRule, request: ExecutionRequest): boolean;
147
+ /**
148
+ * Glob-style tool name pattern matching.
149
+ * Supports:
150
+ * '*' → match all
151
+ * 'prefix*' → starts with prefix
152
+ * '*suffix' → ends with suffix
153
+ * '*infix*' → contains infix
154
+ * Does NOT support regex (ReDoS prevention).
155
+ */
156
+ declare function toolPatternMatches(pattern: string, toolName: string): boolean;
157
+ declare function trustLevelMeetsMinimum(actual: TrustLevel, minimum: TrustLevel): boolean;
158
+
159
+ /**
160
+ * Creates the default "deny all" policy set.
161
+ * This is the starting policy for any new SolonGate deployment.
162
+ */
163
+ declare function createDefaultDenyPolicySet(): PolicySet;
164
+ /**
165
+ * Creates a permissive "allow all" policy set.
166
+ * Allows all tool executions — useful for development or when
167
+ * using SolonGate only for monitoring and audit logging.
168
+ */
169
+ declare function createPermissivePolicySet(): PolicySet;
170
+ /**
171
+ * Creates a read-only policy set for a specific tool pattern.
172
+ * Allows reads for VERIFIED requests only.
173
+ */
174
+ declare function createReadOnlyPolicySet(toolPattern: string): PolicySet;
175
+ /**
176
+ * Creates a sandboxed policy set for a given root directory.
177
+ * Allows file operations within rootDir, blocks dangerous commands,
178
+ * denies access to sensitive files.
179
+ */
180
+ declare function createSandboxedPolicySet(rootDir: string): PolicySet;
181
+
182
+ type PathConstraints = NonNullable<PolicyRule['pathConstraints']>;
183
+ /**
184
+ * Normalizes a file path for consistent matching.
185
+ * Resolves . and .. segments, normalizes separators.
186
+ */
187
+ declare function normalizePath(path: string): string;
188
+ /**
189
+ * Checks if a path is within a root directory (sandbox boundary).
190
+ * Prevents escaping via .., symlinks, etc.
191
+ */
192
+ declare function isWithinRoot(path: string, root: string): boolean;
193
+ /**
194
+ * Glob-style path pattern matching.
195
+ * Supports:
196
+ * - * matches any single path segment (not /)
197
+ * - ** matches any number of path segments
198
+ * - Exact match
199
+ *
200
+ * Does NOT support regex (ReDoS prevention).
201
+ */
202
+ declare function matchPathPattern(path: string, pattern: string): boolean;
203
+ /**
204
+ * Checks if a path is allowed by the given constraints.
205
+ *
206
+ * Evaluation order:
207
+ * 1. If rootDirectory is set, path must be within it
208
+ * 2. If denied list exists, path must NOT match any denied pattern
209
+ * 3. If allowed list exists, path must match at least one allowed pattern
210
+ * 4. If neither list exists, path is allowed (constraints are optional)
211
+ */
212
+ declare function isPathAllowed(path: string, constraints: PathConstraints): boolean;
213
+ /**
214
+ * Extracts path-like arguments from tool call arguments.
215
+ * Heuristic: any string argument containing / or \ is treated as a path.
216
+ */
217
+ declare function extractPathArguments(args: Readonly<Record<string, unknown>>): string[];
218
+
219
+ type CommandConstraints = NonNullable<PolicyRule['commandConstraints']>;
220
+ /**
221
+ * Extracts command-like arguments from tool call arguments.
222
+ * Looks for known field names and string values that look like commands.
223
+ */
224
+ declare function extractCommandArguments(args: Readonly<Record<string, unknown>>): string[];
225
+ /**
226
+ * Glob-style command pattern matching.
227
+ * Matches against the command string (first word) or full command line.
228
+ *
229
+ * Patterns:
230
+ * 'ls' → exact match on command name
231
+ * 'git*' → command starts with 'git'
232
+ * '*sql*' → command contains 'sql'
233
+ * 'rm -rf *' → full command line starts with 'rm -rf '
234
+ */
235
+ declare function matchCommandPattern(command: string, pattern: string): boolean;
236
+ /**
237
+ * Checks if a command is allowed by the given constraints.
238
+ *
239
+ * Evaluation order:
240
+ * 1. If denied list exists, command must NOT match any denied pattern
241
+ * 2. If allowed list exists, command must match at least one allowed pattern
242
+ * 3. If neither list exists, command is allowed (constraints are optional)
243
+ */
244
+ declare function isCommandAllowed(command: string, constraints: CommandConstraints): boolean;
245
+
246
+ export { type PolicyDiff, PolicyEngine, PolicyStore, type PolicyVersion, type SecurityWarning, type ValidationResult, analyzeSecurityWarnings, createDefaultDenyPolicySet, createPermissivePolicySet, createReadOnlyPolicySet, createSandboxedPolicySet, evaluatePolicy, extractCommandArguments, extractPathArguments, isCommandAllowed, isPathAllowed, isWithinRoot, matchCommandPattern, matchPathPattern, normalizePath, ruleMatchesRequest, toolPatternMatches, trustLevelMeetsMinimum, validatePolicyRule, validatePolicySet };
package/dist/index.js ADDED
@@ -0,0 +1,843 @@
1
+ import { TrustLevel, DEFAULT_POLICY_EFFECT, PolicyRuleSchema, UNSAFE_CONFIGURATION_WARNINGS, PolicySetSchema, MAX_RULES_PER_POLICY_SET, Permission, PolicyEffect, POLICY_EVALUATION_TIMEOUT_MS } from '@solongate/core';
2
+ import { createHash } from 'crypto';
3
+
4
+ // src/engine.ts
5
+
6
+ // src/path-matcher.ts
7
+ function normalizePath(path) {
8
+ let normalized = path.replace(/\\/g, "/");
9
+ if (normalized.length > 1 && normalized.endsWith("/")) {
10
+ normalized = normalized.slice(0, -1);
11
+ }
12
+ const parts = normalized.split("/");
13
+ const resolved = [];
14
+ for (const part of parts) {
15
+ if (part === "." || part === "") {
16
+ if (resolved.length === 0) resolved.push("");
17
+ continue;
18
+ }
19
+ if (part === "..") {
20
+ if (resolved.length > 1) {
21
+ resolved.pop();
22
+ }
23
+ continue;
24
+ }
25
+ resolved.push(part);
26
+ }
27
+ return resolved.join("/") || "/";
28
+ }
29
+ function isWithinRoot(path, root) {
30
+ const normalizedPath = normalizePath(path);
31
+ const normalizedRoot = normalizePath(root);
32
+ if (normalizedPath === normalizedRoot) return true;
33
+ return normalizedPath.startsWith(normalizedRoot + "/");
34
+ }
35
+ function matchPathPattern(path, pattern) {
36
+ const normalizedPath = normalizePath(path);
37
+ const normalizedPattern = normalizePath(pattern);
38
+ if (normalizedPattern === "*") return true;
39
+ if (normalizedPattern === normalizedPath) return true;
40
+ const patternParts = normalizedPattern.split("/");
41
+ const pathParts = normalizedPath.split("/");
42
+ return matchParts(pathParts, 0, patternParts, 0);
43
+ }
44
+ function matchParts(pathParts, pi, patternParts, qi) {
45
+ while (pi < pathParts.length && qi < patternParts.length) {
46
+ const pattern = patternParts[qi];
47
+ if (pattern === "**") {
48
+ if (qi === patternParts.length - 1) return true;
49
+ for (let i = pi; i <= pathParts.length; i++) {
50
+ if (matchParts(pathParts, i, patternParts, qi + 1)) {
51
+ return true;
52
+ }
53
+ }
54
+ return false;
55
+ }
56
+ if (pattern === "*") {
57
+ pi++;
58
+ qi++;
59
+ continue;
60
+ }
61
+ if (pattern.includes("*")) {
62
+ if (!matchSegmentGlob(pathParts[pi], pattern)) {
63
+ return false;
64
+ }
65
+ pi++;
66
+ qi++;
67
+ continue;
68
+ }
69
+ if (pattern !== pathParts[pi]) {
70
+ return false;
71
+ }
72
+ pi++;
73
+ qi++;
74
+ }
75
+ while (qi < patternParts.length && patternParts[qi] === "**") {
76
+ qi++;
77
+ }
78
+ return pi === pathParts.length && qi === patternParts.length;
79
+ }
80
+ function isPathAllowed(path, constraints) {
81
+ if (constraints.rootDirectory) {
82
+ if (!isWithinRoot(path, constraints.rootDirectory)) {
83
+ return false;
84
+ }
85
+ }
86
+ if (constraints.denied && constraints.denied.length > 0) {
87
+ for (const pattern of constraints.denied) {
88
+ if (matchPathPattern(path, pattern)) {
89
+ return false;
90
+ }
91
+ }
92
+ }
93
+ if (constraints.allowed && constraints.allowed.length > 0) {
94
+ let matchesAllowed = false;
95
+ for (const pattern of constraints.allowed) {
96
+ if (matchPathPattern(path, pattern)) {
97
+ matchesAllowed = true;
98
+ break;
99
+ }
100
+ }
101
+ if (!matchesAllowed) return false;
102
+ }
103
+ return true;
104
+ }
105
+ function matchSegmentGlob(segment, pattern) {
106
+ const startsWithStar = pattern.startsWith("*");
107
+ const endsWithStar = pattern.endsWith("*");
108
+ if (pattern === "*") return true;
109
+ if (startsWithStar && endsWithStar) {
110
+ const infix = pattern.slice(1, -1);
111
+ return segment.toLowerCase().includes(infix.toLowerCase());
112
+ }
113
+ if (startsWithStar) {
114
+ const suffix = pattern.slice(1);
115
+ return segment.toLowerCase().endsWith(suffix.toLowerCase());
116
+ }
117
+ if (endsWithStar) {
118
+ const prefix = pattern.slice(0, -1);
119
+ return segment.toLowerCase().startsWith(prefix.toLowerCase());
120
+ }
121
+ const starIdx = pattern.indexOf("*");
122
+ if (starIdx !== -1) {
123
+ const prefix = pattern.slice(0, starIdx);
124
+ const suffix = pattern.slice(starIdx + 1);
125
+ const seg = segment.toLowerCase();
126
+ return seg.startsWith(prefix.toLowerCase()) && seg.endsWith(suffix.toLowerCase()) && seg.length >= prefix.length + suffix.length;
127
+ }
128
+ return segment === pattern;
129
+ }
130
+ function extractPathArguments(args) {
131
+ const paths = [];
132
+ for (const value of Object.values(args)) {
133
+ if (typeof value === "string" && (value.includes("/") || value.includes("\\"))) {
134
+ paths.push(value);
135
+ }
136
+ }
137
+ return paths;
138
+ }
139
+
140
+ // src/command-matcher.ts
141
+ var COMMAND_FIELDS = /* @__PURE__ */ new Set([
142
+ "command",
143
+ "cmd",
144
+ "query",
145
+ "code",
146
+ "script",
147
+ "shell",
148
+ "exec",
149
+ "sql",
150
+ "expression"
151
+ ]);
152
+ function extractCommandArguments(args) {
153
+ const commands = [];
154
+ for (const [key, value] of Object.entries(args)) {
155
+ if (typeof value !== "string") continue;
156
+ if (COMMAND_FIELDS.has(key.toLowerCase())) {
157
+ commands.push(value);
158
+ }
159
+ }
160
+ return commands;
161
+ }
162
+ function matchCommandPattern(command, pattern) {
163
+ if (pattern === "*") return true;
164
+ const normalizedCommand = command.trim().toLowerCase();
165
+ const normalizedPattern = pattern.trim().toLowerCase();
166
+ if (normalizedPattern === normalizedCommand) return true;
167
+ const startsWithStar = normalizedPattern.startsWith("*");
168
+ const endsWithStar = normalizedPattern.endsWith("*");
169
+ if (startsWithStar && endsWithStar) {
170
+ const infix = normalizedPattern.slice(1, -1);
171
+ return infix.length > 0 && normalizedCommand.includes(infix);
172
+ }
173
+ if (endsWithStar) {
174
+ const prefix = normalizedPattern.slice(0, -1);
175
+ return normalizedCommand.startsWith(prefix);
176
+ }
177
+ if (startsWithStar) {
178
+ const suffix = normalizedPattern.slice(1);
179
+ return normalizedCommand.endsWith(suffix);
180
+ }
181
+ const commandName = normalizedCommand.split(/\s+/)[0] ?? "";
182
+ return commandName === normalizedPattern;
183
+ }
184
+ function isCommandAllowed(command, constraints) {
185
+ if (constraints.denied && constraints.denied.length > 0) {
186
+ for (const pattern of constraints.denied) {
187
+ if (matchCommandPattern(command, pattern)) {
188
+ return false;
189
+ }
190
+ }
191
+ }
192
+ if (constraints.allowed && constraints.allowed.length > 0) {
193
+ let matchesAllowed = false;
194
+ for (const pattern of constraints.allowed) {
195
+ if (matchCommandPattern(command, pattern)) {
196
+ matchesAllowed = true;
197
+ break;
198
+ }
199
+ }
200
+ if (!matchesAllowed) return false;
201
+ }
202
+ return true;
203
+ }
204
+
205
+ // src/matcher.ts
206
+ function ruleMatchesRequest(rule, request) {
207
+ if (!rule.enabled) return false;
208
+ if (rule.permission !== request.requiredPermission) return false;
209
+ if (!toolPatternMatches(rule.toolPattern, request.toolName)) return false;
210
+ if (!trustLevelMeetsMinimum(request.context.trustLevel, rule.minimumTrustLevel)) {
211
+ return false;
212
+ }
213
+ if (rule.argumentConstraints) {
214
+ if (!argumentConstraintsMatch(rule.argumentConstraints, request.arguments)) {
215
+ return false;
216
+ }
217
+ }
218
+ if (rule.pathConstraints) {
219
+ const satisfied = pathConstraintsMatch(rule.pathConstraints, request.arguments);
220
+ if (rule.effect === "DENY") {
221
+ if (satisfied) return false;
222
+ } else {
223
+ if (!satisfied) return false;
224
+ }
225
+ }
226
+ if (rule.commandConstraints) {
227
+ const satisfied = commandConstraintsMatch(rule.commandConstraints, request.arguments);
228
+ if (rule.effect === "DENY") {
229
+ if (satisfied) return false;
230
+ } else {
231
+ if (!satisfied) return false;
232
+ }
233
+ }
234
+ return true;
235
+ }
236
+ function toolPatternMatches(pattern, toolName) {
237
+ if (pattern === "*") return true;
238
+ const startsWithStar = pattern.startsWith("*");
239
+ const endsWithStar = pattern.endsWith("*");
240
+ if (startsWithStar && endsWithStar) {
241
+ const infix = pattern.slice(1, -1);
242
+ return infix.length > 0 && toolName.includes(infix);
243
+ }
244
+ if (endsWithStar) {
245
+ const prefix = pattern.slice(0, -1);
246
+ return toolName.startsWith(prefix);
247
+ }
248
+ if (startsWithStar) {
249
+ const suffix = pattern.slice(1);
250
+ return toolName.endsWith(suffix);
251
+ }
252
+ return pattern === toolName;
253
+ }
254
+ var TRUST_LEVEL_ORDER = {
255
+ [TrustLevel.UNTRUSTED]: 0,
256
+ [TrustLevel.VERIFIED]: 1,
257
+ [TrustLevel.TRUSTED]: 2
258
+ };
259
+ function trustLevelMeetsMinimum(actual, minimum) {
260
+ return (TRUST_LEVEL_ORDER[actual] ?? -1) >= (TRUST_LEVEL_ORDER[minimum] ?? Infinity);
261
+ }
262
+ function argumentConstraintsMatch(constraints, args) {
263
+ for (const [key, constraint] of Object.entries(constraints)) {
264
+ if (!(key in args)) return false;
265
+ const argValue = args[key];
266
+ if (typeof constraint === "string") {
267
+ if (constraint === "*") continue;
268
+ if (typeof argValue === "string") {
269
+ if (argValue !== constraint) return false;
270
+ } else {
271
+ return false;
272
+ }
273
+ continue;
274
+ }
275
+ if (typeof constraint === "object" && constraint !== null && !Array.isArray(constraint)) {
276
+ const ops = constraint;
277
+ const strValue = typeof argValue === "string" ? argValue : void 0;
278
+ const numValue = typeof argValue === "number" ? argValue : void 0;
279
+ if ("$contains" in ops && typeof ops.$contains === "string") {
280
+ if (!strValue || !strValue.includes(ops.$contains)) return false;
281
+ }
282
+ if ("$notContains" in ops && typeof ops.$notContains === "string") {
283
+ if (strValue && strValue.includes(ops.$notContains)) return false;
284
+ }
285
+ if ("$startsWith" in ops && typeof ops.$startsWith === "string") {
286
+ if (!strValue || !strValue.startsWith(ops.$startsWith)) return false;
287
+ }
288
+ if ("$endsWith" in ops && typeof ops.$endsWith === "string") {
289
+ if (!strValue || !strValue.endsWith(ops.$endsWith)) return false;
290
+ }
291
+ if ("$in" in ops && Array.isArray(ops.$in)) {
292
+ if (!ops.$in.includes(argValue)) return false;
293
+ }
294
+ if ("$notIn" in ops && Array.isArray(ops.$notIn)) {
295
+ if (ops.$notIn.includes(argValue)) return false;
296
+ }
297
+ if ("$gt" in ops && typeof ops.$gt === "number") {
298
+ if (numValue === void 0 || numValue <= ops.$gt) return false;
299
+ }
300
+ if ("$lt" in ops && typeof ops.$lt === "number") {
301
+ if (numValue === void 0 || numValue >= ops.$lt) return false;
302
+ }
303
+ if ("$gte" in ops && typeof ops.$gte === "number") {
304
+ if (numValue === void 0 || numValue < ops.$gte) return false;
305
+ }
306
+ if ("$lte" in ops && typeof ops.$lte === "number") {
307
+ if (numValue === void 0 || numValue > ops.$lte) return false;
308
+ }
309
+ continue;
310
+ }
311
+ }
312
+ return true;
313
+ }
314
+ function pathConstraintsMatch(constraints, args) {
315
+ const paths = extractPathArguments(args);
316
+ if (paths.length === 0) return true;
317
+ return paths.every((path) => isPathAllowed(path, constraints));
318
+ }
319
+ function commandConstraintsMatch(constraints, args) {
320
+ const commands = extractCommandArguments(args);
321
+ if (commands.length === 0) return true;
322
+ return commands.every((cmd) => isCommandAllowed(cmd, constraints));
323
+ }
324
+
325
+ // src/evaluator.ts
326
+ function evaluatePolicy(policySet, request) {
327
+ const startTime = performance.now();
328
+ const sortedRules = [...policySet.rules].sort(
329
+ (a, b) => a.priority - b.priority
330
+ );
331
+ for (const rule of sortedRules) {
332
+ if (ruleMatchesRequest(rule, request)) {
333
+ const endTime2 = performance.now();
334
+ return {
335
+ effect: rule.effect,
336
+ matchedRule: rule,
337
+ reason: `Matched rule "${rule.id}": ${rule.description}`,
338
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
339
+ evaluationTimeMs: endTime2 - startTime
340
+ };
341
+ }
342
+ }
343
+ const endTime = performance.now();
344
+ return {
345
+ effect: DEFAULT_POLICY_EFFECT,
346
+ matchedRule: null,
347
+ reason: "No matching policy rule found. Default action: DENY.",
348
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
349
+ evaluationTimeMs: endTime - startTime,
350
+ metadata: {
351
+ evaluatedRules: sortedRules.length,
352
+ ruleIds: sortedRules.map((r) => r.id),
353
+ requestContext: {
354
+ tool: request.toolName,
355
+ arguments: Object.keys(request.arguments ?? {})
356
+ }
357
+ }
358
+ };
359
+ }
360
+ function validatePolicyRule(input) {
361
+ const errors = [];
362
+ const warnings = [];
363
+ const result = PolicyRuleSchema.safeParse(input);
364
+ if (!result.success) {
365
+ return {
366
+ valid: false,
367
+ errors: result.error.errors.map(
368
+ (e) => `${e.path.join(".")}: ${e.message}`
369
+ ),
370
+ warnings: []
371
+ };
372
+ }
373
+ const rule = result.data;
374
+ if (rule.toolPattern === "*" && rule.effect === "ALLOW") {
375
+ warnings.push(UNSAFE_CONFIGURATION_WARNINGS.WILDCARD_ALLOW);
376
+ }
377
+ if (rule.minimumTrustLevel === "TRUSTED") {
378
+ warnings.push(UNSAFE_CONFIGURATION_WARNINGS.TRUSTED_LEVEL_EXTERNAL);
379
+ }
380
+ if (rule.permission === "EXECUTE") {
381
+ warnings.push(UNSAFE_CONFIGURATION_WARNINGS.EXECUTE_WITHOUT_REVIEW);
382
+ }
383
+ return { valid: true, errors, warnings };
384
+ }
385
+ function validatePolicySet(input) {
386
+ const errors = [];
387
+ const warnings = [];
388
+ const result = PolicySetSchema.safeParse(input);
389
+ if (!result.success) {
390
+ return {
391
+ valid: false,
392
+ errors: result.error.errors.map(
393
+ (e) => `${e.path.join(".")}: ${e.message}`
394
+ ),
395
+ warnings: []
396
+ };
397
+ }
398
+ const policySet = result.data;
399
+ if (policySet.rules.length > MAX_RULES_PER_POLICY_SET) {
400
+ errors.push(
401
+ `Policy set exceeds maximum of ${MAX_RULES_PER_POLICY_SET} rules`
402
+ );
403
+ }
404
+ const ruleIds = /* @__PURE__ */ new Set();
405
+ for (const rule of policySet.rules) {
406
+ if (ruleIds.has(rule.id)) {
407
+ errors.push(`Duplicate rule ID: "${rule.id}"`);
408
+ }
409
+ ruleIds.add(rule.id);
410
+ }
411
+ for (const rule of policySet.rules) {
412
+ const ruleResult = validatePolicyRule(rule);
413
+ warnings.push(...ruleResult.warnings);
414
+ }
415
+ const hasDenyRule = policySet.rules.some((r) => r.effect === "DENY");
416
+ if (!hasDenyRule && policySet.rules.length > 0) {
417
+ warnings.push(
418
+ "Policy set contains only ALLOW rules. The default-deny fallback is the only protection."
419
+ );
420
+ }
421
+ return {
422
+ valid: errors.length === 0,
423
+ errors,
424
+ warnings
425
+ };
426
+ }
427
+ function analyzeSecurityWarnings(policySet) {
428
+ const warnings = [];
429
+ for (const rule of policySet.rules) {
430
+ warnings.push(...analyzeRuleWarnings(rule));
431
+ }
432
+ const allowRules = policySet.rules.filter(
433
+ (r) => r.effect === "ALLOW" && r.enabled
434
+ );
435
+ const wildcardAllows = allowRules.filter((r) => r.toolPattern === "*");
436
+ if (wildcardAllows.length > 0) {
437
+ warnings.push({
438
+ level: "CRITICAL",
439
+ code: "WILDCARD_ALLOW",
440
+ message: UNSAFE_CONFIGURATION_WARNINGS.WILDCARD_ALLOW,
441
+ recommendation: "Replace wildcard ALLOW rules with specific tool patterns."
442
+ });
443
+ }
444
+ return warnings;
445
+ }
446
+ function analyzeRuleWarnings(rule) {
447
+ const warnings = [];
448
+ if (rule.effect === "ALLOW" && rule.minimumTrustLevel === "UNTRUSTED") {
449
+ warnings.push({
450
+ level: "CRITICAL",
451
+ code: "ALLOW_UNTRUSTED",
452
+ message: `Rule "${rule.id}" allows execution for UNTRUSTED requests. Unverified LLM requests can execute tools.`,
453
+ ruleId: rule.id,
454
+ recommendation: "Set minimumTrustLevel to VERIFIED or higher for ALLOW rules."
455
+ });
456
+ }
457
+ if (rule.effect === "ALLOW" && rule.permission === "EXECUTE") {
458
+ warnings.push({
459
+ level: "WARNING",
460
+ code: "ALLOW_EXECUTE",
461
+ message: UNSAFE_CONFIGURATION_WARNINGS.EXECUTE_WITHOUT_REVIEW,
462
+ ruleId: rule.id,
463
+ recommendation: "Ensure EXECUTE permissions are intentional and scoped to specific tools."
464
+ });
465
+ }
466
+ return warnings;
467
+ }
468
+ function createDefaultDenyPolicySet() {
469
+ const now = (/* @__PURE__ */ new Date()).toISOString();
470
+ return {
471
+ id: "default-deny",
472
+ name: "Default Deny All",
473
+ description: "Denies all tool executions. Add explicit ALLOW rules to grant access to specific tools.",
474
+ version: 1,
475
+ rules: [
476
+ {
477
+ id: "deny-all-execute",
478
+ description: "Explicitly deny all tool executions",
479
+ effect: PolicyEffect.DENY,
480
+ priority: 1e4,
481
+ toolPattern: "*",
482
+ permission: Permission.EXECUTE,
483
+ minimumTrustLevel: TrustLevel.UNTRUSTED,
484
+ enabled: true,
485
+ createdAt: now,
486
+ updatedAt: now
487
+ },
488
+ {
489
+ id: "deny-all-write",
490
+ description: "Explicitly deny all write operations",
491
+ effect: PolicyEffect.DENY,
492
+ priority: 1e4,
493
+ toolPattern: "*",
494
+ permission: Permission.WRITE,
495
+ minimumTrustLevel: TrustLevel.UNTRUSTED,
496
+ enabled: true,
497
+ createdAt: now,
498
+ updatedAt: now
499
+ },
500
+ {
501
+ id: "deny-all-read",
502
+ description: "Explicitly deny all read operations",
503
+ effect: PolicyEffect.DENY,
504
+ priority: 1e4,
505
+ toolPattern: "*",
506
+ permission: Permission.READ,
507
+ minimumTrustLevel: TrustLevel.UNTRUSTED,
508
+ enabled: true,
509
+ createdAt: now,
510
+ updatedAt: now
511
+ }
512
+ ],
513
+ createdAt: now,
514
+ updatedAt: now
515
+ };
516
+ }
517
+ function createPermissivePolicySet() {
518
+ const now = (/* @__PURE__ */ new Date()).toISOString();
519
+ return {
520
+ id: "permissive",
521
+ name: "Permissive (Allow All)",
522
+ description: "Allows all tool executions. SolonGate still provides input validation, rate limiting, and audit logging.",
523
+ version: 1,
524
+ rules: [
525
+ {
526
+ id: "allow-all-execute",
527
+ description: "Allow all tool executions",
528
+ effect: PolicyEffect.ALLOW,
529
+ priority: 1e3,
530
+ toolPattern: "*",
531
+ permission: Permission.EXECUTE,
532
+ minimumTrustLevel: TrustLevel.UNTRUSTED,
533
+ enabled: true,
534
+ createdAt: now,
535
+ updatedAt: now
536
+ },
537
+ {
538
+ id: "allow-all-read",
539
+ description: "Allow all read operations",
540
+ effect: PolicyEffect.ALLOW,
541
+ priority: 1e3,
542
+ toolPattern: "*",
543
+ permission: Permission.READ,
544
+ minimumTrustLevel: TrustLevel.UNTRUSTED,
545
+ enabled: true,
546
+ createdAt: now,
547
+ updatedAt: now
548
+ },
549
+ {
550
+ id: "allow-all-write",
551
+ description: "Allow all write operations",
552
+ effect: PolicyEffect.ALLOW,
553
+ priority: 1e3,
554
+ toolPattern: "*",
555
+ permission: Permission.WRITE,
556
+ minimumTrustLevel: TrustLevel.UNTRUSTED,
557
+ enabled: true,
558
+ createdAt: now,
559
+ updatedAt: now
560
+ }
561
+ ],
562
+ createdAt: now,
563
+ updatedAt: now
564
+ };
565
+ }
566
+ function createReadOnlyPolicySet(toolPattern) {
567
+ const now = (/* @__PURE__ */ new Date()).toISOString();
568
+ return {
569
+ id: `read-only-${toolPattern}`,
570
+ name: `Read-Only: ${toolPattern}`,
571
+ description: `Allows read access to tools matching "${toolPattern}". Denies write and execute.`,
572
+ version: 1,
573
+ rules: [
574
+ {
575
+ id: `allow-read-${toolPattern}`,
576
+ description: `Allow read access to ${toolPattern}`,
577
+ effect: PolicyEffect.ALLOW,
578
+ priority: 100,
579
+ toolPattern,
580
+ permission: Permission.READ,
581
+ minimumTrustLevel: TrustLevel.VERIFIED,
582
+ enabled: true,
583
+ createdAt: now,
584
+ updatedAt: now
585
+ }
586
+ ],
587
+ createdAt: now,
588
+ updatedAt: now
589
+ };
590
+ }
591
+ function createSandboxedPolicySet(rootDir) {
592
+ const now = (/* @__PURE__ */ new Date()).toISOString();
593
+ return {
594
+ id: `sandbox-${rootDir.replace(/\//g, "-")}`,
595
+ name: `Sandbox: ${rootDir}`,
596
+ description: `Allows operations within ${rootDir}. Blocks dangerous commands and sensitive file access.`,
597
+ version: 1,
598
+ rules: [
599
+ {
600
+ id: "deny-dangerous-commands",
601
+ description: "Block dangerous shell commands",
602
+ effect: PolicyEffect.DENY,
603
+ priority: 50,
604
+ toolPattern: "*",
605
+ permission: Permission.EXECUTE,
606
+ minimumTrustLevel: TrustLevel.UNTRUSTED,
607
+ commandConstraints: {
608
+ denied: [
609
+ "rm -rf *",
610
+ "rm -r /*",
611
+ "mkfs*",
612
+ "dd if=*",
613
+ "curl*|*bash*",
614
+ "wget*|*sh*",
615
+ "shutdown*",
616
+ "reboot*",
617
+ "chmod*777*"
618
+ ]
619
+ },
620
+ enabled: true,
621
+ createdAt: now,
622
+ updatedAt: now
623
+ },
624
+ {
625
+ id: "deny-sensitive-paths",
626
+ description: "Block access to sensitive files",
627
+ effect: PolicyEffect.DENY,
628
+ priority: 51,
629
+ toolPattern: "*",
630
+ permission: Permission.EXECUTE,
631
+ minimumTrustLevel: TrustLevel.UNTRUSTED,
632
+ pathConstraints: {
633
+ denied: [
634
+ "**/.env*",
635
+ "**/.ssh/**",
636
+ "**/.aws/**",
637
+ "**/credentials*",
638
+ "**/*.pem",
639
+ "**/*.key"
640
+ ]
641
+ },
642
+ enabled: true,
643
+ createdAt: now,
644
+ updatedAt: now
645
+ },
646
+ {
647
+ id: "allow-sandboxed-files",
648
+ description: `Allow file operations within ${rootDir}`,
649
+ effect: PolicyEffect.ALLOW,
650
+ priority: 100,
651
+ toolPattern: "file_*",
652
+ permission: Permission.EXECUTE,
653
+ minimumTrustLevel: TrustLevel.UNTRUSTED,
654
+ pathConstraints: {
655
+ rootDirectory: rootDir,
656
+ allowed: [`${rootDir}/**`]
657
+ },
658
+ enabled: true,
659
+ createdAt: now,
660
+ updatedAt: now
661
+ },
662
+ {
663
+ id: "allow-all-execute",
664
+ description: "Allow all other tool executions",
665
+ effect: PolicyEffect.ALLOW,
666
+ priority: 1e3,
667
+ toolPattern: "*",
668
+ permission: Permission.EXECUTE,
669
+ minimumTrustLevel: TrustLevel.UNTRUSTED,
670
+ enabled: true,
671
+ createdAt: now,
672
+ updatedAt: now
673
+ }
674
+ ],
675
+ createdAt: now,
676
+ updatedAt: now
677
+ };
678
+ }
679
+
680
+ // src/engine.ts
681
+ var PolicyEngine = class {
682
+ policySet;
683
+ timeoutMs;
684
+ store;
685
+ constructor(options) {
686
+ this.policySet = options?.policySet ?? createDefaultDenyPolicySet();
687
+ this.timeoutMs = options?.timeoutMs ?? POLICY_EVALUATION_TIMEOUT_MS;
688
+ this.store = options?.store ?? null;
689
+ }
690
+ /**
691
+ * Evaluates an execution request against the current policy set.
692
+ * Never throws for denials - denial is a normal outcome, not an error.
693
+ */
694
+ evaluate(request) {
695
+ const startTime = performance.now();
696
+ const decision = evaluatePolicy(this.policySet, request);
697
+ const elapsed = performance.now() - startTime;
698
+ if (elapsed > this.timeoutMs) {
699
+ console.warn(
700
+ `[SolonGate] Policy evaluation took ${elapsed.toFixed(1)}ms (limit: ${this.timeoutMs}ms) for tool "${request.toolName}"`
701
+ );
702
+ }
703
+ return decision;
704
+ }
705
+ /**
706
+ * Loads a new policy set, replacing the current one.
707
+ * Validates before accepting. Auto-saves version when store is present.
708
+ */
709
+ loadPolicySet(policySet, options) {
710
+ const validation = validatePolicySet(policySet);
711
+ if (!validation.valid) {
712
+ return validation;
713
+ }
714
+ this.policySet = policySet;
715
+ if (this.store) {
716
+ this.store.saveVersion(
717
+ policySet,
718
+ options?.reason ?? "Policy updated",
719
+ options?.createdBy ?? "system"
720
+ );
721
+ }
722
+ return validation;
723
+ }
724
+ /**
725
+ * Rolls back to a previous policy version.
726
+ * Only available when a PolicyStore is configured.
727
+ */
728
+ rollback(version) {
729
+ if (!this.store) {
730
+ throw new Error("PolicyStore not configured - cannot rollback");
731
+ }
732
+ const policyVersion = this.store.rollback(this.policySet.id, version);
733
+ this.policySet = policyVersion.policySet;
734
+ return policyVersion;
735
+ }
736
+ getPolicySet() {
737
+ return this.policySet;
738
+ }
739
+ getSecurityWarnings() {
740
+ return analyzeSecurityWarnings(this.policySet);
741
+ }
742
+ getStore() {
743
+ return this.store;
744
+ }
745
+ reset() {
746
+ this.policySet = createDefaultDenyPolicySet();
747
+ }
748
+ };
749
+ var PolicyStore = class {
750
+ versions = /* @__PURE__ */ new Map();
751
+ /**
752
+ * Saves a new version of a policy set.
753
+ * The version number auto-increments.
754
+ */
755
+ saveVersion(policySet, reason, createdBy) {
756
+ const id = policySet.id;
757
+ const history = this.versions.get(id) ?? [];
758
+ const latestVersion = history.length > 0 ? history[history.length - 1].version : 0;
759
+ const version = {
760
+ version: latestVersion + 1,
761
+ policySet: Object.freeze({ ...policySet }),
762
+ hash: this.computeHash(policySet),
763
+ reason,
764
+ createdBy,
765
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
766
+ };
767
+ const newHistory = [...history, version];
768
+ this.versions.set(id, newHistory);
769
+ return version;
770
+ }
771
+ /**
772
+ * Gets a specific version of a policy set.
773
+ */
774
+ getVersion(id, version) {
775
+ const history = this.versions.get(id);
776
+ if (!history) return null;
777
+ return history.find((v) => v.version === version) ?? null;
778
+ }
779
+ /**
780
+ * Gets the latest version of a policy set.
781
+ */
782
+ getLatest(id) {
783
+ const history = this.versions.get(id);
784
+ if (!history || history.length === 0) return null;
785
+ return history[history.length - 1];
786
+ }
787
+ /**
788
+ * Gets the full version history of a policy set.
789
+ */
790
+ getHistory(id) {
791
+ return this.versions.get(id) ?? [];
792
+ }
793
+ /**
794
+ * Rolls back to a previous version by creating a new version
795
+ * with the same content as the target version.
796
+ */
797
+ rollback(id, toVersion) {
798
+ const target = this.getVersion(id, toVersion);
799
+ if (!target) {
800
+ throw new Error(`Version ${toVersion} not found for policy "${id}"`);
801
+ }
802
+ return this.saveVersion(
803
+ target.policySet,
804
+ `Rollback to version ${toVersion}`,
805
+ "system"
806
+ );
807
+ }
808
+ /**
809
+ * Computes a diff between two policy versions.
810
+ */
811
+ diff(v1, v2) {
812
+ const oldRulesMap = new Map(v1.policySet.rules.map((r) => [r.id, r]));
813
+ const newRulesMap = new Map(v2.policySet.rules.map((r) => [r.id, r]));
814
+ const added = [];
815
+ const removed = [];
816
+ const modified = [];
817
+ for (const [id, newRule] of newRulesMap) {
818
+ const oldRule = oldRulesMap.get(id);
819
+ if (!oldRule) {
820
+ added.push(newRule);
821
+ } else if (JSON.stringify(oldRule) !== JSON.stringify(newRule)) {
822
+ modified.push({ old: oldRule, new: newRule });
823
+ }
824
+ }
825
+ for (const [id, oldRule] of oldRulesMap) {
826
+ if (!newRulesMap.has(id)) {
827
+ removed.push(oldRule);
828
+ }
829
+ }
830
+ return { added, removed, modified };
831
+ }
832
+ /**
833
+ * Computes SHA256 hash of a policy set for integrity verification.
834
+ */
835
+ computeHash(policySet) {
836
+ const serialized = JSON.stringify(policySet, Object.keys(policySet).sort());
837
+ return createHash("sha256").update(serialized).digest("hex");
838
+ }
839
+ };
840
+
841
+ export { PolicyEngine, PolicyStore, analyzeSecurityWarnings, createDefaultDenyPolicySet, createPermissivePolicySet, createReadOnlyPolicySet, createSandboxedPolicySet, evaluatePolicy, extractCommandArguments, extractPathArguments, isCommandAllowed, isPathAllowed, isWithinRoot, matchCommandPattern, matchPathPattern, normalizePath, ruleMatchesRequest, toolPatternMatches, trustLevelMeetsMinimum, validatePolicyRule, validatePolicySet };
842
+ //# sourceMappingURL=index.js.map
843
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/path-matcher.ts","../src/command-matcher.ts","../src/matcher.ts","../src/evaluator.ts","../src/validator.ts","../src/warnings.ts","../src/defaults.ts","../src/engine.ts","../src/policy-store.ts"],"names":["endTime","UNSAFE_CONFIGURATION_WARNINGS","TrustLevel"],"mappings":"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","file":"index.js","sourcesContent":["import type { PolicyRule } from '@solongate/core';\n\ntype PathConstraints = NonNullable<PolicyRule['pathConstraints']>;\n\n/**\n * Normalizes a file path for consistent matching.\n * Resolves . and .. segments, normalizes separators.\n */\nexport function normalizePath(path: string): string {\n // Normalize separators to forward slash\n let normalized = path.replace(/\\\\/g, '/');\n\n // Remove trailing slash (except for root)\n if (normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n\n // Resolve . and .. segments\n const parts = normalized.split('/');\n const resolved: string[] = [];\n\n for (const part of parts) {\n if (part === '.' || part === '') {\n if (resolved.length === 0) resolved.push('');\n continue;\n }\n if (part === '..') {\n if (resolved.length > 1) {\n resolved.pop();\n }\n continue;\n }\n resolved.push(part);\n }\n\n return resolved.join('/') || '/';\n}\n\n/**\n * Checks if a path is within a root directory (sandbox boundary).\n * Prevents escaping via .., symlinks, etc.\n */\nexport function isWithinRoot(path: string, root: string): boolean {\n const normalizedPath = normalizePath(path);\n const normalizedRoot = normalizePath(root);\n\n // Path must start with root\n if (normalizedPath === normalizedRoot) return true;\n return normalizedPath.startsWith(normalizedRoot + '/');\n}\n\n/**\n * Glob-style path pattern matching.\n * Supports:\n * - * matches any single path segment (not /)\n * - ** matches any number of path segments\n * - Exact match\n *\n * Does NOT support regex (ReDoS prevention).\n */\nexport function matchPathPattern(path: string, pattern: string): boolean {\n const normalizedPath = normalizePath(path);\n const normalizedPattern = normalizePath(pattern);\n\n if (normalizedPattern === '*') return true;\n if (normalizedPattern === normalizedPath) return true;\n\n const patternParts = normalizedPattern.split('/');\n const pathParts = normalizedPath.split('/');\n\n return matchParts(pathParts, 0, patternParts, 0);\n}\n\nfunction matchParts(\n pathParts: string[],\n pi: number,\n patternParts: string[],\n qi: number,\n): boolean {\n while (pi < pathParts.length && qi < patternParts.length) {\n const pattern = patternParts[qi]!;\n\n if (pattern === '**') {\n // ** can match zero or more path segments\n if (qi === patternParts.length - 1) return true;\n\n // Try matching ** against 0, 1, 2, ... path segments\n for (let i = pi; i <= pathParts.length; i++) {\n if (matchParts(pathParts, i, patternParts, qi + 1)) {\n return true;\n }\n }\n return false;\n }\n\n if (pattern === '*') {\n // * matches exactly one path segment\n pi++;\n qi++;\n continue;\n }\n\n // Support intra-segment globs: *.txt, file.*, test-*-data, etc.\n if (pattern.includes('*')) {\n if (!matchSegmentGlob(pathParts[pi]!, pattern)) {\n return false;\n }\n pi++;\n qi++;\n continue;\n }\n\n if (pattern !== pathParts[pi]) {\n return false;\n }\n\n pi++;\n qi++;\n }\n\n // Skip trailing ** patterns\n while (qi < patternParts.length && patternParts[qi] === '**') {\n qi++;\n }\n\n return pi === pathParts.length && qi === patternParts.length;\n}\n\n/**\n * Checks if a path is allowed by the given constraints.\n *\n * Evaluation order:\n * 1. If rootDirectory is set, path must be within it\n * 2. If denied list exists, path must NOT match any denied pattern\n * 3. If allowed list exists, path must match at least one allowed pattern\n * 4. If neither list exists, path is allowed (constraints are optional)\n */\nexport function isPathAllowed(\n path: string,\n constraints: PathConstraints,\n): boolean {\n // 1. Root directory check (sandbox)\n if (constraints.rootDirectory) {\n if (!isWithinRoot(path, constraints.rootDirectory)) {\n return false;\n }\n }\n\n // 2. Denied list - any match means denied\n if (constraints.denied && constraints.denied.length > 0) {\n for (const pattern of constraints.denied) {\n if (matchPathPattern(path, pattern)) {\n return false;\n }\n }\n }\n\n // 3. Allowed list - must match at least one\n if (constraints.allowed && constraints.allowed.length > 0) {\n let matchesAllowed = false;\n for (const pattern of constraints.allowed) {\n if (matchPathPattern(path, pattern)) {\n matchesAllowed = true;\n break;\n }\n }\n if (!matchesAllowed) return false;\n }\n\n return true;\n}\n\n/**\n * Matches a single path segment against a glob pattern containing *.\n * Examples: *.txt matches safe.txt, file.* matches file.js, *secret* matches my-secret-key\n */\nfunction matchSegmentGlob(segment: string, pattern: string): boolean {\n const startsWithStar = pattern.startsWith('*');\n const endsWithStar = pattern.endsWith('*');\n\n if (pattern === '*') return true;\n\n if (startsWithStar && endsWithStar) {\n // *infix* — contains\n const infix = pattern.slice(1, -1);\n return segment.toLowerCase().includes(infix.toLowerCase());\n }\n if (startsWithStar) {\n // *suffix — ends with\n const suffix = pattern.slice(1);\n return segment.toLowerCase().endsWith(suffix.toLowerCase());\n }\n if (endsWithStar) {\n // prefix* — starts with\n const prefix = pattern.slice(0, -1);\n return segment.toLowerCase().startsWith(prefix.toLowerCase());\n }\n\n // Single * in middle: split on * and check prefix+suffix\n const starIdx = pattern.indexOf('*');\n if (starIdx !== -1) {\n const prefix = pattern.slice(0, starIdx);\n const suffix = pattern.slice(starIdx + 1);\n const seg = segment.toLowerCase();\n return seg.startsWith(prefix.toLowerCase()) && seg.endsWith(suffix.toLowerCase()) && seg.length >= prefix.length + suffix.length;\n }\n\n return segment === pattern;\n}\n\n/**\n * Extracts path-like arguments from tool call arguments.\n * Heuristic: any string argument containing / or \\ is treated as a path.\n */\nexport function extractPathArguments(\n args: Readonly<Record<string, unknown>>,\n): string[] {\n const paths: string[] = [];\n\n for (const value of Object.values(args)) {\n if (typeof value === 'string' && (value.includes('/') || value.includes('\\\\'))) {\n paths.push(value);\n }\n }\n\n return paths;\n}\n","import type { PolicyRule } from '@solongate/core';\n\ntype CommandConstraints = NonNullable<PolicyRule['commandConstraints']>;\n\n/**\n * Known argument field names that typically contain commands or shell-like content.\n * Used to extract commands from tool call arguments for constraint matching.\n */\nconst COMMAND_FIELDS = new Set([\n 'command',\n 'cmd',\n 'query',\n 'code',\n 'script',\n 'shell',\n 'exec',\n 'sql',\n 'expression',\n]);\n\n/**\n * Extracts command-like arguments from tool call arguments.\n * Looks for known field names and string values that look like commands.\n */\nexport function extractCommandArguments(\n args: Readonly<Record<string, unknown>>,\n): string[] {\n const commands: string[] = [];\n\n for (const [key, value] of Object.entries(args)) {\n if (typeof value !== 'string') continue;\n\n // Check known command field names\n if (COMMAND_FIELDS.has(key.toLowerCase())) {\n commands.push(value);\n }\n }\n\n return commands;\n}\n\n/**\n * Glob-style command pattern matching.\n * Matches against the command string (first word) or full command line.\n *\n * Patterns:\n * 'ls' → exact match on command name\n * 'git*' → command starts with 'git'\n * '*sql*' → command contains 'sql'\n * 'rm -rf *' → full command line starts with 'rm -rf '\n */\nexport function matchCommandPattern(command: string, pattern: string): boolean {\n if (pattern === '*') return true;\n\n const normalizedCommand = command.trim().toLowerCase();\n const normalizedPattern = pattern.trim().toLowerCase();\n\n if (normalizedPattern === normalizedCommand) return true;\n\n const startsWithStar = normalizedPattern.startsWith('*');\n const endsWithStar = normalizedPattern.endsWith('*');\n\n if (startsWithStar && endsWithStar) {\n const infix = normalizedPattern.slice(1, -1);\n return infix.length > 0 && normalizedCommand.includes(infix);\n }\n if (endsWithStar) {\n const prefix = normalizedPattern.slice(0, -1);\n return normalizedCommand.startsWith(prefix);\n }\n if (startsWithStar) {\n const suffix = normalizedPattern.slice(1);\n return normalizedCommand.endsWith(suffix);\n }\n\n // Also try matching just the command name (first word)\n const commandName = normalizedCommand.split(/\\s+/)[0] ?? '';\n return commandName === normalizedPattern;\n}\n\n/**\n * Checks if a command is allowed by the given constraints.\n *\n * Evaluation order:\n * 1. If denied list exists, command must NOT match any denied pattern\n * 2. If allowed list exists, command must match at least one allowed pattern\n * 3. If neither list exists, command is allowed (constraints are optional)\n */\nexport function isCommandAllowed(\n command: string,\n constraints: CommandConstraints,\n): boolean {\n // 1. Denied list — any match means denied\n if (constraints.denied && constraints.denied.length > 0) {\n for (const pattern of constraints.denied) {\n if (matchCommandPattern(command, pattern)) {\n return false;\n }\n }\n }\n\n // 2. Allowed list — must match at least one\n if (constraints.allowed && constraints.allowed.length > 0) {\n let matchesAllowed = false;\n for (const pattern of constraints.allowed) {\n if (matchCommandPattern(command, pattern)) {\n matchesAllowed = true;\n break;\n }\n }\n if (!matchesAllowed) return false;\n }\n\n return true;\n}\n","import type { PolicyRule, ExecutionRequest } from '@solongate/core';\nimport { TrustLevel } from '@solongate/core';\nimport { isPathAllowed, extractPathArguments } from './path-matcher.js';\nimport { isCommandAllowed, extractCommandArguments } from './command-matcher.js';\n\n/**\n * Pure function: determines if a policy rule matches an execution request.\n * No side effects. No I/O. Fully deterministic.\n */\nexport function ruleMatchesRequest(\n rule: PolicyRule,\n request: ExecutionRequest,\n): boolean {\n if (!rule.enabled) return false;\n if (rule.permission !== request.requiredPermission) return false;\n if (!toolPatternMatches(rule.toolPattern, request.toolName)) return false;\n if (!trustLevelMeetsMinimum(request.context.trustLevel, rule.minimumTrustLevel)) {\n return false;\n }\n if (rule.argumentConstraints) {\n if (!argumentConstraintsMatch(rule.argumentConstraints, request.arguments)) {\n return false;\n }\n }\n if (rule.pathConstraints) {\n const satisfied = pathConstraintsMatch(rule.pathConstraints, request.arguments);\n // For DENY rules: match when constraints are VIOLATED (path is dangerous)\n // For ALLOW rules: match when constraints are SATISFIED (path is safe)\n if (rule.effect === 'DENY') {\n if (satisfied) return false; // path is safe → don't deny\n } else {\n if (!satisfied) return false; // path is dangerous → don't allow\n }\n }\n if (rule.commandConstraints) {\n const satisfied = commandConstraintsMatch(rule.commandConstraints, request.arguments);\n // For DENY rules: match when constraints are VIOLATED (command is dangerous)\n // For ALLOW rules: match when constraints are SATISFIED (command is safe)\n if (rule.effect === 'DENY') {\n if (satisfied) return false; // command is safe → don't deny\n } else {\n if (!satisfied) return false; // command is dangerous → don't allow\n }\n }\n return true;\n}\n\n/**\n * Glob-style tool name pattern matching.\n * Supports:\n * '*' → match all\n * 'prefix*' → starts with prefix\n * '*suffix' → ends with suffix\n * '*infix*' → contains infix\n * Does NOT support regex (ReDoS prevention).\n */\nexport function toolPatternMatches(pattern: string, toolName: string): boolean {\n if (pattern === '*') return true;\n\n const startsWithStar = pattern.startsWith('*');\n const endsWithStar = pattern.endsWith('*');\n\n if (startsWithStar && endsWithStar) {\n // *infix* → contains\n const infix = pattern.slice(1, -1);\n return infix.length > 0 && toolName.includes(infix);\n }\n if (endsWithStar) {\n // prefix* → starts with\n const prefix = pattern.slice(0, -1);\n return toolName.startsWith(prefix);\n }\n if (startsWithStar) {\n // *suffix → ends with\n const suffix = pattern.slice(1);\n return toolName.endsWith(suffix);\n }\n\n return pattern === toolName;\n}\n\nconst TRUST_LEVEL_ORDER: Record<string, number> = {\n [TrustLevel.UNTRUSTED]: 0,\n [TrustLevel.VERIFIED]: 1,\n [TrustLevel.TRUSTED]: 2,\n};\n\nexport function trustLevelMeetsMinimum(\n actual: TrustLevel,\n minimum: TrustLevel,\n): boolean {\n return (TRUST_LEVEL_ORDER[actual] ?? -1) >= (TRUST_LEVEL_ORDER[minimum] ?? Infinity);\n}\n\n/**\n * Condition operators for argument constraints.\n * When constraint value is a plain string → exact match (or '*' for any).\n * When constraint value is an object → operator-based matching:\n * { $contains: \"str\" } — value includes substring\n * { $notContains: \"str\" } — value does NOT include substring\n * { $startsWith: \"str\" } — value starts with prefix\n * { $endsWith: \"str\" } — value ends with suffix\n * { $in: [\"a\",\"b\"] } — value is one of the listed values\n * { $notIn: [\"a\",\"b\"] } — value is NOT one of the listed values\n * { $gt: 5 } — numeric greater than\n * { $lt: 5 } — numeric less than\n * { $gte: 5 } — numeric greater than or equal\n * { $lte: 5 } — numeric less than or equal\n */\nfunction argumentConstraintsMatch(\n constraints: Record<string, unknown>,\n args: Readonly<Record<string, unknown>>,\n): boolean {\n for (const [key, constraint] of Object.entries(constraints)) {\n if (!(key in args)) return false;\n const argValue = args[key];\n\n // Plain string: exact match (backward compatible)\n if (typeof constraint === 'string') {\n if (constraint === '*') continue;\n if (typeof argValue === 'string') {\n if (argValue !== constraint) return false;\n } else {\n return false;\n }\n continue;\n }\n\n // Object with operators\n if (typeof constraint === 'object' && constraint !== null && !Array.isArray(constraint)) {\n const ops = constraint as Record<string, unknown>;\n const strValue = typeof argValue === 'string' ? argValue : undefined;\n const numValue = typeof argValue === 'number' ? argValue : undefined;\n\n if ('$contains' in ops && typeof ops.$contains === 'string') {\n if (!strValue || !strValue.includes(ops.$contains)) return false;\n }\n if ('$notContains' in ops && typeof ops.$notContains === 'string') {\n if (strValue && strValue.includes(ops.$notContains)) return false;\n }\n if ('$startsWith' in ops && typeof ops.$startsWith === 'string') {\n if (!strValue || !strValue.startsWith(ops.$startsWith)) return false;\n }\n if ('$endsWith' in ops && typeof ops.$endsWith === 'string') {\n if (!strValue || !strValue.endsWith(ops.$endsWith)) return false;\n }\n if ('$in' in ops && Array.isArray(ops.$in)) {\n if (!ops.$in.includes(argValue)) return false;\n }\n if ('$notIn' in ops && Array.isArray(ops.$notIn)) {\n if (ops.$notIn.includes(argValue)) return false;\n }\n if ('$gt' in ops && typeof ops.$gt === 'number') {\n if (numValue === undefined || numValue <= ops.$gt) return false;\n }\n if ('$lt' in ops && typeof ops.$lt === 'number') {\n if (numValue === undefined || numValue >= ops.$lt) return false;\n }\n if ('$gte' in ops && typeof ops.$gte === 'number') {\n if (numValue === undefined || numValue < ops.$gte) return false;\n }\n if ('$lte' in ops && typeof ops.$lte === 'number') {\n if (numValue === undefined || numValue > ops.$lte) return false;\n }\n\n continue;\n }\n }\n return true;\n}\n\nfunction pathConstraintsMatch(\n constraints: NonNullable<PolicyRule['pathConstraints']>,\n args: Readonly<Record<string, unknown>>,\n): boolean {\n const paths = extractPathArguments(args);\n\n // If no path arguments found, constraints don't apply\n if (paths.length === 0) return true;\n\n // ALL path arguments must satisfy constraints\n return paths.every((path) => isPathAllowed(path, constraints));\n}\n\nfunction commandConstraintsMatch(\n constraints: NonNullable<PolicyRule['commandConstraints']>,\n args: Readonly<Record<string, unknown>>,\n): boolean {\n const commands = extractCommandArguments(args);\n\n // If no command arguments found, constraints don't apply\n if (commands.length === 0) return true;\n\n // ALL command arguments must satisfy constraints\n return commands.every((cmd) => isCommandAllowed(cmd, constraints));\n}\n","import type {\n PolicySet,\n PolicyDecision,\n ExecutionRequest,\n PolicyEffect,\n} from '@solongate/core';\nimport { DEFAULT_POLICY_EFFECT } from '@solongate/core';\nimport { ruleMatchesRequest } from './matcher.js';\n\n/**\n * Evaluates a policy set against an execution request.\n *\n * Pure function: no side effects, no I/O, fully deterministic.\n *\n * Algorithm:\n * 1. Sort rules by priority (ascending - lower number = higher priority)\n * 2. Find the first matching rule\n * 3. If a rule matches, return its effect\n * 4. If no rule matches, return DENY (default-deny)\n */\nexport function evaluatePolicy(\n policySet: PolicySet,\n request: ExecutionRequest,\n): PolicyDecision {\n const startTime = performance.now();\n\n const sortedRules = [...policySet.rules].sort(\n (a, b) => a.priority - b.priority,\n );\n\n for (const rule of sortedRules) {\n if (ruleMatchesRequest(rule, request)) {\n const endTime = performance.now();\n return {\n effect: rule.effect,\n matchedRule: rule,\n reason: `Matched rule \"${rule.id}\": ${rule.description}`,\n timestamp: new Date().toISOString(),\n evaluationTimeMs: endTime - startTime,\n };\n }\n }\n\n const endTime = performance.now();\n return {\n effect: DEFAULT_POLICY_EFFECT as PolicyEffect,\n matchedRule: null,\n reason: 'No matching policy rule found. Default action: DENY.',\n timestamp: new Date().toISOString(),\n evaluationTimeMs: endTime - startTime,\n metadata: {\n evaluatedRules: sortedRules.length,\n ruleIds: sortedRules.map((r) => r.id),\n requestContext: {\n tool: request.toolName,\n arguments: Object.keys(request.arguments ?? {}),\n },\n },\n };\n}\n","import { PolicyRuleSchema, PolicySetSchema } from '@solongate/core';\nimport {\n MAX_RULES_PER_POLICY_SET,\n UNSAFE_CONFIGURATION_WARNINGS,\n} from '@solongate/core';\n\nexport interface ValidationResult {\n readonly valid: boolean;\n readonly errors: readonly string[];\n readonly warnings: readonly string[];\n}\n\nexport function validatePolicyRule(input: unknown): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n const result = PolicyRuleSchema.safeParse(input);\n if (!result.success) {\n return {\n valid: false,\n errors: result.error.errors.map(\n (e) => `${e.path.join('.')}: ${e.message}`,\n ),\n warnings: [],\n };\n }\n\n const rule = result.data;\n\n if (rule.toolPattern === '*' && rule.effect === 'ALLOW') {\n warnings.push(UNSAFE_CONFIGURATION_WARNINGS.WILDCARD_ALLOW);\n }\n\n if (rule.minimumTrustLevel === 'TRUSTED') {\n warnings.push(UNSAFE_CONFIGURATION_WARNINGS.TRUSTED_LEVEL_EXTERNAL);\n }\n\n if (rule.permission === 'EXECUTE') {\n warnings.push(UNSAFE_CONFIGURATION_WARNINGS.EXECUTE_WITHOUT_REVIEW);\n }\n\n return { valid: true, errors, warnings };\n}\n\nexport function validatePolicySet(input: unknown): ValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n const result = PolicySetSchema.safeParse(input);\n if (!result.success) {\n return {\n valid: false,\n errors: result.error.errors.map(\n (e) => `${e.path.join('.')}: ${e.message}`,\n ),\n warnings: [],\n };\n }\n\n const policySet = result.data;\n\n if (policySet.rules.length > MAX_RULES_PER_POLICY_SET) {\n errors.push(\n `Policy set exceeds maximum of ${MAX_RULES_PER_POLICY_SET} rules`,\n );\n }\n\n const ruleIds = new Set<string>();\n for (const rule of policySet.rules) {\n if (ruleIds.has(rule.id)) {\n errors.push(`Duplicate rule ID: \"${rule.id}\"`);\n }\n ruleIds.add(rule.id);\n }\n\n for (const rule of policySet.rules) {\n const ruleResult = validatePolicyRule(rule);\n warnings.push(...ruleResult.warnings);\n }\n\n const hasDenyRule = policySet.rules.some((r) => r.effect === 'DENY');\n if (!hasDenyRule && policySet.rules.length > 0) {\n warnings.push(\n 'Policy set contains only ALLOW rules. The default-deny fallback is the only protection.',\n );\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n };\n}\n","import type { PolicyRule, PolicySet } from '@solongate/core';\nimport { UNSAFE_CONFIGURATION_WARNINGS } from '@solongate/core';\n\nexport interface SecurityWarning {\n readonly level: 'WARNING' | 'CRITICAL';\n readonly code: string;\n readonly message: string;\n readonly ruleId?: string;\n readonly recommendation: string;\n}\n\n/** Analyzes a policy set and returns security warnings. Pure function. */\nexport function analyzeSecurityWarnings(\n policySet: PolicySet,\n): readonly SecurityWarning[] {\n const warnings: SecurityWarning[] = [];\n\n for (const rule of policySet.rules) {\n warnings.push(...analyzeRuleWarnings(rule));\n }\n\n const allowRules = policySet.rules.filter(\n (r) => r.effect === 'ALLOW' && r.enabled,\n );\n const wildcardAllows = allowRules.filter((r) => r.toolPattern === '*');\n\n if (wildcardAllows.length > 0) {\n warnings.push({\n level: 'CRITICAL',\n code: 'WILDCARD_ALLOW',\n message: UNSAFE_CONFIGURATION_WARNINGS.WILDCARD_ALLOW,\n recommendation:\n 'Replace wildcard ALLOW rules with specific tool patterns.',\n });\n }\n\n return warnings;\n}\n\nfunction analyzeRuleWarnings(rule: PolicyRule): SecurityWarning[] {\n const warnings: SecurityWarning[] = [];\n\n if (rule.effect === 'ALLOW' && rule.minimumTrustLevel === 'UNTRUSTED') {\n warnings.push({\n level: 'CRITICAL',\n code: 'ALLOW_UNTRUSTED',\n message: `Rule \"${rule.id}\" allows execution for UNTRUSTED requests. Unverified LLM requests can execute tools.`,\n ruleId: rule.id,\n recommendation:\n 'Set minimumTrustLevel to VERIFIED or higher for ALLOW rules.',\n });\n }\n\n if (rule.effect === 'ALLOW' && rule.permission === 'EXECUTE') {\n warnings.push({\n level: 'WARNING',\n code: 'ALLOW_EXECUTE',\n message: UNSAFE_CONFIGURATION_WARNINGS.EXECUTE_WITHOUT_REVIEW,\n ruleId: rule.id,\n recommendation:\n 'Ensure EXECUTE permissions are intentional and scoped to specific tools.',\n });\n }\n\n return warnings;\n}\n","import type { PolicySet } from '@solongate/core';\nimport { PolicyEffect, Permission, TrustLevel } from '@solongate/core';\n\n/**\n * Creates the default \"deny all\" policy set.\n * This is the starting policy for any new SolonGate deployment.\n */\nexport function createDefaultDenyPolicySet(): PolicySet {\n const now = new Date().toISOString();\n\n return {\n id: 'default-deny',\n name: 'Default Deny All',\n description:\n 'Denies all tool executions. Add explicit ALLOW rules to grant access to specific tools.',\n version: 1,\n rules: [\n {\n id: 'deny-all-execute',\n description: 'Explicitly deny all tool executions',\n effect: PolicyEffect.DENY,\n priority: 10000,\n toolPattern: '*',\n permission: Permission.EXECUTE,\n minimumTrustLevel: TrustLevel.UNTRUSTED,\n enabled: true,\n createdAt: now,\n updatedAt: now,\n },\n {\n id: 'deny-all-write',\n description: 'Explicitly deny all write operations',\n effect: PolicyEffect.DENY,\n priority: 10000,\n toolPattern: '*',\n permission: Permission.WRITE,\n minimumTrustLevel: TrustLevel.UNTRUSTED,\n enabled: true,\n createdAt: now,\n updatedAt: now,\n },\n {\n id: 'deny-all-read',\n description: 'Explicitly deny all read operations',\n effect: PolicyEffect.DENY,\n priority: 10000,\n toolPattern: '*',\n permission: Permission.READ,\n minimumTrustLevel: TrustLevel.UNTRUSTED,\n enabled: true,\n createdAt: now,\n updatedAt: now,\n },\n ],\n createdAt: now,\n updatedAt: now,\n };\n}\n\n/**\n * Creates a permissive \"allow all\" policy set.\n * Allows all tool executions — useful for development or when\n * using SolonGate only for monitoring and audit logging.\n */\nexport function createPermissivePolicySet(): PolicySet {\n const now = new Date().toISOString();\n\n return {\n id: 'permissive',\n name: 'Permissive (Allow All)',\n description: 'Allows all tool executions. SolonGate still provides input validation, rate limiting, and audit logging.',\n version: 1,\n rules: [\n {\n id: 'allow-all-execute',\n description: 'Allow all tool executions',\n effect: PolicyEffect.ALLOW,\n priority: 1000,\n toolPattern: '*',\n permission: Permission.EXECUTE,\n minimumTrustLevel: TrustLevel.UNTRUSTED,\n enabled: true,\n createdAt: now,\n updatedAt: now,\n },\n {\n id: 'allow-all-read',\n description: 'Allow all read operations',\n effect: PolicyEffect.ALLOW,\n priority: 1000,\n toolPattern: '*',\n permission: Permission.READ,\n minimumTrustLevel: TrustLevel.UNTRUSTED,\n enabled: true,\n createdAt: now,\n updatedAt: now,\n },\n {\n id: 'allow-all-write',\n description: 'Allow all write operations',\n effect: PolicyEffect.ALLOW,\n priority: 1000,\n toolPattern: '*',\n permission: Permission.WRITE,\n minimumTrustLevel: TrustLevel.UNTRUSTED,\n enabled: true,\n createdAt: now,\n updatedAt: now,\n },\n ],\n createdAt: now,\n updatedAt: now,\n };\n}\n\n/**\n * Creates a read-only policy set for a specific tool pattern.\n * Allows reads for VERIFIED requests only.\n */\nexport function createReadOnlyPolicySet(toolPattern: string): PolicySet {\n const now = new Date().toISOString();\n\n return {\n id: `read-only-${toolPattern}`,\n name: `Read-Only: ${toolPattern}`,\n description: `Allows read access to tools matching \"${toolPattern}\". Denies write and execute.`,\n version: 1,\n rules: [\n {\n id: `allow-read-${toolPattern}`,\n description: `Allow read access to ${toolPattern}`,\n effect: PolicyEffect.ALLOW,\n priority: 100,\n toolPattern,\n permission: Permission.READ,\n minimumTrustLevel: TrustLevel.VERIFIED,\n enabled: true,\n createdAt: now,\n updatedAt: now,\n },\n ],\n createdAt: now,\n updatedAt: now,\n };\n}\n\n/**\n * Creates a sandboxed policy set for a given root directory.\n * Allows file operations within rootDir, blocks dangerous commands,\n * denies access to sensitive files.\n */\nexport function createSandboxedPolicySet(rootDir: string): PolicySet {\n const now = new Date().toISOString();\n\n return {\n id: `sandbox-${rootDir.replace(/\\//g, '-')}`,\n name: `Sandbox: ${rootDir}`,\n description: `Allows operations within ${rootDir}. Blocks dangerous commands and sensitive file access.`,\n version: 1,\n rules: [\n {\n id: 'deny-dangerous-commands',\n description: 'Block dangerous shell commands',\n effect: PolicyEffect.DENY,\n priority: 50,\n toolPattern: '*',\n permission: Permission.EXECUTE,\n minimumTrustLevel: TrustLevel.UNTRUSTED,\n commandConstraints: {\n denied: [\n 'rm -rf *', 'rm -r /*', 'mkfs*', 'dd if=*',\n 'curl*|*bash*', 'wget*|*sh*',\n 'shutdown*', 'reboot*', 'chmod*777*',\n ],\n },\n enabled: true,\n createdAt: now,\n updatedAt: now,\n },\n {\n id: 'deny-sensitive-paths',\n description: 'Block access to sensitive files',\n effect: PolicyEffect.DENY,\n priority: 51,\n toolPattern: '*',\n permission: Permission.EXECUTE,\n minimumTrustLevel: TrustLevel.UNTRUSTED,\n pathConstraints: {\n denied: [\n '**/.env*', '**/.ssh/**', '**/.aws/**',\n '**/credentials*', '**/*.pem', '**/*.key',\n ],\n },\n enabled: true,\n createdAt: now,\n updatedAt: now,\n },\n {\n id: 'allow-sandboxed-files',\n description: `Allow file operations within ${rootDir}`,\n effect: PolicyEffect.ALLOW,\n priority: 100,\n toolPattern: 'file_*',\n permission: Permission.EXECUTE,\n minimumTrustLevel: TrustLevel.UNTRUSTED,\n pathConstraints: {\n rootDirectory: rootDir,\n allowed: [`${rootDir}/**`],\n },\n enabled: true,\n createdAt: now,\n updatedAt: now,\n },\n {\n id: 'allow-all-execute',\n description: 'Allow all other tool executions',\n effect: PolicyEffect.ALLOW,\n priority: 1000,\n toolPattern: '*',\n permission: Permission.EXECUTE,\n minimumTrustLevel: TrustLevel.UNTRUSTED,\n enabled: true,\n createdAt: now,\n updatedAt: now,\n },\n ],\n createdAt: now,\n updatedAt: now,\n };\n}\n","import type {\n PolicySet,\n PolicyDecision,\n ExecutionRequest,\n} from '@solongate/core';\nimport { POLICY_EVALUATION_TIMEOUT_MS } from '@solongate/core';\nimport { evaluatePolicy } from './evaluator.js';\nimport { validatePolicySet, type ValidationResult } from './validator.js';\nimport { analyzeSecurityWarnings, type SecurityWarning } from './warnings.js';\nimport { createDefaultDenyPolicySet } from './defaults.js';\nimport { PolicyStore, type PolicyVersion } from './policy-store.js';\n\n/**\n * PolicyEngine is the primary interface for policy evaluation.\n *\n * Wraps pure evaluation functions with:\n * - Policy set management (load, validate, swap)\n * - Timeout protection\n * - Warning aggregation\n * - Optional versioned policy store\n */\nexport class PolicyEngine {\n private policySet: PolicySet;\n private readonly timeoutMs: number;\n private readonly store: PolicyStore | null;\n\n constructor(options?: {\n policySet?: PolicySet;\n timeoutMs?: number;\n store?: PolicyStore;\n }) {\n this.policySet = options?.policySet ?? createDefaultDenyPolicySet();\n this.timeoutMs = options?.timeoutMs ?? POLICY_EVALUATION_TIMEOUT_MS;\n this.store = options?.store ?? null;\n }\n\n /**\n * Evaluates an execution request against the current policy set.\n * Never throws for denials - denial is a normal outcome, not an error.\n */\n evaluate(request: ExecutionRequest): PolicyDecision {\n const startTime = performance.now();\n const decision = evaluatePolicy(this.policySet, request);\n const elapsed = performance.now() - startTime;\n\n if (elapsed > this.timeoutMs) {\n console.warn(\n `[SolonGate] Policy evaluation took ${elapsed.toFixed(1)}ms ` +\n `(limit: ${this.timeoutMs}ms) for tool \"${request.toolName}\"`,\n );\n }\n\n return decision;\n }\n\n /**\n * Loads a new policy set, replacing the current one.\n * Validates before accepting. Auto-saves version when store is present.\n */\n loadPolicySet(\n policySet: PolicySet,\n options?: { reason?: string; createdBy?: string },\n ): ValidationResult {\n const validation = validatePolicySet(policySet);\n if (!validation.valid) {\n return validation;\n }\n this.policySet = policySet;\n\n if (this.store) {\n this.store.saveVersion(\n policySet,\n options?.reason ?? 'Policy updated',\n options?.createdBy ?? 'system',\n );\n }\n\n return validation;\n }\n\n /**\n * Rolls back to a previous policy version.\n * Only available when a PolicyStore is configured.\n */\n rollback(version: number): PolicyVersion {\n if (!this.store) {\n throw new Error('PolicyStore not configured - cannot rollback');\n }\n\n const policyVersion = this.store.rollback(this.policySet.id, version);\n this.policySet = policyVersion.policySet;\n return policyVersion;\n }\n\n getPolicySet(): Readonly<PolicySet> {\n return this.policySet;\n }\n\n getSecurityWarnings(): readonly SecurityWarning[] {\n return analyzeSecurityWarnings(this.policySet);\n }\n\n getStore(): PolicyStore | null {\n return this.store;\n }\n\n reset(): void {\n this.policySet = createDefaultDenyPolicySet();\n }\n}\n","import type { PolicySet, PolicyRule } from '@solongate/core';\nimport { createHash } from 'node:crypto';\n\n/**\n * A versioned snapshot of a policy set.\n * Immutable once created - modifications create new versions.\n */\nexport interface PolicyVersion {\n readonly version: number;\n readonly policySet: PolicySet;\n readonly hash: string;\n readonly reason: string;\n readonly createdBy: string;\n readonly createdAt: string;\n}\n\n/**\n * Diff between two policy versions.\n */\nexport interface PolicyDiff {\n readonly added: readonly PolicyRule[];\n readonly removed: readonly PolicyRule[];\n readonly modified: readonly { readonly old: PolicyRule; readonly new: PolicyRule }[];\n}\n\n/**\n * In-memory versioned policy store.\n * Stores complete history of policy changes with cryptographic hashes.\n *\n * Security properties:\n * - Immutable versions: once saved, a version cannot be modified\n * - Hash chain: each version includes SHA256 of the policy content\n * - Full history: no version is ever deleted\n */\nexport class PolicyStore {\n private readonly versions = new Map<string, PolicyVersion[]>();\n\n /**\n * Saves a new version of a policy set.\n * The version number auto-increments.\n */\n saveVersion(\n policySet: PolicySet,\n reason: string,\n createdBy: string,\n ): PolicyVersion {\n const id = policySet.id;\n const history = this.versions.get(id) ?? [];\n\n const latestVersion = history.length > 0 ? history[history.length - 1]!.version : 0;\n\n const version: PolicyVersion = {\n version: latestVersion + 1,\n policySet: Object.freeze({ ...policySet }),\n hash: this.computeHash(policySet),\n reason,\n createdBy,\n createdAt: new Date().toISOString(),\n };\n\n const newHistory = [...history, version];\n this.versions.set(id, newHistory);\n\n return version;\n }\n\n /**\n * Gets a specific version of a policy set.\n */\n getVersion(id: string, version: number): PolicyVersion | null {\n const history = this.versions.get(id);\n if (!history) return null;\n return history.find((v) => v.version === version) ?? null;\n }\n\n /**\n * Gets the latest version of a policy set.\n */\n getLatest(id: string): PolicyVersion | null {\n const history = this.versions.get(id);\n if (!history || history.length === 0) return null;\n return history[history.length - 1]!;\n }\n\n /**\n * Gets the full version history of a policy set.\n */\n getHistory(id: string): readonly PolicyVersion[] {\n return this.versions.get(id) ?? [];\n }\n\n /**\n * Rolls back to a previous version by creating a new version\n * with the same content as the target version.\n */\n rollback(id: string, toVersion: number): PolicyVersion {\n const target = this.getVersion(id, toVersion);\n if (!target) {\n throw new Error(`Version ${toVersion} not found for policy \"${id}\"`);\n }\n\n return this.saveVersion(\n target.policySet,\n `Rollback to version ${toVersion}`,\n 'system',\n );\n }\n\n /**\n * Computes a diff between two policy versions.\n */\n diff(v1: PolicyVersion, v2: PolicyVersion): PolicyDiff {\n const oldRulesMap = new Map(v1.policySet.rules.map((r) => [r.id, r]));\n const newRulesMap = new Map(v2.policySet.rules.map((r) => [r.id, r]));\n\n const added: PolicyRule[] = [];\n const removed: PolicyRule[] = [];\n const modified: { old: PolicyRule; new: PolicyRule }[] = [];\n\n // Find added and modified rules\n for (const [id, newRule] of newRulesMap) {\n const oldRule = oldRulesMap.get(id);\n if (!oldRule) {\n added.push(newRule);\n } else if (JSON.stringify(oldRule) !== JSON.stringify(newRule)) {\n modified.push({ old: oldRule, new: newRule });\n }\n }\n\n // Find removed rules\n for (const [id, oldRule] of oldRulesMap) {\n if (!newRulesMap.has(id)) {\n removed.push(oldRule);\n }\n }\n\n return { added, removed, modified };\n }\n\n /**\n * Computes SHA256 hash of a policy set for integrity verification.\n */\n computeHash(policySet: PolicySet): string {\n const serialized = JSON.stringify(policySet, Object.keys(policySet).sort());\n return createHash('sha256').update(serialized).digest('hex');\n }\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@solongate/policy-engine",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": ["dist"],
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "dev": "tsup --watch",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest",
20
+ "typecheck": "tsc --noEmit",
21
+ "clean": "rm -rf dist .turbo"
22
+ },
23
+ "dependencies": {
24
+ "@solongate/core": "workspace:*",
25
+ "zod": "^3.25.0"
26
+ },
27
+ "devDependencies": {
28
+ "@solongate/tsconfig": "workspace:*",
29
+ "tsup": "^8.3.0",
30
+ "typescript": "^5.7.0",
31
+ "vitest": "^2.1.0"
32
+ }
33
+ }