vellum 0.2.13 → 0.2.14
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/README.md +32 -0
- package/bun.lock +2 -2
- package/docs/skills.md +4 -4
- package/package.json +2 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +213 -3
- package/src/__tests__/app-git-history.test.ts +176 -0
- package/src/__tests__/app-git-service.test.ts +169 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +315 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +8 -8
- package/src/__tests__/browser-skill-endstate.test.ts +6 -6
- package/src/__tests__/call-bridge.test.ts +105 -13
- package/src/__tests__/call-domain.test.ts +163 -0
- package/src/__tests__/call-orchestrator.test.ts +113 -0
- package/src/__tests__/call-routes-http.test.ts +246 -6
- package/src/__tests__/channel-approval-routes.test.ts +438 -0
- package/src/__tests__/channel-approval.test.ts +266 -0
- package/src/__tests__/channel-approvals.test.ts +393 -0
- package/src/__tests__/channel-delivery-store.test.ts +447 -0
- package/src/__tests__/checker.test.ts +607 -1048
- package/src/__tests__/cli.test.ts +1 -56
- package/src/__tests__/config-schema.test.ts +137 -18
- package/src/__tests__/conflict-intent-tokenization.test.ts +141 -0
- package/src/__tests__/conflict-policy.test.ts +121 -0
- package/src/__tests__/conflict-store.test.ts +2 -0
- package/src/__tests__/contacts-tools.test.ts +3 -3
- package/src/__tests__/contradiction-checker.test.ts +99 -1
- package/src/__tests__/credential-security-invariants.test.ts +22 -6
- package/src/__tests__/credential-vault-unit.test.ts +780 -0
- package/src/__tests__/elevenlabs-client.test.ts +62 -0
- package/src/__tests__/ephemeral-permissions.test.ts +73 -23
- package/src/__tests__/filesystem-tools.test.ts +579 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +114 -4
- package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +202 -0
- package/src/__tests__/handlers-cu-observation-blob.test.ts +2 -1
- package/src/__tests__/handlers-ipc-blob-probe.test.ts +2 -1
- package/src/__tests__/handlers-slack-config.test.ts +2 -1
- package/src/__tests__/handlers-telegram-config.test.ts +855 -0
- package/src/__tests__/handlers-twitter-config.test.ts +141 -1
- package/src/__tests__/hooks-runner.test.ts +6 -2
- package/src/__tests__/host-file-edit-tool.test.ts +124 -0
- package/src/__tests__/host-file-read-tool.test.ts +62 -0
- package/src/__tests__/host-file-write-tool.test.ts +59 -0
- package/src/__tests__/host-shell-tool.test.ts +251 -0
- package/src/__tests__/ingress-reconcile.test.ts +581 -0
- package/src/__tests__/ipc-snapshot.test.ts +100 -41
- package/src/__tests__/ipc-validate.test.ts +50 -0
- package/src/__tests__/key-migration.test.ts +23 -0
- package/src/__tests__/memory-regressions.test.ts +99 -0
- package/src/__tests__/memory-retrieval.benchmark.test.ts +1 -1
- package/src/__tests__/oauth-callback-registry.test.ts +11 -4
- package/src/__tests__/playbook-execution.test.ts +502 -0
- package/src/__tests__/playbook-tools.test.ts +4 -6
- package/src/__tests__/public-ingress-urls.test.ts +34 -0
- package/src/__tests__/qdrant-manager.test.ts +267 -0
- package/src/__tests__/recurrence-engine-rruleset.test.ts +97 -0
- package/src/__tests__/recurrence-engine.test.ts +9 -0
- package/src/__tests__/recurrence-types.test.ts +8 -0
- package/src/__tests__/registry.test.ts +1 -1
- package/src/__tests__/runtime-runs.test.ts +1 -25
- package/src/__tests__/schedule-store.test.ts +16 -14
- package/src/__tests__/schedule-tools.test.ts +83 -0
- package/src/__tests__/scheduler-recurrence.test.ts +111 -10
- package/src/__tests__/secret-allowlist.test.ts +18 -17
- package/src/__tests__/secret-ingress-handler.test.ts +11 -0
- package/src/__tests__/secret-scanner.test.ts +43 -0
- package/src/__tests__/session-conflict-gate.test.ts +442 -6
- package/src/__tests__/session-init.benchmark.test.ts +3 -0
- package/src/__tests__/session-process-bridge.test.ts +242 -0
- package/src/__tests__/session-skill-tools.test.ts +1 -1
- package/src/__tests__/shell-identity.test.ts +256 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +11 -1
- package/src/__tests__/subagent-tools.test.ts +637 -54
- package/src/__tests__/task-management-tools.test.ts +936 -0
- package/src/__tests__/task-runner.test.ts +2 -2
- package/src/__tests__/terminal-tools.test.ts +840 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +301 -0
- package/src/__tests__/tool-executor.test.ts +85 -151
- package/src/__tests__/tool-permission-simulate-handler.test.ts +336 -0
- package/src/__tests__/trust-store.test.ts +27 -453
- package/src/__tests__/twilio-provider.test.ts +153 -3
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +375 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +4 -4
- package/src/__tests__/twilio-routes.test.ts +17 -262
- package/src/__tests__/twitter-auth-handler.test.ts +2 -1
- package/src/__tests__/twitter-cli-error-shaping.test.ts +208 -0
- package/src/__tests__/twitter-cli-routing.test.ts +252 -0
- package/src/__tests__/twitter-oauth-client.test.ts +209 -0
- package/src/__tests__/workspace-policy.test.ts +213 -0
- package/src/calls/call-bridge.ts +92 -19
- package/src/calls/call-domain.ts +157 -5
- package/src/calls/call-orchestrator.ts +93 -7
- package/src/calls/call-store.ts +6 -0
- package/src/calls/elevenlabs-client.ts +8 -0
- package/src/calls/elevenlabs-config.ts +7 -5
- package/src/calls/twilio-provider.ts +91 -0
- package/src/calls/twilio-routes.ts +32 -37
- package/src/calls/types.ts +3 -1
- package/src/calls/voice-quality.ts +29 -7
- package/src/cli/twitter.ts +200 -21
- package/src/cli.ts +1 -20
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +52 -4
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +55 -4
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +61 -4
- package/src/config/bundled-skills/messaging/SKILL.md +17 -2
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +4 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
- package/src/config/bundled-skills/messaging/tools/shared.ts +5 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +142 -34
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +95 -6
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +51 -6
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +73 -6
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +110 -6
- package/src/config/bundled-skills/public-ingress/SKILL.md +22 -5
- package/src/config/bundled-skills/twitter/SKILL.md +103 -17
- package/src/config/defaults.ts +10 -4
- package/src/config/schema.ts +80 -21
- package/src/config/types.ts +1 -0
- package/src/config/vellum-skills/telegram-setup/SKILL.md +56 -61
- package/src/daemon/assistant-attachments.ts +4 -2
- package/src/daemon/handlers/apps.ts +69 -0
- package/src/daemon/handlers/config.ts +543 -24
- package/src/daemon/handlers/index.ts +1 -0
- package/src/daemon/handlers/sessions.ts +22 -6
- package/src/daemon/handlers/shared.ts +2 -1
- package/src/daemon/handlers/skills.ts +5 -20
- package/src/daemon/ipc-contract-inventory.json +28 -0
- package/src/daemon/ipc-contract.ts +168 -10
- package/src/daemon/ipc-validate.ts +17 -0
- package/src/daemon/lifecycle.ts +2 -0
- package/src/daemon/server.ts +78 -72
- package/src/daemon/session-attachments.ts +1 -1
- package/src/daemon/session-conflict-gate.ts +62 -6
- package/src/daemon/session-notifiers.ts +1 -1
- package/src/daemon/session-process.ts +62 -3
- package/src/daemon/session-tool-setup.ts +1 -2
- package/src/daemon/tls-certs.ts +189 -0
- package/src/daemon/video-thumbnail.ts +5 -3
- package/src/hooks/manager.ts +5 -9
- package/src/memory/app-git-service.ts +295 -0
- package/src/memory/app-store.ts +21 -0
- package/src/memory/conflict-intent.ts +47 -4
- package/src/memory/conflict-policy.ts +73 -0
- package/src/memory/conflict-store.ts +9 -1
- package/src/memory/contradiction-checker.ts +28 -0
- package/src/memory/conversation-key-store.ts +15 -0
- package/src/memory/db.ts +81 -0
- package/src/memory/embedding-local.ts +3 -13
- package/src/memory/external-conversation-store.ts +234 -0
- package/src/memory/job-handlers/conflict.ts +22 -2
- package/src/memory/jobs-worker.ts +67 -28
- package/src/memory/runs-store.ts +54 -7
- package/src/memory/schema.ts +20 -0
- package/src/messaging/provider.ts +9 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +162 -0
- package/src/messaging/providers/telegram-bot/client.ts +104 -0
- package/src/messaging/providers/telegram-bot/types.ts +15 -0
- package/src/messaging/registry.ts +1 -0
- package/src/permissions/checker.ts +48 -44
- package/src/permissions/prompter.ts +0 -4
- package/src/permissions/shell-identity.ts +227 -0
- package/src/permissions/trust-store.ts +76 -53
- package/src/permissions/types.ts +0 -19
- package/src/permissions/workspace-policy.ts +114 -0
- package/src/providers/retry.ts +12 -37
- package/src/runtime/assistant-event-hub.ts +41 -4
- package/src/runtime/channel-approval-parser.ts +60 -0
- package/src/runtime/channel-approval-types.ts +71 -0
- package/src/runtime/channel-approvals.ts +145 -0
- package/src/runtime/gateway-client.ts +16 -0
- package/src/runtime/http-server.ts +29 -9
- package/src/runtime/routes/call-routes.ts +52 -2
- package/src/runtime/routes/channel-routes.ts +296 -16
- package/src/runtime/routes/events-routes.ts +97 -28
- package/src/runtime/routes/run-routes.ts +2 -7
- package/src/runtime/run-orchestrator.ts +0 -3
- package/src/schedule/recurrence-engine.ts +26 -2
- package/src/schedule/recurrence-types.ts +1 -1
- package/src/schedule/schedule-store.ts +12 -3
- package/src/security/secret-scanner.ts +7 -0
- package/src/tasks/ephemeral-permissions.ts +0 -2
- package/src/tasks/task-scheduler.ts +2 -1
- package/src/tools/calls/call-start.ts +8 -0
- package/src/tools/execution-target.ts +21 -0
- package/src/tools/execution-timeout.ts +49 -0
- package/src/tools/executor.ts +6 -135
- package/src/tools/network/web-search.ts +9 -32
- package/src/tools/policy-context.ts +29 -0
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/terminal/parser.ts +16 -18
- package/src/tools/types.ts +4 -11
- package/src/twitter/oauth-client.ts +102 -0
- package/src/twitter/router.ts +101 -0
- package/src/util/debounce.ts +88 -0
- package/src/util/network-info.ts +47 -0
- package/src/util/platform.ts +29 -4
- package/src/util/promise-guard.ts +37 -0
- package/src/util/retry.ts +98 -0
- package/src/util/truncate.ts +1 -1
- package/src/workspace/git-service.ts +129 -112
- package/src/tools/contacts/contact-merge.ts +0 -55
- package/src/tools/contacts/contact-search.ts +0 -58
- package/src/tools/contacts/contact-upsert.ts +0 -64
- package/src/tools/playbooks/index.ts +0 -4
- package/src/tools/playbooks/playbook-create.ts +0 -96
- package/src/tools/playbooks/playbook-delete.ts +0 -52
- package/src/tools/playbooks/playbook-list.ts +0 -74
- package/src/tools/playbooks/playbook-update.ts +0 -111
|
@@ -148,16 +148,10 @@ describe('Trust Store', () => {
|
|
|
148
148
|
expect(userRules[1].decision).toBe('allow');
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
-
test('accepts
|
|
151
|
+
test('accepts executionTarget option and persists it', () => {
|
|
152
152
|
const rule = addRule('skill_tool', 'skill_tool:*', '/tmp', 'allow', 100, {
|
|
153
|
-
principalKind: 'skill',
|
|
154
|
-
principalId: 'my-skill-42',
|
|
155
|
-
principalVersion: 'sha256-abc123',
|
|
156
153
|
executionTarget: 'sandbox',
|
|
157
154
|
});
|
|
158
|
-
expect(rule.principalKind).toBe('skill');
|
|
159
|
-
expect(rule.principalId).toBe('my-skill-42');
|
|
160
|
-
expect(rule.principalVersion).toBe('sha256-abc123');
|
|
161
155
|
expect(rule.executionTarget).toBe('sandbox');
|
|
162
156
|
|
|
163
157
|
// Verify persistence to disk
|
|
@@ -165,24 +159,15 @@ describe('Trust Store', () => {
|
|
|
165
159
|
const rules = getAllRules();
|
|
166
160
|
const found = rules.find((r) => r.id === rule.id);
|
|
167
161
|
expect(found).toBeDefined();
|
|
168
|
-
expect(found!.principalKind).toBe('skill');
|
|
169
|
-
expect(found!.principalId).toBe('my-skill-42');
|
|
170
|
-
expect(found!.principalVersion).toBe('sha256-abc123');
|
|
171
162
|
expect(found!.executionTarget).toBe('sandbox');
|
|
172
163
|
});
|
|
173
164
|
|
|
174
|
-
test('accepts all contextual options together (
|
|
165
|
+
test('accepts all contextual options together (target, allowHighRisk)', () => {
|
|
175
166
|
const rule = addRule('risky_tool', 'risky_tool:*', 'everywhere', 'allow', 100, {
|
|
176
167
|
allowHighRisk: true,
|
|
177
|
-
principalKind: 'skill',
|
|
178
|
-
principalId: 'dangerous-skill',
|
|
179
|
-
principalVersion: 'sha256-deadbeef',
|
|
180
168
|
executionTarget: 'host',
|
|
181
169
|
});
|
|
182
170
|
expect(rule.allowHighRisk).toBe(true);
|
|
183
|
-
expect(rule.principalKind).toBe('skill');
|
|
184
|
-
expect(rule.principalId).toBe('dangerous-skill');
|
|
185
|
-
expect(rule.principalVersion).toBe('sha256-deadbeef');
|
|
186
171
|
expect(rule.executionTarget).toBe('host');
|
|
187
172
|
|
|
188
173
|
// Verify on disk
|
|
@@ -190,26 +175,17 @@ describe('Trust Store', () => {
|
|
|
190
175
|
const diskRule = raw.rules.find((r: { id: string }) => r.id === rule.id);
|
|
191
176
|
expect(diskRule).toBeDefined();
|
|
192
177
|
expect(diskRule.allowHighRisk).toBe(true);
|
|
193
|
-
expect(diskRule.principalKind).toBe('skill');
|
|
194
|
-
expect(diskRule.principalId).toBe('dangerous-skill');
|
|
195
|
-
expect(diskRule.principalVersion).toBe('sha256-deadbeef');
|
|
196
178
|
expect(diskRule.executionTarget).toBe('host');
|
|
197
179
|
});
|
|
198
180
|
|
|
199
|
-
test('addRule without
|
|
181
|
+
test('addRule without options does not set optional fields', () => {
|
|
200
182
|
const rule = addRule('bash', 'echo *', '/tmp');
|
|
201
|
-
expect(rule.principalKind).toBeUndefined();
|
|
202
|
-
expect(rule.principalId).toBeUndefined();
|
|
203
|
-
expect(rule.principalVersion).toBeUndefined();
|
|
204
183
|
expect(rule.executionTarget).toBeUndefined();
|
|
205
184
|
|
|
206
185
|
// Verify on disk
|
|
207
186
|
const raw = JSON.parse(readFileSync(trustPath, 'utf-8'));
|
|
208
187
|
const diskRule = raw.rules.find((r: { id: string }) => r.id === rule.id);
|
|
209
188
|
expect(diskRule).toBeDefined();
|
|
210
|
-
expect(diskRule).not.toHaveProperty('principalKind');
|
|
211
|
-
expect(diskRule).not.toHaveProperty('principalId');
|
|
212
|
-
expect(diskRule).not.toHaveProperty('principalVersion');
|
|
213
189
|
expect(diskRule).not.toHaveProperty('executionTarget');
|
|
214
190
|
});
|
|
215
191
|
});
|
|
@@ -1026,12 +1002,9 @@ describe('Trust Store', () => {
|
|
|
1026
1002
|
// ── trust rule schema v3 (PR 14) ──────────────────────────────
|
|
1027
1003
|
|
|
1028
1004
|
describe('trust rule schema v3 (PR 14)', () => {
|
|
1029
|
-
test('new rules can include
|
|
1005
|
+
test('new rules can include v3 optional fields', () => {
|
|
1030
1006
|
const rule = addRule('bash', 'git *', '/tmp');
|
|
1031
|
-
// Manually set v3
|
|
1032
|
-
rule.principalKind = 'skill';
|
|
1033
|
-
rule.principalId = 'my-skill';
|
|
1034
|
-
rule.principalVersion = 'abc123';
|
|
1007
|
+
// Manually set v3 optional fields on the rule and persist
|
|
1035
1008
|
rule.executionTarget = '/usr/local/bin/node';
|
|
1036
1009
|
rule.allowHighRisk = true;
|
|
1037
1010
|
// Re-persist the updated rules
|
|
@@ -1045,9 +1018,6 @@ describe('Trust Store', () => {
|
|
|
1045
1018
|
const reloaded = getAllRules();
|
|
1046
1019
|
const found = reloaded.find((r) => r.id === rule.id);
|
|
1047
1020
|
expect(found).toBeDefined();
|
|
1048
|
-
expect(found!.principalKind).toBe('skill');
|
|
1049
|
-
expect(found!.principalId).toBe('my-skill');
|
|
1050
|
-
expect(found!.principalVersion).toBe('abc123');
|
|
1051
1021
|
expect(found!.executionTarget).toBe('/usr/local/bin/node');
|
|
1052
1022
|
expect(found!.allowHighRisk).toBe(true);
|
|
1053
1023
|
});
|
|
@@ -1072,7 +1042,7 @@ describe('Trust Store', () => {
|
|
|
1072
1042
|
expect(data.version).toBe(3);
|
|
1073
1043
|
});
|
|
1074
1044
|
|
|
1075
|
-
test('v2 rules survive v3 migration with no
|
|
1045
|
+
test('v2 rules survive v3 migration with no v3-only fields', () => {
|
|
1076
1046
|
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1077
1047
|
writeFileSync(trustPath, JSON.stringify({
|
|
1078
1048
|
version: 2,
|
|
@@ -1105,10 +1075,7 @@ describe('Trust Store', () => {
|
|
|
1105
1075
|
expect(ruleB).toBeDefined();
|
|
1106
1076
|
expect(ruleA!.pattern).toBe('git *');
|
|
1107
1077
|
expect(ruleB!.decision).toBe('deny');
|
|
1108
|
-
// No
|
|
1109
|
-
expect(ruleA).not.toHaveProperty('principalKind');
|
|
1110
|
-
expect(ruleA).not.toHaveProperty('principalId');
|
|
1111
|
-
expect(ruleA).not.toHaveProperty('principalVersion');
|
|
1078
|
+
// No v3-only fields should be present
|
|
1112
1079
|
expect(ruleA).not.toHaveProperty('executionTarget');
|
|
1113
1080
|
expect(ruleA).not.toHaveProperty('allowHighRisk');
|
|
1114
1081
|
});
|
|
@@ -1254,25 +1221,22 @@ describe('Trust Store', () => {
|
|
|
1254
1221
|
expect(userRules).toHaveLength(1);
|
|
1255
1222
|
});
|
|
1256
1223
|
|
|
1257
|
-
test('v3 file with
|
|
1224
|
+
test('v3 file with optional fields is loaded correctly without re-migration', () => {
|
|
1258
1225
|
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1259
1226
|
const v3Rules = [
|
|
1260
1227
|
{
|
|
1261
|
-
id: 'v3-with-
|
|
1228
|
+
id: 'v3-with-options',
|
|
1262
1229
|
tool: 'bash',
|
|
1263
1230
|
pattern: 'skill-cmd *',
|
|
1264
1231
|
scope: '/tmp',
|
|
1265
1232
|
decision: 'allow',
|
|
1266
1233
|
priority: 100,
|
|
1267
1234
|
createdAt: 7000,
|
|
1268
|
-
principalKind: 'skill',
|
|
1269
|
-
principalId: 'my-skill',
|
|
1270
|
-
principalVersion: 'sha256-abc',
|
|
1271
1235
|
executionTarget: '/usr/bin/node',
|
|
1272
1236
|
allowHighRisk: false,
|
|
1273
1237
|
},
|
|
1274
1238
|
{
|
|
1275
|
-
id: 'v3-without-
|
|
1239
|
+
id: 'v3-without-options',
|
|
1276
1240
|
tool: 'bash',
|
|
1277
1241
|
pattern: 'git *',
|
|
1278
1242
|
scope: '/tmp',
|
|
@@ -1285,23 +1249,19 @@ describe('Trust Store', () => {
|
|
|
1285
1249
|
clearCache();
|
|
1286
1250
|
const rules = getAllRules();
|
|
1287
1251
|
|
|
1288
|
-
// Rule with
|
|
1289
|
-
const
|
|
1290
|
-
expect(
|
|
1291
|
-
expect(
|
|
1292
|
-
expect(
|
|
1293
|
-
expect(withPrincipal!.principalVersion).toBe('sha256-abc');
|
|
1294
|
-
expect(withPrincipal!.executionTarget).toBe('/usr/bin/node');
|
|
1295
|
-
expect(withPrincipal!.allowHighRisk).toBe(false);
|
|
1252
|
+
// Rule with optional fields should have them preserved
|
|
1253
|
+
const withOptions = rules.find((r) => r.id === 'v3-with-options');
|
|
1254
|
+
expect(withOptions).toBeDefined();
|
|
1255
|
+
expect(withOptions!.executionTarget).toBe('/usr/bin/node');
|
|
1256
|
+
expect(withOptions!.allowHighRisk).toBe(false);
|
|
1296
1257
|
|
|
1297
|
-
// Rule without
|
|
1298
|
-
const
|
|
1299
|
-
expect(
|
|
1300
|
-
expect(
|
|
1301
|
-
expect(withoutPrincipal).not.toHaveProperty('principalId');
|
|
1258
|
+
// Rule without optional fields should remain without them
|
|
1259
|
+
const withoutOptions = rules.find((r) => r.id === 'v3-without-options');
|
|
1260
|
+
expect(withoutOptions).toBeDefined();
|
|
1261
|
+
expect(withoutOptions).not.toHaveProperty('executionTarget');
|
|
1302
1262
|
});
|
|
1303
1263
|
|
|
1304
|
-
test('v2 migration preserves rule meaning exactly — no
|
|
1264
|
+
test('v2 migration preserves rule meaning exactly — no extra fields added', () => {
|
|
1305
1265
|
mkdirSync(dirname(trustPath), { recursive: true });
|
|
1306
1266
|
const originalRules = [
|
|
1307
1267
|
{
|
|
@@ -1337,22 +1297,10 @@ describe('Trust Store', () => {
|
|
|
1337
1297
|
expect(migrated!.decision).toBe(original.decision);
|
|
1338
1298
|
expect(migrated!.priority).toBe(original.priority);
|
|
1339
1299
|
expect(migrated!.createdAt).toBe(original.createdAt);
|
|
1340
|
-
// No
|
|
1341
|
-
expect(migrated).not.toHaveProperty('principalKind');
|
|
1342
|
-
expect(migrated).not.toHaveProperty('principalId');
|
|
1343
|
-
expect(migrated).not.toHaveProperty('principalVersion');
|
|
1300
|
+
// No extra fields were injected by migration
|
|
1344
1301
|
expect(migrated).not.toHaveProperty('executionTarget');
|
|
1345
1302
|
expect(migrated).not.toHaveProperty('allowHighRisk');
|
|
1346
1303
|
}
|
|
1347
|
-
|
|
1348
|
-
// Verify disk representation also has no principal fields on user rules
|
|
1349
|
-
const data = JSON.parse(readFileSync(trustPath, 'utf-8'));
|
|
1350
|
-
for (const original of originalRules) {
|
|
1351
|
-
const diskRule = data.rules.find((r: { id: string }) => r.id === original.id);
|
|
1352
|
-
expect(diskRule).toBeDefined();
|
|
1353
|
-
expect(diskRule).not.toHaveProperty('principalKind');
|
|
1354
|
-
expect(diskRule).not.toHaveProperty('principalId');
|
|
1355
|
-
}
|
|
1356
1304
|
});
|
|
1357
1305
|
|
|
1358
1306
|
test('v1 → v3 full migration preserves rules and adds priority', () => {
|
|
@@ -1374,54 +1322,15 @@ describe('Trust Store', () => {
|
|
|
1374
1322
|
expect(rule).toBeDefined();
|
|
1375
1323
|
// v1 → v2 adds priority 100
|
|
1376
1324
|
expect(rule!.priority).toBe(100);
|
|
1377
|
-
// v2 → v3 adds no principal fields
|
|
1378
|
-
expect(rule).not.toHaveProperty('principalKind');
|
|
1379
|
-
expect(rule).not.toHaveProperty('principalId');
|
|
1380
1325
|
// File should be v3 on disk
|
|
1381
1326
|
const data = JSON.parse(readFileSync(trustPath, 'utf-8'));
|
|
1382
1327
|
expect(data.version).toBe(3);
|
|
1383
1328
|
});
|
|
1384
1329
|
});
|
|
1385
1330
|
|
|
1386
|
-
// ──
|
|
1387
|
-
// These tests verify that addRule() without explicit principal options
|
|
1388
|
-
// creates wildcard rules. The TrustRule schema *does* support principal
|
|
1389
|
-
// and version fields (since PR 14), but they are only set when explicitly
|
|
1390
|
-
// provided via the options parameter.
|
|
1391
|
-
|
|
1392
|
-
describe('backward compat: addRule without principal options (PR 2/40)', () => {
|
|
1393
|
-
test('addRule without principal options creates rules without principal fields', () => {
|
|
1394
|
-
const rule = addRule('skill_test_tool', 'skill_test_tool:*', '/tmp');
|
|
1395
|
-
expect(rule).not.toHaveProperty('principalKind');
|
|
1396
|
-
expect(rule).not.toHaveProperty('principalId');
|
|
1397
|
-
expect(rule).not.toHaveProperty('principalVersion');
|
|
1398
|
-
expect(rule).not.toHaveProperty('executionTarget');
|
|
1399
|
-
expect(rule).not.toHaveProperty('allowHighRisk');
|
|
1400
|
-
});
|
|
1401
|
-
|
|
1402
|
-
test('findHighestPriorityRule matches without policy context (backward compat)', () => {
|
|
1403
|
-
addRule('skill_test_tool', 'skill_test_tool:*', '/tmp', 'allow', 200);
|
|
1404
|
-
// Calling without the optional 4th ctx parameter still matches wildcard rules
|
|
1405
|
-
const match = findHighestPriorityRule('skill_test_tool', ['skill_test_tool:do-thing'], '/tmp');
|
|
1406
|
-
expect(match).not.toBeNull();
|
|
1407
|
-
expect(match!.decision).toBe('allow');
|
|
1408
|
-
});
|
|
1409
|
-
|
|
1410
|
-
test('trust file schema is v3 (rules created without principal fields)', () => {
|
|
1411
|
-
addRule('skill_test_tool', 'skill_test_tool:*', '/tmp');
|
|
1412
|
-
const raw = JSON.parse(readFileSync(trustPath, 'utf-8'));
|
|
1413
|
-
expect(raw.version).toBe(3);
|
|
1414
|
-
const userRule = raw.rules.find((r: { pattern: string }) => r.pattern === 'skill_test_tool:*');
|
|
1415
|
-
expect(userRule).toBeDefined();
|
|
1416
|
-
// addRule without principal options doesn't set principal fields
|
|
1417
|
-
expect(userRule).not.toHaveProperty('principalVersion');
|
|
1418
|
-
expect(userRule).not.toHaveProperty('principalKind');
|
|
1419
|
-
});
|
|
1420
|
-
});
|
|
1421
|
-
|
|
1422
|
-
// ── principal-aware rule matching (PR 16) ──────────────────────
|
|
1331
|
+
// ── executionTarget-aware rule matching ──────────────────────
|
|
1423
1332
|
|
|
1424
|
-
describe('
|
|
1333
|
+
describe('executionTarget-aware rule matching', () => {
|
|
1425
1334
|
/**
|
|
1426
1335
|
* Helper: write a v3 trust file with the given rules directly to disk,
|
|
1427
1336
|
* then clear the cache so the next getRules() call picks them up.
|
|
@@ -1432,26 +1341,17 @@ describe('Trust Store', () => {
|
|
|
1432
1341
|
clearCache();
|
|
1433
1342
|
}
|
|
1434
1343
|
|
|
1435
|
-
// ── wildcard semantics (no
|
|
1344
|
+
// ── wildcard semantics (no executionTarget on rule) ──────────
|
|
1436
1345
|
|
|
1437
|
-
describe('wildcard semantics — rules without
|
|
1438
|
-
test('rule with no
|
|
1346
|
+
describe('wildcard semantics — rules without executionTarget', () => {
|
|
1347
|
+
test('rule with no executionTarget matches when no context is provided', () => {
|
|
1439
1348
|
addRule('bash', 'git *', '/tmp', 'allow', 200);
|
|
1440
1349
|
const match = findHighestPriorityRule('bash', ['git status'], '/tmp');
|
|
1441
1350
|
expect(match).not.toBeNull();
|
|
1442
1351
|
expect(match!.decision).toBe('allow');
|
|
1443
1352
|
});
|
|
1444
1353
|
|
|
1445
|
-
test('rule with no
|
|
1446
|
-
addRule('bash', 'git *', '/tmp', 'allow', 200);
|
|
1447
|
-
const match = findHighestPriorityRule('bash', ['git status'], '/tmp', {
|
|
1448
|
-
principal: { kind: 'skill', id: 'my-skill', version: 'v1' },
|
|
1449
|
-
});
|
|
1450
|
-
expect(match).not.toBeNull();
|
|
1451
|
-
expect(match!.decision).toBe('allow');
|
|
1452
|
-
});
|
|
1453
|
-
|
|
1454
|
-
test('rule with no principal fields matches any execution target', () => {
|
|
1354
|
+
test('rule with no executionTarget matches any execution target', () => {
|
|
1455
1355
|
addRule('bash', 'git *', '/tmp', 'allow', 200);
|
|
1456
1356
|
const match = findHighestPriorityRule('bash', ['git status'], '/tmp', {
|
|
1457
1357
|
executionTarget: '/usr/bin/node',
|
|
@@ -1459,186 +1359,6 @@ describe('Trust Store', () => {
|
|
|
1459
1359
|
expect(match).not.toBeNull();
|
|
1460
1360
|
expect(match!.decision).toBe('allow');
|
|
1461
1361
|
});
|
|
1462
|
-
|
|
1463
|
-
test('rule with no principal fields matches context with both principal and target', () => {
|
|
1464
|
-
addRule('bash', 'npm *', '/tmp', 'allow', 200);
|
|
1465
|
-
const match = findHighestPriorityRule('bash', ['npm install'], '/tmp', {
|
|
1466
|
-
principal: { kind: 'skill', id: 'builder', version: 'sha256-xyz' },
|
|
1467
|
-
executionTarget: '/usr/local/bin/bun',
|
|
1468
|
-
});
|
|
1469
|
-
expect(match).not.toBeNull();
|
|
1470
|
-
});
|
|
1471
|
-
});
|
|
1472
|
-
|
|
1473
|
-
// ── principalKind matching ────────────────────────────────────
|
|
1474
|
-
|
|
1475
|
-
describe('principalKind matching', () => {
|
|
1476
|
-
test('rule with principalKind matches when context kind matches', () => {
|
|
1477
|
-
seedRules([{
|
|
1478
|
-
id: 'pk-match',
|
|
1479
|
-
tool: 'bash',
|
|
1480
|
-
pattern: 'echo *',
|
|
1481
|
-
scope: 'everywhere',
|
|
1482
|
-
decision: 'allow',
|
|
1483
|
-
priority: 200,
|
|
1484
|
-
createdAt: Date.now(),
|
|
1485
|
-
principalKind: 'skill',
|
|
1486
|
-
}]);
|
|
1487
|
-
const match = findHighestPriorityRule('bash', ['echo hello'], '/tmp', {
|
|
1488
|
-
principal: { kind: 'skill' },
|
|
1489
|
-
});
|
|
1490
|
-
expect(match).not.toBeNull();
|
|
1491
|
-
expect(match!.id).toBe('pk-match');
|
|
1492
|
-
});
|
|
1493
|
-
|
|
1494
|
-
test('rule with principalKind does NOT match when context kind differs', () => {
|
|
1495
|
-
seedRules([{
|
|
1496
|
-
id: 'pk-mismatch',
|
|
1497
|
-
tool: 'bash',
|
|
1498
|
-
pattern: 'echo *',
|
|
1499
|
-
scope: 'everywhere',
|
|
1500
|
-
decision: 'allow',
|
|
1501
|
-
priority: 200,
|
|
1502
|
-
createdAt: Date.now(),
|
|
1503
|
-
principalKind: 'skill',
|
|
1504
|
-
}]);
|
|
1505
|
-
const match = findHighestPriorityRule('bash', ['echo hello'], '/tmp', {
|
|
1506
|
-
principal: { kind: 'core' },
|
|
1507
|
-
});
|
|
1508
|
-
// Should not match the pk-mismatch rule; may still match a default rule
|
|
1509
|
-
expect(match === null || match.id !== 'pk-mismatch').toBe(true);
|
|
1510
|
-
});
|
|
1511
|
-
|
|
1512
|
-
test('rule with principalKind does NOT match when no context is provided', () => {
|
|
1513
|
-
seedRules([{
|
|
1514
|
-
id: 'pk-no-ctx',
|
|
1515
|
-
tool: 'bash',
|
|
1516
|
-
pattern: 'echo *',
|
|
1517
|
-
scope: 'everywhere',
|
|
1518
|
-
decision: 'allow',
|
|
1519
|
-
priority: 200,
|
|
1520
|
-
createdAt: Date.now(),
|
|
1521
|
-
principalKind: 'skill',
|
|
1522
|
-
}]);
|
|
1523
|
-
const match = findHighestPriorityRule('bash', ['echo hello'], '/tmp');
|
|
1524
|
-
expect(match === null || match.id !== 'pk-no-ctx').toBe(true);
|
|
1525
|
-
});
|
|
1526
|
-
});
|
|
1527
|
-
|
|
1528
|
-
// ── principalId matching ──────────────────────────────────────
|
|
1529
|
-
|
|
1530
|
-
describe('principalId matching', () => {
|
|
1531
|
-
test('rule with principalKind + principalId matches exact principal', () => {
|
|
1532
|
-
seedRules([{
|
|
1533
|
-
id: 'pid-exact',
|
|
1534
|
-
tool: 'bash',
|
|
1535
|
-
pattern: 'deploy *',
|
|
1536
|
-
scope: 'everywhere',
|
|
1537
|
-
decision: 'allow',
|
|
1538
|
-
priority: 200,
|
|
1539
|
-
createdAt: Date.now(),
|
|
1540
|
-
principalKind: 'skill',
|
|
1541
|
-
principalId: 'deployer',
|
|
1542
|
-
}]);
|
|
1543
|
-
const match = findHighestPriorityRule('bash', ['deploy prod'], '/tmp', {
|
|
1544
|
-
principal: { kind: 'skill', id: 'deployer' },
|
|
1545
|
-
});
|
|
1546
|
-
expect(match).not.toBeNull();
|
|
1547
|
-
expect(match!.id).toBe('pid-exact');
|
|
1548
|
-
});
|
|
1549
|
-
|
|
1550
|
-
test('rule with principalId does NOT match different id', () => {
|
|
1551
|
-
seedRules([{
|
|
1552
|
-
id: 'pid-diff',
|
|
1553
|
-
tool: 'bash',
|
|
1554
|
-
pattern: 'deploy *',
|
|
1555
|
-
scope: 'everywhere',
|
|
1556
|
-
decision: 'allow',
|
|
1557
|
-
priority: 200,
|
|
1558
|
-
createdAt: Date.now(),
|
|
1559
|
-
principalKind: 'skill',
|
|
1560
|
-
principalId: 'deployer',
|
|
1561
|
-
}]);
|
|
1562
|
-
const match = findHighestPriorityRule('bash', ['deploy prod'], '/tmp', {
|
|
1563
|
-
principal: { kind: 'skill', id: 'other-skill' },
|
|
1564
|
-
});
|
|
1565
|
-
expect(match === null || match.id !== 'pid-diff').toBe(true);
|
|
1566
|
-
});
|
|
1567
|
-
});
|
|
1568
|
-
|
|
1569
|
-
// ── principalVersion matching ─────────────────────────────────
|
|
1570
|
-
|
|
1571
|
-
describe('principalVersion matching', () => {
|
|
1572
|
-
test('rule with principalVersion matches exact version', () => {
|
|
1573
|
-
seedRules([{
|
|
1574
|
-
id: 'pv-exact',
|
|
1575
|
-
tool: 'bash',
|
|
1576
|
-
pattern: 'build *',
|
|
1577
|
-
scope: 'everywhere',
|
|
1578
|
-
decision: 'allow',
|
|
1579
|
-
priority: 200,
|
|
1580
|
-
createdAt: Date.now(),
|
|
1581
|
-
principalKind: 'skill',
|
|
1582
|
-
principalId: 'builder',
|
|
1583
|
-
principalVersion: 'sha256-abc123',
|
|
1584
|
-
}]);
|
|
1585
|
-
const match = findHighestPriorityRule('bash', ['build all'], '/tmp', {
|
|
1586
|
-
principal: { kind: 'skill', id: 'builder', version: 'sha256-abc123' },
|
|
1587
|
-
});
|
|
1588
|
-
expect(match).not.toBeNull();
|
|
1589
|
-
expect(match!.id).toBe('pv-exact');
|
|
1590
|
-
});
|
|
1591
|
-
|
|
1592
|
-
test('rule with principalVersion does NOT match different version', () => {
|
|
1593
|
-
seedRules([{
|
|
1594
|
-
id: 'pv-diff',
|
|
1595
|
-
tool: 'bash',
|
|
1596
|
-
pattern: 'build *',
|
|
1597
|
-
scope: 'everywhere',
|
|
1598
|
-
decision: 'allow',
|
|
1599
|
-
priority: 200,
|
|
1600
|
-
createdAt: Date.now(),
|
|
1601
|
-
principalKind: 'skill',
|
|
1602
|
-
principalId: 'builder',
|
|
1603
|
-
principalVersion: 'sha256-abc123',
|
|
1604
|
-
}]);
|
|
1605
|
-
const match = findHighestPriorityRule('bash', ['build all'], '/tmp', {
|
|
1606
|
-
principal: { kind: 'skill', id: 'builder', version: 'sha256-DIFFERENT' },
|
|
1607
|
-
});
|
|
1608
|
-
expect(match === null || match.id !== 'pv-diff').toBe(true);
|
|
1609
|
-
});
|
|
1610
|
-
|
|
1611
|
-
test('rule WITHOUT principalVersion matches any version (wildcard)', () => {
|
|
1612
|
-
seedRules([{
|
|
1613
|
-
id: 'pv-wildcard',
|
|
1614
|
-
tool: 'bash',
|
|
1615
|
-
pattern: 'build *',
|
|
1616
|
-
scope: 'everywhere',
|
|
1617
|
-
decision: 'allow',
|
|
1618
|
-
priority: 200,
|
|
1619
|
-
createdAt: Date.now(),
|
|
1620
|
-
principalKind: 'skill',
|
|
1621
|
-
principalId: 'builder',
|
|
1622
|
-
// no principalVersion — should match any version
|
|
1623
|
-
}]);
|
|
1624
|
-
const matchV1 = findHighestPriorityRule('bash', ['build all'], '/tmp', {
|
|
1625
|
-
principal: { kind: 'skill', id: 'builder', version: 'v1' },
|
|
1626
|
-
});
|
|
1627
|
-
expect(matchV1).not.toBeNull();
|
|
1628
|
-
expect(matchV1!.id).toBe('pv-wildcard');
|
|
1629
|
-
|
|
1630
|
-
const matchV2 = findHighestPriorityRule('bash', ['build all'], '/tmp', {
|
|
1631
|
-
principal: { kind: 'skill', id: 'builder', version: 'v2' },
|
|
1632
|
-
});
|
|
1633
|
-
expect(matchV2).not.toBeNull();
|
|
1634
|
-
expect(matchV2!.id).toBe('pv-wildcard');
|
|
1635
|
-
|
|
1636
|
-
const matchNoVersion = findHighestPriorityRule('bash', ['build all'], '/tmp', {
|
|
1637
|
-
principal: { kind: 'skill', id: 'builder' },
|
|
1638
|
-
});
|
|
1639
|
-
expect(matchNoVersion).not.toBeNull();
|
|
1640
|
-
expect(matchNoVersion!.id).toBe('pv-wildcard');
|
|
1641
|
-
});
|
|
1642
1362
|
});
|
|
1643
1363
|
|
|
1644
1364
|
// ── executionTarget matching ──────────────────────────────────
|
|
@@ -1704,139 +1424,6 @@ describe('Trust Store', () => {
|
|
|
1704
1424
|
});
|
|
1705
1425
|
});
|
|
1706
1426
|
|
|
1707
|
-
// ── combined principal + executionTarget ───────────────────────
|
|
1708
|
-
|
|
1709
|
-
describe('combined principal + executionTarget matching', () => {
|
|
1710
|
-
test('rule with both principal and executionTarget matches when all fields match', () => {
|
|
1711
|
-
seedRules([{
|
|
1712
|
-
id: 'combo-match',
|
|
1713
|
-
tool: 'bash',
|
|
1714
|
-
pattern: 'deploy *',
|
|
1715
|
-
scope: 'everywhere',
|
|
1716
|
-
decision: 'allow',
|
|
1717
|
-
priority: 200,
|
|
1718
|
-
createdAt: Date.now(),
|
|
1719
|
-
principalKind: 'skill',
|
|
1720
|
-
principalId: 'deployer',
|
|
1721
|
-
principalVersion: 'sha256-abc',
|
|
1722
|
-
executionTarget: '/usr/bin/node',
|
|
1723
|
-
}]);
|
|
1724
|
-
const match = findHighestPriorityRule('bash', ['deploy prod'], '/tmp', {
|
|
1725
|
-
principal: { kind: 'skill', id: 'deployer', version: 'sha256-abc' },
|
|
1726
|
-
executionTarget: '/usr/bin/node',
|
|
1727
|
-
});
|
|
1728
|
-
expect(match).not.toBeNull();
|
|
1729
|
-
expect(match!.id).toBe('combo-match');
|
|
1730
|
-
});
|
|
1731
|
-
|
|
1732
|
-
test('rule with both principal and executionTarget fails if principal mismatches', () => {
|
|
1733
|
-
seedRules([{
|
|
1734
|
-
id: 'combo-bad-principal',
|
|
1735
|
-
tool: 'bash',
|
|
1736
|
-
pattern: 'deploy *',
|
|
1737
|
-
scope: 'everywhere',
|
|
1738
|
-
decision: 'allow',
|
|
1739
|
-
priority: 200,
|
|
1740
|
-
createdAt: Date.now(),
|
|
1741
|
-
principalKind: 'skill',
|
|
1742
|
-
principalId: 'deployer',
|
|
1743
|
-
executionTarget: '/usr/bin/node',
|
|
1744
|
-
}]);
|
|
1745
|
-
const match = findHighestPriorityRule('bash', ['deploy prod'], '/tmp', {
|
|
1746
|
-
principal: { kind: 'skill', id: 'other-skill' },
|
|
1747
|
-
executionTarget: '/usr/bin/node',
|
|
1748
|
-
});
|
|
1749
|
-
expect(match === null || match.id !== 'combo-bad-principal').toBe(true);
|
|
1750
|
-
});
|
|
1751
|
-
|
|
1752
|
-
test('rule with both principal and executionTarget fails if target mismatches', () => {
|
|
1753
|
-
seedRules([{
|
|
1754
|
-
id: 'combo-bad-target',
|
|
1755
|
-
tool: 'bash',
|
|
1756
|
-
pattern: 'deploy *',
|
|
1757
|
-
scope: 'everywhere',
|
|
1758
|
-
decision: 'allow',
|
|
1759
|
-
priority: 200,
|
|
1760
|
-
createdAt: Date.now(),
|
|
1761
|
-
principalKind: 'skill',
|
|
1762
|
-
principalId: 'deployer',
|
|
1763
|
-
executionTarget: '/usr/bin/node',
|
|
1764
|
-
}]);
|
|
1765
|
-
const match = findHighestPriorityRule('bash', ['deploy prod'], '/tmp', {
|
|
1766
|
-
principal: { kind: 'skill', id: 'deployer' },
|
|
1767
|
-
executionTarget: '/usr/bin/bun',
|
|
1768
|
-
});
|
|
1769
|
-
expect(match === null || match.id !== 'combo-bad-target').toBe(true);
|
|
1770
|
-
});
|
|
1771
|
-
});
|
|
1772
|
-
|
|
1773
|
-
// ── priority interaction with principal filtering ──────────────
|
|
1774
|
-
|
|
1775
|
-
describe('priority interaction with principal filtering', () => {
|
|
1776
|
-
test('higher-priority principal-specific rule wins over lower-priority wildcard', () => {
|
|
1777
|
-
seedRules([
|
|
1778
|
-
{
|
|
1779
|
-
id: 'wildcard-low',
|
|
1780
|
-
tool: 'bash',
|
|
1781
|
-
pattern: 'test *',
|
|
1782
|
-
scope: 'everywhere',
|
|
1783
|
-
decision: 'deny',
|
|
1784
|
-
priority: 50,
|
|
1785
|
-
createdAt: Date.now(),
|
|
1786
|
-
},
|
|
1787
|
-
{
|
|
1788
|
-
id: 'specific-high',
|
|
1789
|
-
tool: 'bash',
|
|
1790
|
-
pattern: 'test *',
|
|
1791
|
-
scope: 'everywhere',
|
|
1792
|
-
decision: 'allow',
|
|
1793
|
-
priority: 200,
|
|
1794
|
-
createdAt: Date.now(),
|
|
1795
|
-
principalKind: 'skill',
|
|
1796
|
-
principalId: 'tester',
|
|
1797
|
-
},
|
|
1798
|
-
]);
|
|
1799
|
-
const match = findHighestPriorityRule('bash', ['test unit'], '/tmp', {
|
|
1800
|
-
principal: { kind: 'skill', id: 'tester' },
|
|
1801
|
-
});
|
|
1802
|
-
expect(match).not.toBeNull();
|
|
1803
|
-
expect(match!.id).toBe('specific-high');
|
|
1804
|
-
expect(match!.decision).toBe('allow');
|
|
1805
|
-
});
|
|
1806
|
-
|
|
1807
|
-
test('non-matching principal rule is skipped, falling through to wildcard rule', () => {
|
|
1808
|
-
seedRules([
|
|
1809
|
-
{
|
|
1810
|
-
id: 'specific-high',
|
|
1811
|
-
tool: 'bash',
|
|
1812
|
-
pattern: 'test *',
|
|
1813
|
-
scope: 'everywhere',
|
|
1814
|
-
decision: 'allow',
|
|
1815
|
-
priority: 200,
|
|
1816
|
-
createdAt: Date.now(),
|
|
1817
|
-
principalKind: 'skill',
|
|
1818
|
-
principalId: 'deployer',
|
|
1819
|
-
},
|
|
1820
|
-
{
|
|
1821
|
-
id: 'wildcard-low',
|
|
1822
|
-
tool: 'bash',
|
|
1823
|
-
pattern: 'test *',
|
|
1824
|
-
scope: 'everywhere',
|
|
1825
|
-
decision: 'deny',
|
|
1826
|
-
priority: 50,
|
|
1827
|
-
createdAt: Date.now(),
|
|
1828
|
-
},
|
|
1829
|
-
]);
|
|
1830
|
-
// Context has kind=skill, id=tester — doesn't match 'deployer'
|
|
1831
|
-
const match = findHighestPriorityRule('bash', ['test unit'], '/tmp', {
|
|
1832
|
-
principal: { kind: 'skill', id: 'tester' },
|
|
1833
|
-
});
|
|
1834
|
-
expect(match).not.toBeNull();
|
|
1835
|
-
expect(match!.id).toBe('wildcard-low');
|
|
1836
|
-
expect(match!.decision).toBe('deny');
|
|
1837
|
-
});
|
|
1838
|
-
});
|
|
1839
|
-
|
|
1840
1427
|
// ── backward compatibility ────────────────────────────────────
|
|
1841
1428
|
|
|
1842
1429
|
describe('backward compatibility', () => {
|
|
@@ -1848,19 +1435,6 @@ describe('Trust Store', () => {
|
|
|
1848
1435
|
expect(match!.pattern).toBe('git *');
|
|
1849
1436
|
});
|
|
1850
1437
|
|
|
1851
|
-
test('existing default rules (no principal fields) match with any context', () => {
|
|
1852
|
-
// Default rules have no principal fields and should match regardless of context.
|
|
1853
|
-
// Use host_file_read which has a default ask rule (default:ask-host_file_read-global).
|
|
1854
|
-
const match = findHighestPriorityRule(
|
|
1855
|
-
'host_file_read',
|
|
1856
|
-
['host_file_read:/etc/hosts'],
|
|
1857
|
-
'/tmp',
|
|
1858
|
-
{ principal: { kind: 'skill', id: 'random-skill', version: 'v99' } },
|
|
1859
|
-
);
|
|
1860
|
-
expect(match).not.toBeNull();
|
|
1861
|
-
expect(match!.decision).toBe('ask');
|
|
1862
|
-
});
|
|
1863
|
-
|
|
1864
1438
|
test('empty PolicyContext object behaves the same as no context', () => {
|
|
1865
1439
|
addRule('bash', 'ls *', '/tmp', 'allow', 200);
|
|
1866
1440
|
const matchNoCtx = findHighestPriorityRule('bash', ['ls -la'], '/tmp');
|