@highflame/policy 2.0.9 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_schemas/guardrails/context.json +435 -0
- package/_schemas/guardrails/schema.cedarschema +225 -0
- package/_schemas/guardrails/templates/defaults/agentic_safety.cedar +94 -0
- package/_schemas/guardrails/templates/defaults/baseline.cedar +24 -0
- package/_schemas/guardrails/templates/defaults/injection.cedar +70 -0
- package/_schemas/guardrails/templates/defaults/pii.cedar +48 -0
- package/_schemas/guardrails/templates/defaults/secrets.cedar +40 -0
- package/_schemas/guardrails/templates/defaults/semantic.cedar +59 -0
- package/_schemas/guardrails/templates/defaults/tool_risk.cedar +58 -0
- package/_schemas/guardrails/templates/defaults/toxicity.cedar +76 -0
- package/_schemas/guardrails/templates/mcp_tool_permissions.cedar +84 -0
- package/_schemas/guardrails/templates/profiles/chat_assistant/privacy.cedar +22 -0
- package/_schemas/guardrails/templates/profiles/chat_assistant/security.cedar +35 -0
- package/_schemas/guardrails/templates/profiles/chat_assistant/trust_safety.cedar +43 -0
- package/_schemas/guardrails/templates/profiles/chat_assistant.cedar +85 -0
- package/_schemas/guardrails/templates/profiles/code_agent/agentic_security.cedar +109 -0
- package/_schemas/guardrails/templates/profiles/code_agent/security.cedar +22 -0
- package/_schemas/guardrails/templates/profiles/code_agent.cedar +125 -0
- package/_schemas/guardrails/templates/profiles/data_pipeline/agentic_security.cedar +38 -0
- package/_schemas/guardrails/templates/profiles/data_pipeline/privacy.cedar +40 -0
- package/_schemas/guardrails/templates/profiles/data_pipeline/security.cedar +49 -0
- package/_schemas/guardrails/templates/profiles/data_pipeline.cedar +111 -0
- package/_schemas/guardrails/templates/templates.json +213 -0
- package/_schemas/overwatch/context.json +54 -54
- package/_schemas/overwatch/schema.cedarschema +77 -68
- package/dist/builder.d.ts +106 -13
- package/dist/builder.js +103 -34
- package/dist/engine.d.ts +20 -2
- package/dist/engine.js +50 -20
- package/dist/entities.gen.d.ts +4 -0
- package/dist/entities.gen.js +4 -0
- package/dist/explain.d.ts +150 -0
- package/dist/explain.js +363 -0
- package/dist/guardrails-context.gen.d.ts +49 -0
- package/dist/guardrails-context.gen.js +50 -0
- package/dist/guardrails-defaults.gen.d.ts +61 -0
- package/dist/guardrails-defaults.gen.js +1278 -0
- package/dist/guardrails-entities.gen.d.ts +11 -0
- package/dist/guardrails-entities.gen.js +37 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +6 -1
- package/dist/overwatch-defaults.gen.js +122 -2
- package/dist/parser.js +136 -4
- package/dist/schema.gen.d.ts +1 -1
- package/dist/schema.gen.js +6 -0
- package/dist/service-schemas.gen.d.ts +15 -11
- package/dist/service-schemas.gen.js +509 -84
- package/dist/types.d.ts +6 -1
- package/dist/types.js +6 -1
- package/package.json +5 -1
package/dist/engine.d.ts
CHANGED
|
@@ -38,11 +38,21 @@ export interface EngineOptions {
|
|
|
38
38
|
export declare class InputValidationError extends Error {
|
|
39
39
|
constructor(message: string);
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* A policy that contributed to the authorization decision,
|
|
43
|
+
* enriched with its Cedar annotations.
|
|
44
|
+
*/
|
|
45
|
+
export interface DeterminingPolicy {
|
|
46
|
+
/** Policy ID (from @id annotation or positional fallback) */
|
|
47
|
+
id: string;
|
|
48
|
+
/** All annotations from this policy as key-value pairs */
|
|
49
|
+
annotations: Record<string, string>;
|
|
50
|
+
}
|
|
41
51
|
export declare class Decision {
|
|
42
52
|
readonly effect: "Allow" | "Deny";
|
|
43
|
-
readonly determining_policies:
|
|
53
|
+
readonly determining_policies: DeterminingPolicy[];
|
|
44
54
|
readonly reason?: string;
|
|
45
|
-
constructor(effect: "Allow" | "Deny", determining_policies:
|
|
55
|
+
constructor(effect: "Allow" | "Deny", determining_policies: DeterminingPolicy[], reason?: string);
|
|
46
56
|
isAllowed(): boolean;
|
|
47
57
|
isDenied(): boolean;
|
|
48
58
|
}
|
|
@@ -63,6 +73,7 @@ export interface EvaluateRequest {
|
|
|
63
73
|
*/
|
|
64
74
|
export declare class PolicyEngine {
|
|
65
75
|
private policySet;
|
|
76
|
+
private policyAnnotations;
|
|
66
77
|
private schema;
|
|
67
78
|
private options;
|
|
68
79
|
private limits;
|
|
@@ -74,11 +85,13 @@ export declare class PolicyEngine {
|
|
|
74
85
|
/**
|
|
75
86
|
* Load a single Cedar policy text string.
|
|
76
87
|
* Uses @id annotations as policy IDs when available.
|
|
88
|
+
* Stores all annotations per policy for enriching evaluation results.
|
|
77
89
|
*/
|
|
78
90
|
loadPolicy(policy: string): void;
|
|
79
91
|
/**
|
|
80
92
|
* Load multiple Cedar policy texts (concatenated with newlines).
|
|
81
93
|
* Uses @id annotations as policy IDs when available.
|
|
94
|
+
* Stores all annotations per policy for enriching evaluation results.
|
|
82
95
|
*/
|
|
83
96
|
loadPolicies(policies: string[]): void;
|
|
84
97
|
/**
|
|
@@ -89,6 +102,11 @@ export declare class PolicyEngine {
|
|
|
89
102
|
* Load schema from a Cedar schema file.
|
|
90
103
|
*/
|
|
91
104
|
loadSchemaFromFile(path: string): void;
|
|
105
|
+
/**
|
|
106
|
+
* Returns stored annotations for a given policy ID.
|
|
107
|
+
* Returns undefined if the policy ID is not found.
|
|
108
|
+
*/
|
|
109
|
+
getPolicyAnnotations(policyId: string): Record<string, string> | undefined;
|
|
92
110
|
/**
|
|
93
111
|
* Evaluate a policy request and return a decision.
|
|
94
112
|
* @throws InputValidationError if context validation fails
|
package/dist/engine.js
CHANGED
|
@@ -147,39 +147,51 @@ function parseActionString(action) {
|
|
|
147
147
|
}
|
|
148
148
|
return { type: actionType, id: actionId };
|
|
149
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Regex to extract Cedar annotations from policy text.
|
|
152
|
+
* Matches `@key("value")` with proper escaped-quote handling.
|
|
153
|
+
*/
|
|
154
|
+
const ANNOTATION_REGEX = /@(\w+)\("((?:[^"\\]|\\.)*)"\)/g;
|
|
155
|
+
/**
|
|
156
|
+
* Extract all `@key("value")` annotations from a single Cedar policy text string.
|
|
157
|
+
*/
|
|
158
|
+
function extractAnnotationsFromText(policyText) {
|
|
159
|
+
const annotations = {};
|
|
160
|
+
let match;
|
|
161
|
+
const regex = new RegExp(ANNOTATION_REGEX.source, ANNOTATION_REGEX.flags);
|
|
162
|
+
while ((match = regex.exec(policyText)) !== null) {
|
|
163
|
+
// Unescape the annotation value (reverse Cedar escaping)
|
|
164
|
+
annotations[match[1]] = match[2].replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
165
|
+
}
|
|
166
|
+
return annotations;
|
|
167
|
+
}
|
|
150
168
|
/**
|
|
151
169
|
* Extract @id annotations from Cedar policy text and return a
|
|
152
|
-
* Record<PolicyId, Policy> for cedar-wasm
|
|
153
|
-
*
|
|
154
|
-
* instead of positional IDs (policy0, policy1...).
|
|
155
|
-
*
|
|
156
|
-
* Falls back to the raw string when no @id annotations are found.
|
|
170
|
+
* Record<PolicyId, Policy> for cedar-wasm, along with a map of
|
|
171
|
+
* all annotations per policy for enriching evaluation results.
|
|
157
172
|
*/
|
|
158
|
-
function
|
|
173
|
+
function extractPolicies(policyText) {
|
|
174
|
+
const annotationsMap = new Map();
|
|
159
175
|
const parts = cedar.policySetTextToParts(policyText);
|
|
160
176
|
if (parts.type !== "success" || parts.policies.length === 0) {
|
|
161
|
-
return policyText;
|
|
177
|
+
return { policySet: policyText, annotations: annotationsMap };
|
|
162
178
|
}
|
|
163
179
|
const policyMap = {};
|
|
164
|
-
let hasAnnotationIds = false;
|
|
165
180
|
for (let i = 0; i < parts.policies.length; i++) {
|
|
166
181
|
const policy = parts.policies[i];
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
policyMap[`policy${i}`] = policy;
|
|
174
|
-
}
|
|
182
|
+
const policyAnnotations = extractAnnotationsFromText(policy);
|
|
183
|
+
const policyId = policyAnnotations["id"] || `policy${i}`;
|
|
184
|
+
policyMap[policyId] = policy;
|
|
185
|
+
annotationsMap.set(policyId, policyAnnotations);
|
|
175
186
|
}
|
|
176
|
-
return
|
|
187
|
+
return { policySet: policyMap, annotations: annotationsMap };
|
|
177
188
|
}
|
|
178
189
|
/**
|
|
179
190
|
* PolicyEngine wraps cedar-wasm with Highflame schema types.
|
|
180
191
|
*/
|
|
181
192
|
export class PolicyEngine {
|
|
182
193
|
policySet = "";
|
|
194
|
+
policyAnnotations = new Map();
|
|
183
195
|
schema;
|
|
184
196
|
options;
|
|
185
197
|
limits;
|
|
@@ -203,16 +215,22 @@ export class PolicyEngine {
|
|
|
203
215
|
/**
|
|
204
216
|
* Load a single Cedar policy text string.
|
|
205
217
|
* Uses @id annotations as policy IDs when available.
|
|
218
|
+
* Stores all annotations per policy for enriching evaluation results.
|
|
206
219
|
*/
|
|
207
220
|
loadPolicy(policy) {
|
|
208
|
-
|
|
221
|
+
const extracted = extractPolicies(policy);
|
|
222
|
+
this.policySet = extracted.policySet;
|
|
223
|
+
this.policyAnnotations = extracted.annotations;
|
|
209
224
|
}
|
|
210
225
|
/**
|
|
211
226
|
* Load multiple Cedar policy texts (concatenated with newlines).
|
|
212
227
|
* Uses @id annotations as policy IDs when available.
|
|
228
|
+
* Stores all annotations per policy for enriching evaluation results.
|
|
213
229
|
*/
|
|
214
230
|
loadPolicies(policies) {
|
|
215
|
-
|
|
231
|
+
const extracted = extractPolicies(policies.join("\n"));
|
|
232
|
+
this.policySet = extracted.policySet;
|
|
233
|
+
this.policyAnnotations = extracted.annotations;
|
|
216
234
|
}
|
|
217
235
|
/**
|
|
218
236
|
* Load schema from a Cedar schema string.
|
|
@@ -227,6 +245,13 @@ export class PolicyEngine {
|
|
|
227
245
|
const content = fs.readFileSync(path, "utf-8");
|
|
228
246
|
this.loadSchema(content);
|
|
229
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Returns stored annotations for a given policy ID.
|
|
250
|
+
* Returns undefined if the policy ID is not found.
|
|
251
|
+
*/
|
|
252
|
+
getPolicyAnnotations(policyId) {
|
|
253
|
+
return this.policyAnnotations.get(policyId);
|
|
254
|
+
}
|
|
230
255
|
/**
|
|
231
256
|
* Evaluate a policy request and return a decision.
|
|
232
257
|
* @throws InputValidationError if context validation fails
|
|
@@ -276,7 +301,12 @@ export class PolicyEngine {
|
|
|
276
301
|
if (result.type === "failure") {
|
|
277
302
|
return new Decision("Deny", [], result.errors.map(e => e.message).join("; "));
|
|
278
303
|
}
|
|
279
|
-
|
|
304
|
+
// Build enriched DeterminingPolicy objects with annotations
|
|
305
|
+
const determiningPolicies = result.response.diagnostics.reason.map(id => ({
|
|
306
|
+
id,
|
|
307
|
+
annotations: this.policyAnnotations.get(id) || {},
|
|
308
|
+
}));
|
|
309
|
+
return new Decision(result.response.decision === "allow" ? "Allow" : "Deny", determiningPolicies, result.response.diagnostics.errors.length > 0
|
|
280
310
|
? result.response.diagnostics.errors.map(e => e.error.message).join("; ")
|
|
281
311
|
: undefined);
|
|
282
312
|
}
|
package/dist/entities.gen.d.ts
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* Entity types defined in the Highflame Cedar schema.
|
|
3
3
|
*/
|
|
4
4
|
export declare const EntityType: {
|
|
5
|
+
readonly Account: "Account";
|
|
5
6
|
readonly Agent: "Agent";
|
|
7
|
+
readonly App: "App";
|
|
6
8
|
readonly Artifact: "Artifact";
|
|
7
9
|
readonly ExternalAPI: "ExternalAPI";
|
|
8
10
|
readonly FilePath: "FilePath";
|
|
@@ -12,12 +14,14 @@ export declare const EntityType: {
|
|
|
12
14
|
readonly Memory: "Memory";
|
|
13
15
|
readonly Model: "Model";
|
|
14
16
|
readonly Package: "Package";
|
|
17
|
+
readonly Project: "Project";
|
|
15
18
|
readonly Repository: "Repository";
|
|
16
19
|
readonly Resource: "Resource";
|
|
17
20
|
readonly ResponseData: "ResponseData";
|
|
18
21
|
readonly Scanner: "Scanner";
|
|
19
22
|
readonly Server: "Server";
|
|
20
23
|
readonly Service: "Service";
|
|
24
|
+
readonly Session: "Session";
|
|
21
25
|
readonly Tool: "Tool";
|
|
22
26
|
readonly User: "User";
|
|
23
27
|
};
|
package/dist/entities.gen.js
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* Entity types defined in the Highflame Cedar schema.
|
|
5
5
|
*/
|
|
6
6
|
export const EntityType = {
|
|
7
|
+
Account: 'Account',
|
|
7
8
|
Agent: 'Agent',
|
|
9
|
+
App: 'App',
|
|
8
10
|
Artifact: 'Artifact',
|
|
9
11
|
ExternalAPI: 'ExternalAPI',
|
|
10
12
|
FilePath: 'FilePath',
|
|
@@ -14,12 +16,14 @@ export const EntityType = {
|
|
|
14
16
|
Memory: 'Memory',
|
|
15
17
|
Model: 'Model',
|
|
16
18
|
Package: 'Package',
|
|
19
|
+
Project: 'Project',
|
|
17
20
|
Repository: 'Repository',
|
|
18
21
|
Resource: 'Resource',
|
|
19
22
|
ResponseData: 'ResponseData',
|
|
20
23
|
Scanner: 'Scanner',
|
|
21
24
|
Server: 'Server',
|
|
22
25
|
Service: 'Service',
|
|
26
|
+
Session: 'Session',
|
|
23
27
|
Tool: 'Tool',
|
|
24
28
|
User: 'User',
|
|
25
29
|
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Decision Explanation
|
|
3
|
+
*
|
|
4
|
+
* Provides structured explanations for Cedar policy decisions by matching
|
|
5
|
+
* determining policies against their structured conditions and the request context.
|
|
6
|
+
*
|
|
7
|
+
* Browser-safe — no WASM or Node.js dependencies.
|
|
8
|
+
*/
|
|
9
|
+
import type { PolicyRule, PolicyEffect, ConditionOperator, ConditionExpression } from "./builder.js";
|
|
10
|
+
/**
|
|
11
|
+
* Duck-typed decision input — accepts the Decision class from engine.ts
|
|
12
|
+
* without importing cedar-wasm (keeps this module browser-safe).
|
|
13
|
+
*/
|
|
14
|
+
export interface DecisionInput {
|
|
15
|
+
effect: "Allow" | "Deny";
|
|
16
|
+
determining_policies: Array<{
|
|
17
|
+
id: string;
|
|
18
|
+
annotations: Record<string, string>;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Result of explaining a policy decision.
|
|
23
|
+
*/
|
|
24
|
+
export interface ExplainedDecision {
|
|
25
|
+
/** The original decision effect */
|
|
26
|
+
effect: "Allow" | "Deny";
|
|
27
|
+
/** Enriched explanations for each determining policy */
|
|
28
|
+
explanations: PolicyExplanation[];
|
|
29
|
+
/** Determining policy IDs that had no matching rule in the provided rules array */
|
|
30
|
+
unmatched_policies: string[];
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Explanation for a single determining policy.
|
|
34
|
+
*/
|
|
35
|
+
export interface PolicyExplanation {
|
|
36
|
+
/** Policy ID */
|
|
37
|
+
policy_id: string;
|
|
38
|
+
/** Policy effect (permit or forbid) */
|
|
39
|
+
effect: PolicyEffect;
|
|
40
|
+
/** Human-readable summary */
|
|
41
|
+
summary: string;
|
|
42
|
+
/** Recursive evaluated condition tree with actual values (from conditionExpression) */
|
|
43
|
+
evaluated_expression?: EvaluatedExpression;
|
|
44
|
+
/** Per-condition match results (flat, from structured conditions[]) */
|
|
45
|
+
condition_results: ConditionResult[];
|
|
46
|
+
/** Raw Cedar condition text if the policy uses rawCondition instead of structured conditions */
|
|
47
|
+
raw_condition?: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Result of evaluating a single condition against the request context.
|
|
51
|
+
*/
|
|
52
|
+
export interface ConditionResult {
|
|
53
|
+
/** Context field name */
|
|
54
|
+
field: string;
|
|
55
|
+
/** Comparison operator */
|
|
56
|
+
operator: ConditionOperator;
|
|
57
|
+
/** Expected value (threshold from the rule) */
|
|
58
|
+
expected: string | number | boolean | string[];
|
|
59
|
+
/** Actual value from the context (undefined if field was missing) */
|
|
60
|
+
actual?: unknown;
|
|
61
|
+
/** Whether this condition matched */
|
|
62
|
+
matched: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** Evaluated comparison: context.field <op> value */
|
|
65
|
+
export interface EvaluatedComparison {
|
|
66
|
+
kind: 'comparison';
|
|
67
|
+
field: string;
|
|
68
|
+
operator: ConditionOperator;
|
|
69
|
+
expected: string | number | boolean | string[];
|
|
70
|
+
actual: unknown;
|
|
71
|
+
matched: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** Evaluated contains: context.field.contains(value) */
|
|
74
|
+
export interface EvaluatedContains {
|
|
75
|
+
kind: 'contains';
|
|
76
|
+
field: string;
|
|
77
|
+
expected: string | number | boolean;
|
|
78
|
+
actual: unknown;
|
|
79
|
+
matched: boolean;
|
|
80
|
+
}
|
|
81
|
+
/** Evaluated like: context.field like "pattern" */
|
|
82
|
+
export interface EvaluatedLike {
|
|
83
|
+
kind: 'like';
|
|
84
|
+
field: string;
|
|
85
|
+
pattern: string;
|
|
86
|
+
actual: unknown;
|
|
87
|
+
matched: boolean;
|
|
88
|
+
}
|
|
89
|
+
/** Evaluated has: context has field */
|
|
90
|
+
export interface EvaluatedHas {
|
|
91
|
+
kind: 'has';
|
|
92
|
+
field: string;
|
|
93
|
+
matched: boolean;
|
|
94
|
+
}
|
|
95
|
+
/** Evaluated AND */
|
|
96
|
+
export interface EvaluatedAnd {
|
|
97
|
+
kind: 'and';
|
|
98
|
+
children: EvaluatedExpression[];
|
|
99
|
+
matched: boolean;
|
|
100
|
+
}
|
|
101
|
+
/** Evaluated OR */
|
|
102
|
+
export interface EvaluatedOr {
|
|
103
|
+
kind: 'or';
|
|
104
|
+
children: EvaluatedExpression[];
|
|
105
|
+
matched: boolean;
|
|
106
|
+
}
|
|
107
|
+
/** Evaluated NOT */
|
|
108
|
+
export interface EvaluatedNot {
|
|
109
|
+
kind: 'not';
|
|
110
|
+
child: EvaluatedExpression;
|
|
111
|
+
matched: boolean;
|
|
112
|
+
}
|
|
113
|
+
/** Evaluated raw (cannot be decomposed) */
|
|
114
|
+
export interface EvaluatedRaw {
|
|
115
|
+
kind: 'raw';
|
|
116
|
+
text: string;
|
|
117
|
+
matched: boolean;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Recursive evaluated condition expression tree.
|
|
121
|
+
* Produced by evaluateExpression() by walking ConditionExpression against context.
|
|
122
|
+
* Every node has a `matched` boolean; leaf nodes include `actual` values.
|
|
123
|
+
*/
|
|
124
|
+
export type EvaluatedExpression = EvaluatedComparison | EvaluatedContains | EvaluatedLike | EvaluatedHas | EvaluatedAnd | EvaluatedOr | EvaluatedNot | EvaluatedRaw;
|
|
125
|
+
/**
|
|
126
|
+
* Explain a policy decision by matching determining policies against their
|
|
127
|
+
* structured conditions and the request context.
|
|
128
|
+
*
|
|
129
|
+
* @param decision - The decision from PolicyEngine.evaluate()
|
|
130
|
+
* @param rules - The PolicyRule[] that were loaded (parsed or built)
|
|
131
|
+
* @param context - The context map that was passed to evaluate()
|
|
132
|
+
* @returns Structured explanation with per-condition match details
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* const decision = engine.evaluate(request);
|
|
137
|
+
* const explained = explainDecision(decision, rules, request.context);
|
|
138
|
+
*
|
|
139
|
+
* for (const explanation of explained.explanations) {
|
|
140
|
+
* console.log(explanation.summary);
|
|
141
|
+
* // "forbid process_prompt — threat_count (10) > 5"
|
|
142
|
+
* }
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
export declare function explainDecision(decision: DecisionInput, rules: PolicyRule[], context: Record<string, unknown>): ExplainedDecision;
|
|
146
|
+
/**
|
|
147
|
+
* Recursively evaluate a ConditionExpression tree against a context map.
|
|
148
|
+
* Returns an EvaluatedExpression tree with `matched` booleans and `actual` values.
|
|
149
|
+
*/
|
|
150
|
+
export declare function evaluateExpression(expr: ConditionExpression, context: Record<string, unknown>): EvaluatedExpression;
|