@highflame/policy 2.0.9 → 2.0.10

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.
@@ -100,61 +100,61 @@
100
100
  {
101
101
  "key": "violence_score",
102
102
  "type": "number",
103
- "required": false,
103
+ "required": true,
104
104
  "description": "Violence content detection score (0-100)"
105
105
  },
106
106
  {
107
107
  "key": "weapons_score",
108
108
  "type": "number",
109
- "required": false,
109
+ "required": true,
110
110
  "description": "Weapons content detection score (0-100)"
111
111
  },
112
112
  {
113
113
  "key": "hate_speech_score",
114
114
  "type": "number",
115
- "required": false,
115
+ "required": true,
116
116
  "description": "Hate speech detection score (0-100)"
117
117
  },
118
118
  {
119
119
  "key": "crime_score",
120
120
  "type": "number",
121
- "required": false,
121
+ "required": true,
122
122
  "description": "Criminal content detection score (0-100)"
123
123
  },
124
124
  {
125
125
  "key": "sexual_score",
126
126
  "type": "number",
127
- "required": false,
127
+ "required": true,
128
128
  "description": "Sexual content detection score (0-100)"
129
129
  },
130
130
  {
131
131
  "key": "profanity_score",
132
132
  "type": "number",
133
- "required": false,
133
+ "required": true,
134
134
  "description": "Profanity detection score (0-100)"
135
135
  },
136
136
  {
137
137
  "key": "pii_confidence",
138
138
  "type": "number",
139
- "required": false,
139
+ "required": true,
140
140
  "description": "PII detection classifier confidence (0-100)"
141
141
  },
142
142
  {
143
143
  "key": "injection_confidence",
144
144
  "type": "number",
145
- "required": false,
145
+ "required": true,
146
146
  "description": "Prompt injection classifier confidence (0-100)"
147
147
  },
148
148
  {
149
149
  "key": "jailbreak_confidence",
150
150
  "type": "number",
151
- "required": false,
151
+ "required": true,
152
152
  "description": "Jailbreak detection classifier confidence (0-100)"
153
153
  },
154
154
  {
155
155
  "key": "indirect_injection_score",
156
156
  "type": "number",
157
- "required": false,
157
+ "required": true,
158
158
  "description": "Indirect prompt injection risk score (0-100)"
159
159
  }
160
160
  ]
@@ -226,44 +226,44 @@
226
226
  {
227
227
  "key": "threat_count",
228
228
  "type": "number",
229
- "required": true,
230
- "description": "Total threats detected"
229
+ "required": false,
230
+ "description": "Total threats detected (if scanning ran)"
231
231
  },
232
232
  {
233
233
  "key": "highest_severity",
234
234
  "type": "string",
235
- "required": true,
236
- "description": "Highest severity: critical, high, medium, low"
235
+ "required": false,
236
+ "description": "Highest severity (if scanning ran)"
237
237
  },
238
238
  {
239
239
  "key": "threat_categories",
240
240
  "type": "array",
241
- "required": true,
242
- "description": "Threat category names"
241
+ "required": false,
242
+ "description": "Threat category names (if scanning ran)"
243
243
  },
244
244
  {
245
245
  "key": "threat_types",
246
246
  "type": "array",
247
- "required": true,
248
- "description": "YARA threat categories"
247
+ "required": false,
248
+ "description": "YARA threat categories (if scanning ran)"
249
249
  },
250
250
  {
251
251
  "key": "yara_threats",
252
252
  "type": "array",
253
- "required": true,
254
- "description": "YARA rule names"
253
+ "required": false,
254
+ "description": "YARA rule names (if scanning ran)"
255
255
  },
256
256
  {
257
257
  "key": "max_threat_severity",
258
258
  "type": "number",
259
- "required": true,
260
- "description": "Numeric severity (0-4)"
259
+ "required": false,
260
+ "description": "Numeric severity 0-4 (if scanning ran)"
261
261
  },
