@red-codes/policy 1.0.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,335 @@
1
+ // Policy evaluator — matches actions against loaded policies.
2
+ // Pure domain logic. No DOM, no Node.js-specific APIs.
3
+ import { PolicyMatcher } from '@red-codes/matchers';
4
+ function matchAction(pattern, action) {
5
+ return PolicyMatcher.matchAction(pattern, action);
6
+ }
7
+ function matchScope(scopePatterns, target) {
8
+ return PolicyMatcher.matchScope(scopePatterns, target);
9
+ }
10
+ function matchPersonaCondition(personaCond, persona) {
11
+ if (!persona)
12
+ return false;
13
+ if (personaCond.trustTier && personaCond.trustTier.length > 0) {
14
+ if (!persona.trustTier || !personaCond.trustTier.includes(persona.trustTier))
15
+ return false;
16
+ }
17
+ if (personaCond.role && personaCond.role.length > 0) {
18
+ if (!persona.role || !personaCond.role.includes(persona.role))
19
+ return false;
20
+ }
21
+ if (personaCond.autonomy && personaCond.autonomy.length > 0) {
22
+ if (!persona.autonomy || !personaCond.autonomy.includes(persona.autonomy))
23
+ return false;
24
+ }
25
+ if (personaCond.riskTolerance && personaCond.riskTolerance.length > 0) {
26
+ if (!persona.riskTolerance || !personaCond.riskTolerance.includes(persona.riskTolerance)) {
27
+ return false;
28
+ }
29
+ }
30
+ if (personaCond.tags && personaCond.tags.length > 0) {
31
+ if (!persona.tags || !personaCond.tags.some((t) => persona.tags.includes(t)))
32
+ return false;
33
+ }
34
+ return true;
35
+ }
36
+ function matchForecastCondition(forecastCond, forecast) {
37
+ if (!forecast)
38
+ return { matched: false, values: {} };
39
+ const values = {};
40
+ if (forecastCond.testRiskScore !== undefined) {
41
+ values.testRiskScore = {
42
+ actual: forecast.testRiskScore,
43
+ threshold: forecastCond.testRiskScore,
44
+ };
45
+ if (forecast.testRiskScore < forecastCond.testRiskScore) {
46
+ return { matched: false, values };
47
+ }
48
+ }
49
+ if (forecastCond.blastRadiusScore !== undefined) {
50
+ values.blastRadiusScore = {
51
+ actual: forecast.blastRadiusScore,
52
+ threshold: forecastCond.blastRadiusScore,
53
+ };
54
+ if (forecast.blastRadiusScore < forecastCond.blastRadiusScore) {
55
+ return { matched: false, values };
56
+ }
57
+ }
58
+ if (forecastCond.riskLevel && forecastCond.riskLevel.length > 0) {
59
+ values.riskLevel = { actual: forecast.riskLevel, required: forecastCond.riskLevel };
60
+ if (!forecastCond.riskLevel.includes(forecast.riskLevel)) {
61
+ return { matched: false, values };
62
+ }
63
+ }
64
+ if (forecastCond.predictedFileCount !== undefined) {
65
+ values.predictedFileCount = {
66
+ actual: forecast.predictedFiles.length,
67
+ threshold: forecastCond.predictedFileCount,
68
+ };
69
+ if (forecast.predictedFiles.length < forecastCond.predictedFileCount) {
70
+ return { matched: false, values };
71
+ }
72
+ }
73
+ if (forecastCond.dependencyCount !== undefined) {
74
+ values.dependencyCount = {
75
+ actual: forecast.dependenciesAffected.length,
76
+ threshold: forecastCond.dependencyCount,
77
+ };
78
+ if (forecast.dependenciesAffected.length < forecastCond.dependencyCount) {
79
+ return { matched: false, values };
80
+ }
81
+ }
82
+ return { matched: true, values };
83
+ }
84
+ function matchConditions(conditions, intent, effect = 'allow') {
85
+ if (!conditions)
86
+ return { matched: true };
87
+ // Gate conditions: skip this rule when the required flag is satisfied.
88
+ // For deny rules, this means the deny is bypassed when the condition passes.
89
+ if (conditions.requireTests && intent.metadata?.testsPass === true) {
90
+ return { matched: false };
91
+ }
92
+ if (conditions.requireFormat && intent.metadata?.formatPass === true) {
93
+ return { matched: false };
94
+ }
95
+ if (conditions.requireWorktree && intent.metadata?.inWorktree === true) {
96
+ return { matched: false, requireWorktreeMatched: true };
97
+ }
98
+ if (conditions.scope && !matchScope(conditions.scope, intent.target)) {
99
+ return { matched: false, scopeMatched: false };
100
+ }
101
+ const scopeMatched = conditions.scope ? true : undefined;
102
+ let limitExceeded;
103
+ let branchMatched;
104
+ let personaMatched;
105
+ let forecastMatched;
106
+ let forecastValues;
107
+ if (conditions.limit !== undefined && intent.filesAffected !== undefined) {
108
+ limitExceeded = intent.filesAffected > conditions.limit;
109
+ if (limitExceeded) {
110
+ return { matched: true, scopeMatched, limitExceeded };
111
+ }
112
+ }
113
+ if (conditions.branches) {
114
+ // `branches` acts as a required filter: the rule only matches when the intent's branch is
115
+ // in the configured list (or cannot be determined for deny rules).
116
+ //
117
+ // For deny rules: when branch is unknown (undefined), assume match (fail-closed / conservative).
118
+ // This prevents bypass via commands that defeat branch extraction (e.g. shell chains).
119
+ // For allow rules: when branch is unknown, do NOT match (fail-closed).
120
+ // This ensures actions aren't allowed without confirmed branch context.
121
+ if (intent.branch) {
122
+ branchMatched = conditions.branches.includes(intent.branch);
123
+ }
124
+ else {
125
+ // No branch detected — conservative approach based on rule effect:
126
+ // Deny rules: match (block the action to be safe)
127
+ // Allow rules: skip (don't grant access without branch confirmation)
128
+ branchMatched = effect === 'deny';
129
+ }
130
+ if (!branchMatched) {
131
+ return { matched: false, scopeMatched, limitExceeded, branchMatched };
132
+ }
133
+ }
134
+ if (conditions.persona) {
135
+ personaMatched = matchPersonaCondition(conditions.persona, intent.persona);
136
+ if (!personaMatched) {
137
+ return { matched: false, scopeMatched, limitExceeded, branchMatched, personaMatched };
138
+ }
139
+ }
140
+ if (conditions.forecast) {
141
+ const forecastResult = matchForecastCondition(conditions.forecast, intent.forecast);
142
+ forecastMatched = forecastResult.matched;
143
+ forecastValues = forecastResult.values;
144
+ if (!forecastMatched) {
145
+ return {
146
+ matched: false,
147
+ scopeMatched,
148
+ limitExceeded,
149
+ branchMatched,
150
+ personaMatched,
151
+ forecastMatched,
152
+ forecastValues,
153
+ };
154
+ }
155
+ }
156
+ return {
157
+ matched: true,
158
+ scopeMatched,
159
+ limitExceeded,
160
+ branchMatched,
161
+ personaMatched,
162
+ forecastMatched,
163
+ forecastValues,
164
+ };
165
+ }
166
+ function ruleKey(policyId, ruleIndex) {
167
+ return `${policyId}:${ruleIndex}`;
168
+ }
169
+ function createRuleEval(policy, ruleIndex, rule, actionMatched, conditionResult, outcome) {
170
+ return {
171
+ policyId: policy.id,
172
+ policyName: policy.name,
173
+ ruleIndex,
174
+ rule,
175
+ actionMatched,
176
+ conditionsMatched: conditionResult?.matched ?? false,
177
+ conditionDetails: conditionResult
178
+ ? {
179
+ scopeMatched: conditionResult.scopeMatched,
180
+ limitExceeded: conditionResult.limitExceeded,
181
+ branchMatched: conditionResult.branchMatched,
182
+ personaMatched: conditionResult.personaMatched,
183
+ forecastMatched: conditionResult.forecastMatched,
184
+ forecastValues: conditionResult.forecastValues,
185
+ requireWorktreeMatched: conditionResult.requireWorktreeMatched,
186
+ }
187
+ : {},
188
+ outcome,
189
+ };
190
+ }
191
+ export function evaluate(intent, policies, options) {
192
+ const startTime = performance.now();
193
+ const rulesEvaluated = [];
194
+ const ruleIndexMap = new Map();
195
+ if (!intent || !intent.action) {
196
+ return {
197
+ allowed: false,
198
+ decision: 'deny',
199
+ matchedRule: null,
200
+ matchedPolicy: null,
201
+ reason: 'Intent is missing required field: action',
202
+ severity: 5,
203
+ trace: {
204
+ rulesEvaluated: [],
205
+ totalRulesChecked: 0,
206
+ phaseThatMatched: null,
207
+ durationMs: performance.now() - startTime,
208
+ },
209
+ };
210
+ }
211
+ // Phase 1: Evaluate deny rules
212
+ for (const policy of policies) {
213
+ for (let ruleIndex = 0; ruleIndex < policy.rules.length; ruleIndex++) {
214
+ const rule = policy.rules[ruleIndex];
215
+ const key = ruleKey(policy.id, ruleIndex);
216
+ if (rule.effect !== 'deny') {
217
+ ruleIndexMap.set(key, rulesEvaluated.length);
218
+ rulesEvaluated.push(createRuleEval(policy, ruleIndex, rule, false, null, 'skipped'));
219
+ continue;
220
+ }
221
+ const actions = Array.isArray(rule.action) ? rule.action : [rule.action];
222
+ const actionMatched = actions.some((pattern) => matchAction(pattern, intent.action));
223
+ if (!actionMatched) {
224
+ ruleIndexMap.set(key, rulesEvaluated.length);
225
+ rulesEvaluated.push(createRuleEval(policy, ruleIndex, rule, false, null, 'no-match'));
226
+ continue;
227
+ }
228
+ const conditionResult = matchConditions(rule.conditions, intent, 'deny');
229
+ if (conditionResult.matched) {
230
+ ruleIndexMap.set(key, rulesEvaluated.length);
231
+ rulesEvaluated.push(createRuleEval(policy, ruleIndex, rule, true, conditionResult, 'match'));
232
+ return {
233
+ allowed: false,
234
+ decision: 'deny',
235
+ matchedRule: rule,
236
+ matchedPolicy: policy,
237
+ reason: rule.reason || `Denied by policy "${policy.name}"`,
238
+ severity: policy.severity,
239
+ policyIntervention: rule.intervention,
240
+ trace: {
241
+ rulesEvaluated,
242
+ totalRulesChecked: rulesEvaluated.length,
243
+ phaseThatMatched: 'deny',
244
+ durationMs: performance.now() - startTime,
245
+ },
246
+ };
247
+ }
248
+ ruleIndexMap.set(key, rulesEvaluated.length);
249
+ rulesEvaluated.push(createRuleEval(policy, ruleIndex, rule, true, conditionResult, 'no-match'));
250
+ }
251
+ }
252
+ // Phase 2: Evaluate allow rules
253
+ for (const policy of policies) {
254
+ for (let ruleIndex = 0; ruleIndex < policy.rules.length; ruleIndex++) {
255
+ const rule = policy.rules[ruleIndex];
256
+ if (rule.effect !== 'allow')
257
+ continue;
258
+ const key = ruleKey(policy.id, ruleIndex);
259
+ const existingIdx = ruleIndexMap.get(key);
260
+ const alreadyRecorded = existingIdx !== undefined;
261
+ const actions = Array.isArray(rule.action) ? rule.action : [rule.action];
262
+ const actionMatched = actions.some((pattern) => matchAction(pattern, intent.action));
263
+ if (!actionMatched) {
264
+ if (!alreadyRecorded) {
265
+ ruleIndexMap.set(key, rulesEvaluated.length);
266
+ rulesEvaluated.push(createRuleEval(policy, ruleIndex, rule, false, null, 'no-match'));
267
+ }
268
+ continue;
269
+ }
270
+ const conditionResult = matchConditions(rule.conditions, intent, 'allow');
271
+ if (conditionResult.matched) {
272
+ const evalRecord = createRuleEval(policy, ruleIndex, rule, true, conditionResult, 'match');
273
+ if (alreadyRecorded) {
274
+ rulesEvaluated[existingIdx] = evalRecord;
275
+ }
276
+ else {
277
+ ruleIndexMap.set(key, rulesEvaluated.length);
278
+ rulesEvaluated.push(evalRecord);
279
+ }
280
+ return {
281
+ allowed: true,
282
+ decision: 'allow',
283
+ matchedRule: rule,
284
+ matchedPolicy: policy,
285
+ reason: rule.reason || `Allowed by policy "${policy.name}"`,
286
+ severity: 0,
287
+ trace: {
288
+ rulesEvaluated,
289
+ totalRulesChecked: rulesEvaluated.filter((r) => r.outcome !== 'skipped').length,
290
+ phaseThatMatched: 'allow',
291
+ durationMs: performance.now() - startTime,
292
+ },
293
+ };
294
+ }
295
+ if (!alreadyRecorded) {
296
+ ruleIndexMap.set(key, rulesEvaluated.length);
297
+ rulesEvaluated.push(createRuleEval(policy, ruleIndex, rule, true, conditionResult, 'no-match'));
298
+ }
299
+ }
300
+ }
301
+ const defaultDeny = options?.defaultDeny ?? true;
302
+ if (defaultDeny) {
303
+ return {
304
+ allowed: false,
305
+ decision: 'deny',
306
+ matchedRule: null,
307
+ matchedPolicy: null,
308
+ reason: 'No matching policy rule — default deny (fail-closed)',
309
+ severity: 3,
310
+ trace: {
311
+ rulesEvaluated,
312
+ totalRulesChecked: rulesEvaluated.filter((r) => r.outcome !== 'skipped').length,
313
+ phaseThatMatched: 'default',
314
+ durationMs: performance.now() - startTime,
315
+ },
316
+ };
317
+ }
318
+ return {
319
+ allowed: true,
320
+ decision: 'allow',
321
+ matchedRule: null,
322
+ matchedPolicy: null,
323
+ reason: 'No matching policy rule — default allow (fail-open)',
324
+ severity: 0,
325
+ warning: 'SECURITY: Fail-open mode is active. All unmatched actions are allowed. Load a policy to enable governance.',
326
+ trace: {
327
+ rulesEvaluated,
328
+ totalRulesChecked: rulesEvaluated.filter((r) => r.outcome !== 'skipped').length,
329
+ phaseThatMatched: 'default',
330
+ durationMs: performance.now() - startTime,
331
+ },
332
+ };
333
+ }
334
+ export { matchAction, matchScope, matchPersonaCondition, matchForecastCondition };
335
+ //# sourceMappingURL=evaluator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluator.js","sourceRoot":"","sources":["../src/evaluator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export * from './evaluator.js';
2
+ export * from './loader.js';
3
+ export * from './yaml-loader.js';
4
+ export * from './pack-loader.js';
5
+ export * from './pack-version.js';
6
+ export * from './composer.js';
7
+ export * from './policy-trust.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export * from './evaluator.js';
2
+ export * from './loader.js';
3
+ export * from './yaml-loader.js';
4
+ export * from './pack-loader.js';
5
+ export * from './pack-version.js';
6
+ export * from './composer.js';
7
+ export * from './policy-trust.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { LoadedPolicy } from './evaluator.js';
2
+ export declare const VALID_ACTIONS: Set<string>;
3
+ interface ValidationResult {
4
+ valid: boolean;
5
+ errors: string[];
6
+ }
7
+ export declare function validatePolicy(policy: unknown): ValidationResult;
8
+ export declare function loadPolicies(policyDefs: unknown[]): {
9
+ policies: LoadedPolicy[];
10
+ errors: string[];
11
+ };
12
+ export {};
13
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAInD,eAAO,MAAM,aAAa,aAexB,CAAC;AAEH,UAAU,gBAAgB;IACxB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AA0ED,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,gBAAgB,CAyChE;AAED,wBAAgB,YAAY,CAAC,UAAU,EAAE,OAAO,EAAE,GAAG;IACnD,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAkCA"}
package/dist/loader.js ADDED
@@ -0,0 +1,151 @@
1
+ // Policy loader — parses and validates policy definitions.
2
+ // Pure domain logic. No DOM, no Node.js-specific APIs.
3
+ //
4
+ // Policy pack loading: see pack-loader.ts for extends/merge support.
5
+ const VALID_EFFECTS = new Set(['allow', 'deny']);
6
+ export const VALID_ACTIONS = new Set([
7
+ 'file.write',
8
+ 'file.delete',
9
+ 'file.rename',
10
+ 'shell.exec',
11
+ 'git.push',
12
+ 'git.force-push',
13
+ 'git.branch.delete',
14
+ 'git.commit',
15
+ 'git.merge',
16
+ 'config.modify',
17
+ 'dependency.add',
18
+ 'dependency.remove',
19
+ 'deploy.trigger',
20
+ '*',
21
+ ]);
22
+ function validateRule(rule) {
23
+ const errors = [];
24
+ if (!rule || typeof rule !== 'object') {
25
+ return { valid: false, errors: ['Rule must be a non-null object'] };
26
+ }
27
+ const r = rule;
28
+ if (!r.action) {
29
+ errors.push('Rule is missing required field: action');
30
+ }
31
+ else {
32
+ const actions = Array.isArray(r.action) ? r.action : [r.action];
33
+ for (const a of actions) {
34
+ if (typeof a !== 'string') {
35
+ errors.push(`Invalid action type: ${typeof a}`);
36
+ }
37
+ }
38
+ }
39
+ if (!r.effect) {
40
+ errors.push('Rule is missing required field: effect');
41
+ }
42
+ else if (!VALID_EFFECTS.has(r.effect)) {
43
+ errors.push(`Invalid effect: ${r.effect}. Must be "allow" or "deny"`);
44
+ }
45
+ if (r.conditions) {
46
+ if (typeof r.conditions !== 'object') {
47
+ errors.push('Conditions must be an object');
48
+ }
49
+ const conds = r.conditions;
50
+ if (conds.limit !== undefined && typeof conds.limit !== 'number') {
51
+ errors.push('Condition "limit" must be a number');
52
+ }
53
+ if (conds.forecast !== undefined) {
54
+ if (typeof conds.forecast !== 'object' || conds.forecast === null) {
55
+ errors.push('Condition "forecast" must be an object');
56
+ }
57
+ else {
58
+ const fc = conds.forecast;
59
+ if (fc.testRiskScore !== undefined && typeof fc.testRiskScore !== 'number') {
60
+ errors.push('Forecast condition "testRiskScore" must be a number');
61
+ }
62
+ if (fc.blastRadiusScore !== undefined && typeof fc.blastRadiusScore !== 'number') {
63
+ errors.push('Forecast condition "blastRadiusScore" must be a number');
64
+ }
65
+ if (fc.predictedFileCount !== undefined && typeof fc.predictedFileCount !== 'number') {
66
+ errors.push('Forecast condition "predictedFileCount" must be a number');
67
+ }
68
+ if (fc.dependencyCount !== undefined && typeof fc.dependencyCount !== 'number') {
69
+ errors.push('Forecast condition "dependencyCount" must be a number');
70
+ }
71
+ if (fc.riskLevel !== undefined) {
72
+ const validLevels = new Set(['low', 'medium', 'high']);
73
+ if (!Array.isArray(fc.riskLevel)) {
74
+ errors.push('Forecast condition "riskLevel" must be an array');
75
+ }
76
+ else {
77
+ for (const level of fc.riskLevel) {
78
+ if (!validLevels.has(level)) {
79
+ errors.push(`Forecast condition "riskLevel" contains invalid value: ${level}. Must be "low", "medium", or "high"`);
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ }
86
+ }
87
+ return { valid: errors.length === 0, errors };
88
+ }
89
+ export function validatePolicy(policy) {
90
+ const errors = [];
91
+ if (!policy || typeof policy !== 'object') {
92
+ return { valid: false, errors: ['Policy must be a non-null object'] };
93
+ }
94
+ const p = policy;
95
+ if (!p.id || typeof p.id !== 'string') {
96
+ errors.push('Policy is missing required field: id (string)');
97
+ }
98
+ if (!p.name || typeof p.name !== 'string') {
99
+ errors.push('Policy is missing required field: name (string)');
100
+ }
101
+ if (!Array.isArray(p.rules) || p.rules.length === 0) {
102
+ errors.push('Policy must have at least one rule');
103
+ }
104
+ else {
105
+ for (let i = 0; i < p.rules.length; i++) {
106
+ const result = validateRule(p.rules[i]);
107
+ if (!result.valid) {
108
+ for (const err of result.errors) {
109
+ errors.push(`Rule[${i}]: ${err}`);
110
+ }
111
+ }
112
+ }
113
+ }
114
+ if (p.severity !== undefined) {
115
+ if (typeof p.severity !== 'number' ||
116
+ p.severity < 1 ||
117
+ p.severity > 5) {
118
+ errors.push('Severity must be a number between 1 and 5');
119
+ }
120
+ }
121
+ return { valid: errors.length === 0, errors };
122
+ }
123
+ export function loadPolicies(policyDefs) {
124
+ const policies = [];
125
+ const errors = [];
126
+ if (!Array.isArray(policyDefs)) {
127
+ return { policies: [], errors: ['Policy definitions must be an array'] };
128
+ }
129
+ const seenIds = new Set();
130
+ for (let i = 0; i < policyDefs.length; i++) {
131
+ const def = policyDefs[i];
132
+ const result = validatePolicy(def);
133
+ if (!result.valid) {
134
+ for (const err of result.errors) {
135
+ errors.push(`Policy[${i}]: ${err}`);
136
+ }
137
+ continue;
138
+ }
139
+ if (seenIds.has(def.id)) {
140
+ errors.push(`Policy[${i}]: Duplicate policy ID "${def.id}"`);
141
+ continue;
142
+ }
143
+ seenIds.add(def.id);
144
+ policies.push({
145
+ ...def,
146
+ severity: def.severity ?? 3,
147
+ });
148
+ }
149
+ return { policies, errors };
150
+ }
151
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { LoadedPolicy } from './evaluator.js';
2
+ export interface PackResolutionResult {
3
+ policies: LoadedPolicy[];
4
+ errors: string[];
5
+ /** Non-fatal version warnings (e.g., version pin mismatch) */
6
+ warnings: string[];
7
+ }
8
+ /** Options for resolving and loading policy packs */
9
+ export interface PackLoadOptions {
10
+ /** The current AgentGuard version for compatibility checking (e.g., "2.2.0") */
11
+ currentAgentguardVersion?: string;
12
+ }
13
+ /**
14
+ * Resolve a single pack reference to an absolute file path.
15
+ *
16
+ * Supports three reference styles:
17
+ * 1. Relative path — `"./packs/strict"` or `"./packs/strict.yaml"`
18
+ * 2. Absolute path — `"/home/user/packs/strict.yaml"`
19
+ * 3. npm package — `"@agentguard/security-pack"` resolved from node_modules
20
+ *
21
+ * References may include a version constraint suffix (e.g., `"./pack@^1.2.0"`),
22
+ * which is stripped before path resolution.
23
+ */
24
+ export declare function resolvePackPath(ref: string, baseDir: string): string | null;
25
+ /**
26
+ * Load a single policy pack from a resolved file path.
27
+ */
28
+ export declare function loadPackFile(filePath: string): LoadedPolicy | null;
29
+ /**
30
+ * Resolve and load all policy packs from an `extends` list.
31
+ *
32
+ * @param extends_ - Array of pack references (paths, npm package names, or versioned refs like "pack@^1.0.0")
33
+ * @param baseDir - Directory to resolve relative paths from
34
+ * @param options - Optional settings including the current AgentGuard version for compatibility checks
35
+ * @returns Loaded pack policies, errors, and version warnings
36
+ */
37
+ export declare function resolveExtends(extends_: string[], baseDir: string, options?: PackLoadOptions): PackResolutionResult;
38
+ /**
39
+ * Merge pack policies with a local policy.
40
+ *
41
+ * Precedence: local rules override pack rules. Within packs, earlier entries
42
+ * in the `extends` list take precedence over later entries.
43
+ *
44
+ * The merge strategy is:
45
+ * 1. Collect all rules from packs (in extends order)
46
+ * 2. Append local rules (which take precedence during evaluation since
47
+ * the evaluator checks deny rules first, then allow rules)
48
+ * 3. Return a single merged policy array
49
+ */
50
+ export declare function mergePolicies(localPolicy: LoadedPolicy, packPolicies: LoadedPolicy[]): LoadedPolicy[];
51
+ //# sourceMappingURL=pack-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pack-loader.d.ts","sourceRoot":"","sources":["../src/pack-loader.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,gBAAgB,CAAC;AAc/D,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAyC3E;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAyBlE;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,MAAM,EAAE,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,eAAe,GACxB,oBAAoB,CA0DtB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,WAAW,EAAE,YAAY,EACzB,YAAY,EAAE,YAAY,EAAE,GAC3B,YAAY,EAAE,CAIhB"}