@highflame/policy 2.0.7 → 2.0.9
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/overwatch/context.json +163 -1
- package/_schemas/overwatch/schema.cedarschema +45 -0
- package/dist/actions.gen.d.ts +0 -1
- package/dist/actions.gen.js +0 -1
- package/dist/annotations.d.ts +0 -1
- package/dist/annotations.js +0 -1
- package/dist/builder.d.ts +0 -1
- package/dist/builder.js +0 -1
- package/dist/context.gen.d.ts +0 -1
- package/dist/context.gen.js +0 -1
- package/dist/engine.d.ts +0 -1
- package/dist/engine.js +0 -1
- package/dist/entities.gen.d.ts +0 -1
- package/dist/entities.gen.js +0 -1
- package/dist/entity-metadata-types.gen.d.ts +0 -1
- package/dist/entity-metadata-types.gen.js +0 -1
- package/dist/errors.d.ts +0 -1
- package/dist/errors.js +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/dist/overwatch-context.gen.d.ts +13 -1
- package/dist/overwatch-context.gen.js +13 -1
- package/dist/overwatch-defaults.gen.d.ts +1 -2
- package/dist/overwatch-defaults.gen.js +346 -2
- package/dist/overwatch-entities.gen.d.ts +0 -1
- package/dist/overwatch-entities.gen.js +0 -1
- package/dist/palisade-context.gen.d.ts +0 -1
- package/dist/palisade-context.gen.js +0 -1
- package/dist/palisade-entities.gen.d.ts +0 -1
- package/dist/palisade-entities.gen.js +0 -1
- package/dist/parser.d.ts +0 -1
- package/dist/parser.js +0 -1
- package/dist/schema.gen.d.ts +0 -1
- package/dist/schema.gen.js +0 -1
- package/dist/schemas.d.ts +0 -1
- package/dist/schemas.js +0 -1
- package/dist/service-schemas.gen.d.ts +0 -1
- package/dist/service-schemas.gen.js +0 -1
- package/dist/types.d.ts +0 -1
- package/dist/types.js +0 -1
- package/package.json +1 -2
- package/dist/actions.gen.d.ts.map +0 -1
- package/dist/actions.gen.js.map +0 -1
- package/dist/annotations.d.ts.map +0 -1
- package/dist/annotations.js.map +0 -1
- package/dist/builder.d.ts.map +0 -1
- package/dist/builder.js.map +0 -1
- package/dist/context.gen.d.ts.map +0 -1
- package/dist/context.gen.js.map +0 -1
- package/dist/engine.d.ts.map +0 -1
- package/dist/engine.js.map +0 -1
- package/dist/engine.test.d.ts +0 -8
- package/dist/engine.test.d.ts.map +0 -1
- package/dist/engine.test.js +0 -190
- package/dist/engine.test.js.map +0 -1
- package/dist/entities.gen.d.ts.map +0 -1
- package/dist/entities.gen.js.map +0 -1
- package/dist/entity-metadata-types.gen.d.ts.map +0 -1
- package/dist/entity-metadata-types.gen.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/overwatch-context.gen.d.ts.map +0 -1
- package/dist/overwatch-context.gen.js.map +0 -1
- package/dist/overwatch-defaults.gen.d.ts.map +0 -1
- package/dist/overwatch-defaults.gen.js.map +0 -1
- package/dist/overwatch-defaults.test.d.ts +0 -8
- package/dist/overwatch-defaults.test.d.ts.map +0 -1
- package/dist/overwatch-defaults.test.js +0 -145
- package/dist/overwatch-defaults.test.js.map +0 -1
- package/dist/overwatch-entities.gen.d.ts.map +0 -1
- package/dist/overwatch-entities.gen.js.map +0 -1
- package/dist/overwatch-rebac.test.d.ts +0 -25
- package/dist/overwatch-rebac.test.d.ts.map +0 -1
- package/dist/overwatch-rebac.test.js +0 -301
- package/dist/overwatch-rebac.test.js.map +0 -1
- package/dist/palisade-context.gen.d.ts.map +0 -1
- package/dist/palisade-context.gen.js.map +0 -1
- package/dist/palisade-entities.gen.d.ts.map +0 -1
- package/dist/palisade-entities.gen.js.map +0 -1
- package/dist/parser.d.ts.map +0 -1
- package/dist/parser.js.map +0 -1
- package/dist/parser.test.d.ts +0 -8
- package/dist/parser.test.d.ts.map +0 -1
- package/dist/parser.test.js +0 -212
- package/dist/parser.test.js.map +0 -1
- package/dist/schema.gen.d.ts.map +0 -1
- package/dist/schema.gen.js.map +0 -1
- package/dist/schemas.d.ts.map +0 -1
- package/dist/schemas.js.map +0 -1
- package/dist/schemas.test.d.ts +0 -8
- package/dist/schemas.test.d.ts.map +0 -1
- package/dist/schemas.test.js +0 -375
- package/dist/schemas.test.js.map +0 -1
- package/dist/service-schemas.gen.d.ts.map +0 -1
- package/dist/service-schemas.gen.js.map +0 -1
- package/dist/studio-ui.test.d.ts +0 -8
- package/dist/studio-ui.test.d.ts.map +0 -1
- package/dist/studio-ui.test.js +0 -687
- package/dist/studio-ui.test.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/src/actions.gen.ts +0 -57
- package/src/annotations.ts +0 -243
- package/src/builder.ts +0 -799
- package/src/context.gen.ts +0 -10
- package/src/engine.test.ts +0 -370
- package/src/engine.ts +0 -497
- package/src/entities.gen.ts +0 -65
- package/src/entity-metadata-types.gen.ts +0 -19
- package/src/errors.ts +0 -195
- package/src/index.ts +0 -62
- package/src/overwatch-context.gen.ts +0 -32
- package/src/overwatch-defaults.gen.ts +0 -907
- package/src/overwatch-defaults.test.ts +0 -176
- package/src/overwatch-entities.gen.ts +0 -41
- package/src/overwatch-rebac.test.ts +0 -346
- package/src/palisade-context.gen.ts +0 -28
- package/src/palisade-entities.gen.ts +0 -49
- package/src/parser.test.ts +0 -251
- package/src/parser.ts +0 -579
- package/src/schema.gen.ts +0 -134
- package/src/schemas.test.ts +0 -445
- package/src/schemas.ts +0 -91
- package/src/service-schemas.gen.ts +0 -608
- package/src/studio-ui.test.ts +0 -813
- package/src/types.ts +0 -66
package/src/schemas.test.ts
DELETED
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Schema validation tests
|
|
3
|
-
*
|
|
4
|
-
* Tests service-specific schema loading and policy validation
|
|
5
|
-
* using Overwatch (Guardian) and Palisade schemas.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, expect, beforeEach } from 'vitest';
|
|
9
|
-
import {
|
|
10
|
-
PolicyEngine,
|
|
11
|
-
PolicyValidator,
|
|
12
|
-
EntityType,
|
|
13
|
-
ActionType,
|
|
14
|
-
newEntityUID,
|
|
15
|
-
newEntity,
|
|
16
|
-
} from './index.js';
|
|
17
|
-
import * as fs from 'fs';
|
|
18
|
-
import * as path from 'path';
|
|
19
|
-
|
|
20
|
-
// Load service-specific schemas
|
|
21
|
-
const SCHEMAS_DIR = path.join(__dirname, '..', '..', '..', 'schemas');
|
|
22
|
-
const OVERWATCH_SCHEMA = fs.readFileSync(
|
|
23
|
-
path.join(SCHEMAS_DIR, 'overwatch', 'schema.cedarschema'),
|
|
24
|
-
'utf-8'
|
|
25
|
-
);
|
|
26
|
-
const PALISADE_SCHEMA = fs.readFileSync(
|
|
27
|
-
path.join(SCHEMAS_DIR, 'palisade', 'schema.cedarschema'),
|
|
28
|
-
'utf-8'
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
describe('Service-Specific Schemas', () => {
|
|
32
|
-
describe('Schema Loading', () => {
|
|
33
|
-
it('should load Overwatch schema successfully', () => {
|
|
34
|
-
expect(OVERWATCH_SCHEMA).toBeTruthy();
|
|
35
|
-
expect(OVERWATCH_SCHEMA).toContain('namespace');
|
|
36
|
-
expect(OVERWATCH_SCHEMA).toContain('Overwatch');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('should load Palisade schema successfully', () => {
|
|
40
|
-
expect(PALISADE_SCHEMA).toBeTruthy();
|
|
41
|
-
expect(PALISADE_SCHEMA).toContain('namespace');
|
|
42
|
-
expect(PALISADE_SCHEMA).toContain('Palisade');
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
describe('Overwatch Schema Validation', () => {
|
|
47
|
-
let validator: PolicyValidator;
|
|
48
|
-
|
|
49
|
-
beforeEach(() => {
|
|
50
|
-
validator = new PolicyValidator(OVERWATCH_SCHEMA);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should validate a correct Overwatch policy', () => {
|
|
54
|
-
const validPolicy = `
|
|
55
|
-
permit(
|
|
56
|
-
principal is Overwatch::User,
|
|
57
|
-
action == Overwatch::Action::"call_tool",
|
|
58
|
-
resource is Overwatch::Tool
|
|
59
|
-
)
|
|
60
|
-
when {
|
|
61
|
-
context.threat_count < 5 &&
|
|
62
|
-
context.highest_severity != "critical"
|
|
63
|
-
};
|
|
64
|
-
`;
|
|
65
|
-
|
|
66
|
-
const result = validator.validate(validPolicy);
|
|
67
|
-
if (!result.valid) {
|
|
68
|
-
console.log('Validation errors:', result.errors);
|
|
69
|
-
}
|
|
70
|
-
expect(result.valid).toBe(true);
|
|
71
|
-
expect(result.errors).toHaveLength(0);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('should reject policy with invalid entity type', () => {
|
|
75
|
-
const invalidPolicy = `
|
|
76
|
-
permit(
|
|
77
|
-
principal is Overwatch::NonExistentEntity,
|
|
78
|
-
action == Overwatch::Action::"call_tool",
|
|
79
|
-
resource is Overwatch::Tool
|
|
80
|
-
);
|
|
81
|
-
`;
|
|
82
|
-
|
|
83
|
-
const result = validator.validate(invalidPolicy);
|
|
84
|
-
expect(result.valid).toBe(false);
|
|
85
|
-
expect(result.errors.length).toBeGreaterThan(0);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should reject policy with invalid action', () => {
|
|
89
|
-
const invalidPolicy = `
|
|
90
|
-
permit(
|
|
91
|
-
principal is Overwatch::User,
|
|
92
|
-
action == Overwatch::Action::"invalid_action",
|
|
93
|
-
resource is Overwatch::Tool
|
|
94
|
-
);
|
|
95
|
-
`;
|
|
96
|
-
|
|
97
|
-
const result = validator.validate(invalidPolicy);
|
|
98
|
-
expect(result.valid).toBe(false);
|
|
99
|
-
expect(result.errors.length).toBeGreaterThan(0);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should validate policy with multiple context attributes', () => {
|
|
103
|
-
const policy = `
|
|
104
|
-
permit(
|
|
105
|
-
principal is Overwatch::Agent,
|
|
106
|
-
action == Overwatch::Action::"process_prompt",
|
|
107
|
-
resource is Overwatch::LlmPrompt
|
|
108
|
-
)
|
|
109
|
-
when {
|
|
110
|
-
context.threat_count == 0 &&
|
|
111
|
-
context.highest_severity == "low" &&
|
|
112
|
-
context.contains_secrets == false
|
|
113
|
-
};
|
|
114
|
-
`;
|
|
115
|
-
|
|
116
|
-
const result = validator.validate(policy);
|
|
117
|
-
expect(result.valid).toBe(true);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('Palisade Schema Validation', () => {
|
|
122
|
-
let validator: PolicyValidator;
|
|
123
|
-
|
|
124
|
-
beforeEach(() => {
|
|
125
|
-
validator = new PolicyValidator(PALISADE_SCHEMA);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('should validate a correct Palisade policy', () => {
|
|
129
|
-
const validPolicy = `
|
|
130
|
-
permit(
|
|
131
|
-
principal is Palisade::Scanner,
|
|
132
|
-
action == Palisade::Action::"scan_artifact",
|
|
133
|
-
resource is Palisade::Artifact
|
|
134
|
-
)
|
|
135
|
-
when {
|
|
136
|
-
context.environment == "production" &&
|
|
137
|
-
context.artifact_format == "safetensors"
|
|
138
|
-
};
|
|
139
|
-
`;
|
|
140
|
-
|
|
141
|
-
const result = validator.validate(validPolicy);
|
|
142
|
-
expect(result.valid).toBe(true);
|
|
143
|
-
expect(result.errors).toHaveLength(0);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it('should reject policy with wrong namespace', () => {
|
|
147
|
-
const invalidPolicy = `
|
|
148
|
-
permit(
|
|
149
|
-
principal is Overwatch::Scanner,
|
|
150
|
-
action == Palisade::Action::"scan_artifact",
|
|
151
|
-
resource is Palisade::Artifact
|
|
152
|
-
);
|
|
153
|
-
`;
|
|
154
|
-
|
|
155
|
-
const result = validator.validate(invalidPolicy);
|
|
156
|
-
expect(result.valid).toBe(false);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('should validate policy with ML-specific context', () => {
|
|
160
|
-
const policy = `
|
|
161
|
-
forbid(
|
|
162
|
-
principal is Palisade::Scanner,
|
|
163
|
-
action == Palisade::Action::"scan_artifact",
|
|
164
|
-
resource is Palisade::Artifact
|
|
165
|
-
)
|
|
166
|
-
when {
|
|
167
|
-
context.pickle_exec_path_detected == true ||
|
|
168
|
-
context.severity == "CRITICAL"
|
|
169
|
-
};
|
|
170
|
-
`;
|
|
171
|
-
|
|
172
|
-
const result = validator.validate(policy);
|
|
173
|
-
expect(result.valid).toBe(true);
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
describe('PolicyEngine with Service Schemas', () => {
|
|
178
|
-
it('should evaluate Overwatch policy correctly', () => {
|
|
179
|
-
const policy = `
|
|
180
|
-
permit(
|
|
181
|
-
principal is Overwatch::User,
|
|
182
|
-
action == Overwatch::Action::"call_tool",
|
|
183
|
-
resource is Overwatch::Tool
|
|
184
|
-
)
|
|
185
|
-
when { context.threat_count < 5 };
|
|
186
|
-
`;
|
|
187
|
-
|
|
188
|
-
const engine = new PolicyEngine({ schema: OVERWATCH_SCHEMA });
|
|
189
|
-
engine.loadPolicy(policy);
|
|
190
|
-
|
|
191
|
-
const entities = [
|
|
192
|
-
newEntity('Overwatch::User', 'mcp_client', { user_type: 'external', email: 'user@example.com' }),
|
|
193
|
-
newEntity('Overwatch::Tool', 'shell', { tool_name: 'shell', risk_level: 'high' }),
|
|
194
|
-
];
|
|
195
|
-
|
|
196
|
-
const decision = engine.evaluate({
|
|
197
|
-
principal: newEntityUID('Overwatch::User', 'mcp_client'),
|
|
198
|
-
action: 'Overwatch::Action::"call_tool"',
|
|
199
|
-
resource: newEntityUID('Overwatch::Tool', 'shell'),
|
|
200
|
-
context: {
|
|
201
|
-
content: 'ls -la',
|
|
202
|
-
source: 'claudecode',
|
|
203
|
-
event: 'PreToolUse',
|
|
204
|
-
user_email: 'user@example.com',
|
|
205
|
-
tool_name: 'shell',
|
|
206
|
-
mcp_server: 'filesystem',
|
|
207
|
-
mcp_tool: 'shell',
|
|
208
|
-
path: '/workspace',
|
|
209
|
-
cwd: '/workspace',
|
|
210
|
-
workspace_root: '/workspace',
|
|
211
|
-
threat_count: 3,
|
|
212
|
-
highest_severity: 'low',
|
|
213
|
-
threat_categories: [],
|
|
214
|
-
|
|
215
|
-
yara_threats: [],
|
|
216
|
-
max_threat_severity: 1,
|
|
217
|
-
contains_secrets: false,
|
|
218
|
-
response_content: '',
|
|
219
|
-
},
|
|
220
|
-
entities,
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
if (decision.effect !== 'Allow') {
|
|
224
|
-
console.log('Decision:', decision);
|
|
225
|
-
console.log('Reason:', decision.reason);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
expect(decision.effect).toBe('Allow');
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
it('should deny Overwatch policy when context fails condition', () => {
|
|
232
|
-
const policy = `
|
|
233
|
-
permit(
|
|
234
|
-
principal is Overwatch::User,
|
|
235
|
-
action == Overwatch::Action::"call_tool",
|
|
236
|
-
resource is Overwatch::Tool
|
|
237
|
-
)
|
|
238
|
-
when { context.threat_count < 5 };
|
|
239
|
-
`;
|
|
240
|
-
|
|
241
|
-
const engine = new PolicyEngine({ schema: OVERWATCH_SCHEMA });
|
|
242
|
-
engine.loadPolicy(policy);
|
|
243
|
-
|
|
244
|
-
const entities = [
|
|
245
|
-
newEntity('Overwatch::User', 'mcp_client', { user_type: 'external', email: 'user@example.com' }),
|
|
246
|
-
newEntity('Overwatch::Tool', 'shell', { tool_name: 'shell', risk_level: 'high' }),
|
|
247
|
-
];
|
|
248
|
-
|
|
249
|
-
const decision = engine.evaluate({
|
|
250
|
-
principal: newEntityUID('Overwatch::User', 'mcp_client'),
|
|
251
|
-
action: 'Overwatch::Action::"call_tool"',
|
|
252
|
-
resource: newEntityUID('Overwatch::Tool', 'shell'),
|
|
253
|
-
context: { threat_count: 10 }, // Too many threats
|
|
254
|
-
entities,
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
expect(decision.effect).toBe('Deny');
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
it('should evaluate Palisade policy correctly', () => {
|
|
261
|
-
const policy = `
|
|
262
|
-
forbid(
|
|
263
|
-
principal is Palisade::Scanner,
|
|
264
|
-
action == Palisade::Action::"load_model",
|
|
265
|
-
resource is Palisade::Artifact
|
|
266
|
-
)
|
|
267
|
-
when {
|
|
268
|
-
context.pickle_exec_path_detected == true
|
|
269
|
-
};
|
|
270
|
-
`;
|
|
271
|
-
|
|
272
|
-
const engine = new PolicyEngine({ schema: PALISADE_SCHEMA });
|
|
273
|
-
engine.loadPolicy(policy);
|
|
274
|
-
|
|
275
|
-
const entities = [
|
|
276
|
-
newEntity('Palisade::Scanner', 'palisade', { scanner_type: 'ml_security' }),
|
|
277
|
-
newEntity('Palisade::Artifact', 'model.pkl', { artifact_format: 'pickle', path: '/models/model.pkl', signed: false, signer: 'unsigned' }),
|
|
278
|
-
];
|
|
279
|
-
|
|
280
|
-
const decision = engine.evaluate({
|
|
281
|
-
principal: newEntityUID('Palisade::Scanner', 'palisade'),
|
|
282
|
-
action: 'Palisade::Action::"load_model"',
|
|
283
|
-
resource: newEntityUID('Palisade::Artifact', 'model.pkl'),
|
|
284
|
-
context: {
|
|
285
|
-
environment: 'production',
|
|
286
|
-
pickle_exec_path_detected: true,
|
|
287
|
-
severity: 'CRITICAL',
|
|
288
|
-
},
|
|
289
|
-
entities,
|
|
290
|
-
});
|
|
291
|
-
|
|
292
|
-
expect(decision.effect).toBe('Deny');
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
it('should allow Palisade policy when condition is false', () => {
|
|
296
|
-
const policy = `
|
|
297
|
-
permit(
|
|
298
|
-
principal is Palisade::Scanner,
|
|
299
|
-
action == Palisade::Action::"scan_artifact",
|
|
300
|
-
resource is Palisade::Artifact
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
forbid(
|
|
304
|
-
principal is Palisade::Scanner,
|
|
305
|
-
action == Palisade::Action::"scan_artifact",
|
|
306
|
-
resource is Palisade::Artifact
|
|
307
|
-
)
|
|
308
|
-
when {
|
|
309
|
-
context.pickle_exec_path_detected == true
|
|
310
|
-
};
|
|
311
|
-
`;
|
|
312
|
-
|
|
313
|
-
const engine = new PolicyEngine({ schema: PALISADE_SCHEMA });
|
|
314
|
-
engine.loadPolicy(policy);
|
|
315
|
-
|
|
316
|
-
const entities = [
|
|
317
|
-
newEntity('Palisade::Scanner', 'palisade', { scanner_type: 'ml_security' }),
|
|
318
|
-
newEntity('Palisade::Artifact', 'model.safetensors', { artifact_format: 'safetensors', path: '/models/model.safetensors', signed: true, signer: 'trusted-org' }),
|
|
319
|
-
];
|
|
320
|
-
|
|
321
|
-
const decision = engine.evaluate({
|
|
322
|
-
principal: newEntityUID('Palisade::Scanner', 'palisade'),
|
|
323
|
-
action: 'Palisade::Action::"scan_artifact"',
|
|
324
|
-
resource: newEntityUID('Palisade::Artifact', 'model.safetensors'),
|
|
325
|
-
context: {
|
|
326
|
-
environment: 'production',
|
|
327
|
-
pickle_exec_path_detected: false,
|
|
328
|
-
severity: 'INFO',
|
|
329
|
-
finding_type: 'backdoor_check',
|
|
330
|
-
artifact_format: 'safetensors',
|
|
331
|
-
path: '/models/model.safetensors',
|
|
332
|
-
artifact_signed: true,
|
|
333
|
-
provenance_signer: 'trusted',
|
|
334
|
-
tokenizer_added_tokens_count: 0,
|
|
335
|
-
adapter_base_digest_mismatch: false,
|
|
336
|
-
gguf_suspicious_metadata: false,
|
|
337
|
-
safetensors_integrity_violation: false,
|
|
338
|
-
metadata_malicious_pattern: false,
|
|
339
|
-
metadata_cosai_level_numeric: 3,
|
|
340
|
-
match_count: 0,
|
|
341
|
-
},
|
|
342
|
-
entities,
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
if (decision.effect !== 'Allow') {
|
|
346
|
-
console.log('Decision:', decision);
|
|
347
|
-
console.log('Reason:', decision.reason);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
expect(decision.effect).toBe('Allow');
|
|
351
|
-
});
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
describe('Example from REFACTORING_SUMMARY.md', () => {
|
|
355
|
-
it('should work with Guardian plugin example', () => {
|
|
356
|
-
const engine = new PolicyEngine({ schema: OVERWATCH_SCHEMA });
|
|
357
|
-
|
|
358
|
-
const policy = `
|
|
359
|
-
permit(
|
|
360
|
-
principal is Overwatch::User,
|
|
361
|
-
action == Overwatch::Action::"call_tool",
|
|
362
|
-
resource is Overwatch::Tool
|
|
363
|
-
)
|
|
364
|
-
when {
|
|
365
|
-
context.threat_count < 10 &&
|
|
366
|
-
context.highest_severity != "critical"
|
|
367
|
-
};
|
|
368
|
-
`;
|
|
369
|
-
|
|
370
|
-
engine.loadPolicy(policy);
|
|
371
|
-
|
|
372
|
-
const entities = [
|
|
373
|
-
newEntity('Overwatch::User', 'mcp_client', { user_type: 'external', email: 'user@example.com' }),
|
|
374
|
-
newEntity('Overwatch::Tool', 'shell', { tool_name: 'shell', risk_level: 'high' }),
|
|
375
|
-
];
|
|
376
|
-
|
|
377
|
-
const decision = engine.evaluate({
|
|
378
|
-
principal: newEntityUID('Overwatch::User', 'mcp_client'),
|
|
379
|
-
action: 'Overwatch::Action::"call_tool"',
|
|
380
|
-
resource: newEntityUID('Overwatch::Tool', 'shell'),
|
|
381
|
-
context: {
|
|
382
|
-
content: 'cat /etc/passwd',
|
|
383
|
-
source: 'claudecode',
|
|
384
|
-
event: 'PreToolUse',
|
|
385
|
-
user_email: 'user@example.com',
|
|
386
|
-
tool_name: 'shell',
|
|
387
|
-
mcp_server: 'filesystem',
|
|
388
|
-
mcp_tool: 'shell',
|
|
389
|
-
path: '/etc/passwd',
|
|
390
|
-
cwd: '/workspace',
|
|
391
|
-
workspace_root: '/workspace',
|
|
392
|
-
threat_count: 5,
|
|
393
|
-
highest_severity: 'medium',
|
|
394
|
-
threat_categories: [],
|
|
395
|
-
|
|
396
|
-
yara_threats: [],
|
|
397
|
-
max_threat_severity: 2,
|
|
398
|
-
contains_secrets: false,
|
|
399
|
-
response_content: '',
|
|
400
|
-
},
|
|
401
|
-
entities,
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
expect(decision.effect).toBe('Allow');
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
it('should work with Palisade service example', () => {
|
|
408
|
-
const engine = new PolicyEngine({ schema: PALISADE_SCHEMA });
|
|
409
|
-
|
|
410
|
-
const policy = `
|
|
411
|
-
forbid(
|
|
412
|
-
principal is Palisade::Scanner,
|
|
413
|
-
action == Palisade::Action::"load_model",
|
|
414
|
-
resource is Palisade::Artifact
|
|
415
|
-
)
|
|
416
|
-
when {
|
|
417
|
-
context.environment == "production" &&
|
|
418
|
-
context.pickle_exec_path_detected == true &&
|
|
419
|
-
context.severity == "CRITICAL"
|
|
420
|
-
};
|
|
421
|
-
`;
|
|
422
|
-
|
|
423
|
-
engine.loadPolicy(policy);
|
|
424
|
-
|
|
425
|
-
const entities = [
|
|
426
|
-
newEntity('Palisade::Scanner', 'palisade', { scanner_type: 'ml_security' }),
|
|
427
|
-
newEntity('Palisade::Artifact', 'model.pkl', { artifact_format: 'pickle', path: '/models/model.pkl', signed: false, signer: 'unsigned' }),
|
|
428
|
-
];
|
|
429
|
-
|
|
430
|
-
const decision = engine.evaluate({
|
|
431
|
-
principal: newEntityUID('Palisade::Scanner', 'palisade'),
|
|
432
|
-
action: 'Palisade::Action::"load_model"',
|
|
433
|
-
resource: newEntityUID('Palisade::Artifact', 'model.pkl'),
|
|
434
|
-
context: {
|
|
435
|
-
environment: 'production',
|
|
436
|
-
pickle_exec_path_detected: true,
|
|
437
|
-
severity: 'CRITICAL',
|
|
438
|
-
},
|
|
439
|
-
entities,
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
expect(decision.effect).toBe('Deny');
|
|
443
|
-
});
|
|
444
|
-
});
|
|
445
|
-
});
|
package/src/schemas.ts
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Service-specific Cedar schemas and context metadata
|
|
3
|
-
*
|
|
4
|
-
* This module provides access to service-specific Cedar schemas
|
|
5
|
-
* and their associated context metadata for UI builders.
|
|
6
|
-
*
|
|
7
|
-
* Schemas are bundled with the package and loaded at runtime. The schema
|
|
8
|
-
* files are copied from schemas/ to packages/typescript/_schemas/ during
|
|
9
|
-
* codegen (see Makefile). This ensures the package is self-contained and
|
|
10
|
-
* works correctly when installed from npm.
|
|
11
|
-
*
|
|
12
|
-
* Available Schemas:
|
|
13
|
-
* - Overwatch (Guardian): IDE security policies for LLM agents and tool calls
|
|
14
|
-
* - Palisade: ML supply chain security policies for artifact scanning
|
|
15
|
-
*
|
|
16
|
-
* @example
|
|
17
|
-
* ```typescript
|
|
18
|
-
* import { OVERWATCH_SCHEMA, OVERWATCH_CONTEXT } from '@highflame/policy/schemas';
|
|
19
|
-
* import { PolicyEngine } from '@highflame/policy';
|
|
20
|
-
*
|
|
21
|
-
* const engine = new PolicyEngine();
|
|
22
|
-
* engine.loadSchema(OVERWATCH_SCHEMA);
|
|
23
|
-
*
|
|
24
|
-
* // Parse context metadata for UI dropdowns
|
|
25
|
-
* const contextMeta = JSON.parse(OVERWATCH_CONTEXT);
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import * as fs from 'fs';
|
|
30
|
-
import * as path from 'path';
|
|
31
|
-
import { fileURLToPath } from 'url';
|
|
32
|
-
|
|
33
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
34
|
-
const __dirname = path.dirname(__filename);
|
|
35
|
-
|
|
36
|
-
// Path to schemas directory (bundled with package)
|
|
37
|
-
const SCHEMAS_DIR = path.join(__dirname, '..', '_schemas');
|
|
38
|
-
|
|
39
|
-
// Re-export service-specific context keys
|
|
40
|
-
export { OverwatchContextKey } from './overwatch-context.gen.js';
|
|
41
|
-
export { PalisadeContextKey } from './palisade-context.gen.js';
|
|
42
|
-
export type { OverwatchContextKey as OverwatchContextKeyType } from './overwatch-context.gen.js';
|
|
43
|
-
export type { PalisadeContextKey as PalisadeContextKeyType } from './palisade-context.gen.js';
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Overwatch (Guardian) Cedar schema
|
|
47
|
-
*
|
|
48
|
-
* Full Cedar schema for IDE security, including:
|
|
49
|
-
* - Actions: process_prompt, call_tool, connect_server, read_file, write_file
|
|
50
|
-
* - Entities: User, Agent, LlmPrompt, Tool, Server, FilePath
|
|
51
|
-
* - 20+ context attributes for threat detection and workspace security
|
|
52
|
-
*/
|
|
53
|
-
export const OVERWATCH_SCHEMA = fs.readFileSync(
|
|
54
|
-
path.join(SCHEMAS_DIR, 'overwatch', 'schema.cedarschema'),
|
|
55
|
-
'utf-8'
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Overwatch context metadata (JSON)
|
|
60
|
-
*
|
|
61
|
-
* Metadata describing available context attributes for each Overwatch action.
|
|
62
|
-
* Used by PolicyBuilder UI to generate context dropdowns with type information.
|
|
63
|
-
*/
|
|
64
|
-
export const OVERWATCH_CONTEXT = fs.readFileSync(
|
|
65
|
-
path.join(SCHEMAS_DIR, 'overwatch', 'context.json'),
|
|
66
|
-
'utf-8'
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Palisade Cedar schema
|
|
71
|
-
*
|
|
72
|
-
* Full Cedar schema for ML supply chain security, including:
|
|
73
|
-
* - Actions: scan_artifact, validate_integrity, validate_provenance, quarantine_artifact, load_model, deploy_model
|
|
74
|
-
* - Entities: Scanner, Artifact, Package
|
|
75
|
-
* - 15+ context attributes for ML security findings
|
|
76
|
-
*/
|
|
77
|
-
export const PALISADE_SCHEMA = fs.readFileSync(
|
|
78
|
-
path.join(SCHEMAS_DIR, 'palisade', 'schema.cedarschema'),
|
|
79
|
-
'utf-8'
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Palisade context metadata (JSON)
|
|
84
|
-
*
|
|
85
|
-
* Metadata describing available context attributes for each Palisade action.
|
|
86
|
-
* Used by PolicyBuilder UI to generate context dropdowns with type information.
|
|
87
|
-
*/
|
|
88
|
-
export const PALISADE_CONTEXT = fs.readFileSync(
|
|
89
|
-
path.join(SCHEMAS_DIR, 'palisade', 'context.json'),
|
|
90
|
-
'utf-8'
|
|
91
|
-
);
|