262
262
  {
263
263
  "key": "contains_secrets",
264
264
  "type": "boolean",
265
- "required": true,
266
- "description": "Whether secrets detected"
265
+ "required": false,
266
+ "description": "Whether secrets detected (if scanning ran)"
267
267
  },
268
268
  {
269
269
  "key": "response_content",
@@ -358,8 +358,8 @@
358
358
  {
359
359
  "key": "content",
360
360
  "type": "string",
361
- "required": true,
362
- "description": "Raw content being scanned"
361
+ "required": false,
362
+ "description": "Raw content being scanned (if available)"
363
363
  },
364
364
  {
365
365
  "key": "source",
@@ -388,26 +388,26 @@
388
388
  {
389
389
  "key": "threat_count",
390
390
  "type": "number",
391
- "required": true,
392
- "description": "Total threats detected"
391
+ "required": false,
392
+ "description": "Total threats detected (if scanning ran)"
393
393
  },
394
394
  {
395
395
  "key": "highest_severity",
396
396
  "type": "string",
397
- "required": true,
398
- "description": "Highest severity level"
397
+ "required": false,
398
+ "description": "Highest severity level (if scanning ran)"
399
399
  },
400
400
  {
401
401
  "key": "threat_categories",
402
402
  "type": "array",
403
- "required": true,
404
- "description": "Threat category names"
403
+ "required": false,
404
+ "description": "Threat category names (if scanning ran)"
405
405
  },
406
406
  {
407
407
  "key": "max_threat_severity",
408
408
  "type": "number",
409
- "required": true,
410
- "description": "Numeric severity (0-4)"
409
+ "required": false,
410
+ "description": "Numeric severity 0-4 (if scanning ran)"
411
411
  },
412
412
  {
413
413
  "key": "tool_poisoning_score",
@@ -484,32 +484,32 @@
484
484
  {
485
485
  "key": "threat_count",
486
486
  "type": "number",
487
- "required": true,
488
- "description": "Total threats detected"
487
+ "required": false,
488
+ "description": "Total threats detected (if scanning ran)"
489
489
  },
490
490
  {
491
491
  "key": "highest_severity",
492
492
  "type": "string",
493
- "required": true,
494
- "description": "Highest severity level"
493
+ "required": false,
494
+ "description": "Highest severity level (if scanning ran)"
495
495
  },
496
496
  {
497
497
  "key": "threat_categories",
498
498
  "type": "array",
499
- "required": true,
500
- "description": "Threat categories"
499
+ "required": false,
500
+ "description": "Threat categories (if scanning ran)"
501
501
  },
502
502
  {
503
503
  "key": "max_threat_severity",
504
504
  "type": "number",
505
- "required": true,
506
- "description": "Numeric severity (0-4)"
505
+ "required": false,
506
+ "description": "Numeric severity 0-4 (if scanning ran)"
507
507
  },
508
508
  {
509
509
  "key": "contains_secrets",
510
510
  "type": "boolean",
511
- "required": true,
512
- "description": "Whether secrets detected"
511
+ "required": false,
512
+ "description": "Whether secrets detected (if scanning ran)"
513
513
  }
514
514
  ]
515
515
  },
@@ -562,32 +562,32 @@
562
562
  {
563
563
  "key": "threat_count",
564
564
  "type": "number",
565
- "required": true,
566
- "description": "Total threats detected"
565
+ "required": false,
566
+ "description": "Total threats detected (if scanning ran)"
567
567
  },
568
568
  {
569
569
  "key": "highest_severity",
570
570
  "type": "string",
571
- "required": true,
572
- "description": "Highest severity level"
571
+ "required": false,
572
+ "description": "Highest severity level (if scanning ran)"
573
573
  },
574
574
  {
575
575
  "key": "threat_categories",
576
576
  "type": "array",
577
- "required": true,
578
- "description": "Threat categories"
577
+ "required": false,
578
+ "description": "Threat categories (if scanning ran)"
579
579
  },
580
580
  {
581
581
  "key": "max_threat_severity",
582
582
  "type": "number",
583
- "required": true,
584
- "description": "Numeric severity (0-4)"
583
+ "required": false,
584
+ "description": "Numeric severity 0-4 (if scanning ran)"
585
585
  },
586
586
  {
587
587
  "key": "contains_secrets",
588
588
  "type": "boolean",
589
- "required": true,
590
- "description": "Whether secrets detected"
589
+ "required": false,
590
+ "description": "Whether secrets detected (if scanning ran)"
591
591
  }
592
592
  ]
593
593
  }
@@ -84,8 +84,8 @@ action process_prompt appliesTo {
84
84
  user_email: String, // User identifier
85
85
 
86
86
  // Workspace
87
- cwd: String, // Current working directory
88
- workspace_root: String, // Workspace/repository root
87
+ cwd?: String, // Current working directory
88
+ workspace_root?: String, // Workspace/repository root
89
89
 
90
90
  // Threat Detection
91
91
  threat_count: Long, // Total threats detected
@@ -94,24 +94,27 @@ action process_prompt appliesTo {
94
94
  yara_threats: Set<String>, // YARA rule names
95
95
  max_threat_severity: Long, // Numeric severity (0-4)
96
96
  contains_secrets: Bool, // Whether secrets detected
97
- prompt_text: String, // Same as content (legacy)
98
- response_content: String, // Response content (if available)
97
+ prompt_text?: String, // Same as content (legacy)
98
+ response_content?: String, // Response content (if available)
99
99
 
100
100
  // Trust/Safety Scores (0-100, from Javelin/Lakera/LlamaGuard classifiers)
101
- violence_score: Long, // Violence content detection score
102
- weapons_score: Long, // Weapons content detection score
103
- hate_speech_score: Long, // Hate speech detection score
104
- crime_score: Long, // Criminal content detection score
105
- sexual_score: Long, // Sexual content detection score
106
- profanity_score: Long, // Profanity detection score
101
+ // Required: content safety classifiers always run for prompt processing
102
+ violence_score: Long, // Violence content detection score
103
+ weapons_score: Long, // Weapons content detection score
104
+ hate_speech_score: Long, // Hate speech detection score
105
+ crime_score: Long, // Criminal content detection score
106
+ sexual_score: Long, // Sexual content detection score
107
+ profanity_score: Long, // Profanity detection score
107
108
 
108
109
  // Detector Confidence Scores (0-100, ML classifier confidence)
109
- pii_confidence: Long, // PII detection confidence
110
- injection_confidence: Long, // Prompt injection confidence
111
- jailbreak_confidence: Long, // Jailbreak detection confidence
110
+ // Required: ML classifiers always run for prompt processing
111
+ pii_confidence: Long, // PII detection confidence
112
+ injection_confidence: Long, // Prompt injection confidence
113
+ jailbreak_confidence: Long, // Jailbreak detection confidence
112
114
 
113
115
  // Agent Security (0-100)
114
- indirect_injection_score: Long, // Indirect prompt injection risk
116
+ // Required: agent security scanners always run for prompt processing
117
+ indirect_injection_score: Long, // Indirect prompt injection risk
115
118
  },
116
119
  };
117
120
 
@@ -127,46 +130,50 @@ action call_tool appliesTo {
127
130
  user_email: String, // User identifier
128
131
 
129
132
  // Tool & MCP
130
- tool_name: String, // Normalized tool name ("shell", "read_file", etc.)
131
- mcp_server: String, // MCP server name
132
- mcp_tool: String, // MCP tool name
133
+ tool_name?: String, // Normalized tool name ("shell", "read_file", etc.)
134
+ mcp_server?: String, // MCP server name
135
+ mcp_tool?: String, // MCP tool name
133
136
 
134
137
  // File & Path
135
- path: String, // File path (if file operation)
138
+ path?: String, // File path (if file operation)
136
139
 
137
140
  // Workspace
138
- cwd: String,
139
- workspace_root: String,
140
-
141
- // Threat Detection
142
- threat_count: Long,
143
- highest_severity: String,
144
- threat_categories: Set<String>,
145
- yara_threats: Set<String>,
146
- max_threat_severity: Long,
147
- contains_secrets: Bool,
148
- response_content: String,
141
+ cwd?: String,
142
+ workspace_root?: String,
143
+
144
+ // Threat Detection (optional: scanning may not have run before tool call)
145
+ threat_count?: Long,
146
+ highest_severity?: String,
147
+ threat_categories?: Set<String>,
148
+ yara_threats?: Set<String>,
149
+ max_threat_severity?: Long,
150
+ contains_secrets?: Bool,
151
+ response_content?: String,
149
152
 
150
153
  // Trust/Safety Scores (0-100, from Javelin/Lakera/LlamaGuard classifiers)
151
- violence_score: Long, // Violence content detection score
152
- weapons_score: Long, // Weapons content detection score
153
- hate_speech_score: Long, // Hate speech detection score
154
- crime_score: Long, // Criminal content detection score
155
- sexual_score: Long, // Sexual content detection score
156
- profanity_score: Long, // Profanity detection score
154
+ // Optional: only present when trust/safety classifiers have run
155
+ violence_score?: Long, // Violence content detection score
156
+ weapons_score?: Long, // Weapons content detection score
157
+ hate_speech_score?: Long, // Hate speech detection score
158
+ crime_score?: Long, // Criminal content detection score
159
+ sexual_score?: Long, // Sexual content detection score
160
+ profanity_score?: Long, // Profanity detection score
157
161
 
158
162
  // Detector Confidence Scores (0-100, ML classifier confidence)
159
- pii_confidence: Long, // PII detection confidence
160
- injection_confidence: Long, // Prompt injection confidence
161
- jailbreak_confidence: Long, // Jailbreak detection confidence
163
+ // Optional: only present when ML classifiers have run
164
+ pii_confidence?: Long, // PII detection confidence
165
+ injection_confidence?: Long, // Prompt injection confidence
166
+ jailbreak_confidence?: Long, // Jailbreak detection confidence
162
167
 
163
168
  // Agent Security (0-100)
164
- tool_poisoning_score: Long, // Tool description manipulation risk
165
- rug_pull_score: Long, // Tool behavior mismatch risk
166
- indirect_injection_score: Long, // Indirect prompt injection risk
169
+ // Optional: only present when agent security scanners have run
170
+ tool_poisoning_score?: Long, // Tool description manipulation risk
171
+ rug_pull_score?: Long, // Tool behavior mismatch risk
172
+ indirect_injection_score?: Long, // Indirect prompt injection risk
167
173
 
168
174
  // MCP Trust
169
- mcp_server_verified: Bool, // Whether server is from verified registry
175
+ // Optional: only present when MCP server verification has run
176
+ mcp_server_verified?: Bool, // Whether server is from verified registry
170
177
  },
171
178
  };
172
179
 
@@ -175,23 +182,25 @@ action connect_server appliesTo {
175
182
  principal: [User, Agent],
176
183
  resource: [Server],
177
184
  context: {
178
- content: String,
185
+ content?: String, // No content to scan when connecting
179
186
  source: String,
180
187
  event: String,
181
188
  user_email: String,
182
- mcp_server: String,
183
- threat_count: Long,
184
- highest_severity: String,
185
- threat_categories: Set<String>,
186
- max_threat_severity: Long,
189
+ mcp_server?: String,
190
+ threat_count?: Long, // Threat scanning may not run for connections
191
+ highest_severity?: String,
192
+ threat_categories?: Set<String>,
193
+ max_threat_severity?: Long,
187
194
 
188
195
  // Agent Security (0-100)
189
- tool_poisoning_score: Long, // Tool description manipulation risk
190
- rug_pull_score: Long, // Tool behavior mismatch risk
191
- indirect_injection_score: Long, // Indirect prompt injection risk
196
+ // Optional: only present when agent security scanners have run
197
+ tool_poisoning_score?: Long, // Tool description manipulation risk
198
+ rug_pull_score?: Long, // Tool behavior mismatch risk
199
+ indirect_injection_score?: Long, // Indirect prompt injection risk
192
200
 
193
201
  // MCP Trust
194
- mcp_server_verified: Bool, // Whether server is from verified registry
202
+ // Optional: only present when MCP server verification has run
203
+ mcp_server_verified?: Bool, // Whether server is from verified registry
195
204
  },
196
205
  };
197
206
 
@@ -204,14 +213,14 @@ action read_file appliesTo {
204
213
  source: String,
205
214
  event: String,
206
215
  user_email: String,
207
- path: String,
208
- cwd: String,
209
- workspace_root: String,
210
- threat_count: Long,
211
- highest_severity: String,
212
- threat_categories: Set<String>,
213
- max_threat_severity: Long,
214
- contains_secrets: Bool,
216
+ path?: String,
217
+ cwd?: String,
218
+ workspace_root?: String,
219
+ threat_count?: Long, // Threat scanning may not have run
220
+ highest_severity?: String,
221
+ threat_categories?: Set<String>,
222
+ max_threat_severity?: Long,
223
+ contains_secrets?: Bool,
215
224
  },
216
225
  };
217
226
 
@@ -224,14 +233,14 @@ action write_file appliesTo {
224
233
  source: String,
225
234
  event: String,
226
235
  user_email: String,
227
- path: String,
228
- cwd: String,
229
- workspace_root: String,
230
- threat_count: Long,
231
- highest_severity: String,
232
- threat_categories: Set<String>,
233
- max_threat_severity: Long,
234
- contains_secrets: Bool,
236
+ path?: String,
237
+ cwd?: String,
238
+ workspace_root?: String,
239
+ threat_count?: Long, // Threat scanning may not have run
240
+ highest_severity?: String,
241
+ threat_categories?: Set<String>,
242
+ max_threat_severity?: Long,
243
+ contains_secrets?: Bool,
235
244
  },
236
245
  };
237
246
 
package/dist/builder.d.ts CHANGED
@@ -33,6 +33,7 @@
33
33
  import { EntityType, EntityUID } from './entities.gen.js';
34
34
  import { ActionType } from './actions.gen.js';
35
35
  import { type PolicyAnnotations, type CustomAnnotations, type PolicySeverity } from './annotations.js';
36
+ import type { ServiceContext } from './service-schemas.gen.js';
36
37
  /**
37
38
  * Policy effect - permit or forbid
38
39
  */
@@ -163,8 +164,11 @@ export declare class Policy {
163
164
  /**
164
165
  * Convert to Cedar policy text.
165
166
  * Uses proper Cedar @annotation syntax.
167
+ *
168
+ * @param optionalFields - Set of context field names that are optional and need `context has` guards.
169
+ * Use `getOptionalFields()` to compute this from service context metadata.
166
170
  */
167
- toCedar(): string;
171
+ toCedar(optionalFields?: Set<string>): string;
168
172
  /**
169
173
  * Get JSON representation for storage
170
174
  */
@@ -178,10 +182,35 @@ export declare class Policy {
178
182
  */
179
183
  getName(): string | undefined;
180
184
  }
185
+ /**
186
+ * Get the set of optional context fields for the given action(s).
187
+ *
188
+ * A field is considered optional if it has `required: false` in ANY of the
189
+ * targeted actions. This is the safe choice because at evaluation time,
190
+ * the policy could be matched against any of the specified actions.
191
+ *
192
+ * @param serviceContext - The service context metadata (from OVERWATCH_CONTEXT, etc.)
193
+ * @param actions - Single action name or array of action names
194
+ * @returns Set of field names that are optional and need `context has` guards
195
+ *
196
+ * @example
197
+ * ```typescript
198
+ * import { OVERWATCH_CONTEXT } from '@highflame/policy/types';
199
+ *
200
+ * const optionalFields = getOptionalFields(OVERWATCH_CONTEXT, 'call_tool');
201
+ * // Set { 'tool_name', 'mcp_server', 'threat_count', ... }
202
+ *
203
+ * const cedar = ruleToCedar(rule, optionalFields);
204
+ * // Conditions on optional fields auto-get `context has` guards
205
+ * ```
206
+ */
207
+ export declare function getOptionalFields(serviceContext: ServiceContext, actions: string | string[]): Set<string>;
181
208
  /**
182
209
  * Convert a PolicyRule to Cedar policy text with proper annotations.
183
210
  *
184
211
  * @param rule - The PolicyRule to convert
212
+ * @param optionalFields - Set of context field names that are optional and need `context has` guards.
213
+ * Use `getOptionalFields()` to compute this from service context metadata.
185
214
  * @returns Cedar policy text string
186
215
  *
187
216
  * @example
@@ -197,35 +226,38 @@ export declare class Policy {
197
226
  * order: 0,
198
227
  * };
199
228
  *
229
+ * // Without optional fields - no guards injected
200
230
  * const cedar = ruleToCedar(rule);
201
- * // Output:
202
- * // @id("rule-001")
203
- * // @name("Block threats")
204
- * // @severity("high")
205
- * // forbid (
206
- * // principal,
207
- * // action == Action::"call_tool",
208
- * // resource
209
- * // )
210
- * // when { context.threat_count > 0 };
231
+ *
232
+ * // With optional fields - auto-injects `context has` guards
233
+ * import { OVERWATCH_CONTEXT } from '@highflame/policy/types';
234
+ * const optionalFields = getOptionalFields(OVERWATCH_CONTEXT, 'call_tool');
235
+ * const cedarWithGuards = ruleToCedar(rule, optionalFields);
236
+ * // when { context has threat_count && context.threat_count > 0 };
211
237
  * ```
212
238
  */
213
- export declare function ruleToCedar(rule: PolicyRule): string;
239
+ export declare function ruleToCedar(rule: PolicyRule, optionalFields?: Set<string>): string;
214
240
  /**
215
241
  * Convert multiple PolicyRules to Cedar policy text.
216
242
  * Only enabled rules are included, sorted by order.
217
243
  *
218
244
  * @param rules - Array of PolicyRules to convert
219
245
  * @param includeDisabled - If true, include disabled rules as comments (default: false)
246
+ * @param optionalFields - Set of context field names that are optional and need `context has` guards.
247
+ * Use `getOptionalFields()` to compute this from service context metadata.
220
248
  * @returns Cedar policy text with all rules separated by blank lines
221
249
  *
222
250
  * @example
223
251
  * ```typescript
224
252
  * const rules: PolicyRule[] = [...];
225
253
  * const cedarText = rulesToCedar(rules);
254
+ *
255
+ * // With optional fields awareness
256
+ * const optionalFields = getOptionalFields(OVERWATCH_CONTEXT, 'call_tool');
257
+ * const cedarText = rulesToCedar(rules, false, optionalFields);
226
258
  * ```
227
259
  */
228
- export declare function rulesToCedar(rules: PolicyRule[], includeDisabled?: boolean): string;
260
+ export declare function rulesToCedar(rules: PolicyRule[], includeDisabled?: boolean, optionalFields?: Set<string>): string;
229
261
  /**
230
262
  * Builder for constructing Cedar policies with type safety.
231
263
  */