@highflame/policy 2.1.42 → 2.1.44

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/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from './schema.gen.js';
5
5
  export * from './decision-effects.gen.js';
6
6
  export * from './aarm-annotations.gen.js';
7
7
  export * from './aarm-annotation.js';
8
+ export * from './privilege-catalog.gen.js';
8
9
  export * from './engine.js';
9
10
  export * from './builder.js';
10
11
  export * from './parser.js';
package/dist/index.js CHANGED
@@ -16,6 +16,9 @@ export * from './aarm-annotations.gen.js';
16
16
  // AARM annotation parser/validator (typed parse + fail-closed validation,
17
17
  // parity with Go's aarm_annotation.go).
18
18
  export * from './aarm-annotation.js';
19
+ // Role -> capability privilege catalog (privilege_scope minted into
20
+ // the trusted-service JWT; checked by role-conditioned Cedar policies).
21
+ export * from './privilege-catalog.gen.js';
19
22
  // Non-generated modules (require Node.js)
20
23
  export * from './engine.js';
21
24
  export * from './builder.js';
@@ -574,6 +574,149 @@ when {
574
574
  )
575
575
  };
576
576
  `;
577
+ const OVERWATCH_TOOLS_BASH_OPERATION_CLASSES_CEDAR = `// =============================================================================
578
+ // Tool Permissioning: Bash AST operation classes (Opt-in)
579
+ // =============================================================================
580
+ // Tiered shell control driven by Shield's bash_ast classifier. The classifier
581
+ // parses every shell command (bash, sh, zsh, dash) into an AST and emits
582
+ // tool_operation_classes, a set drawn from:
583
+ //
584
+ // readonly | network_access | write_enabling | execute_enabling | unknown
585
+ //
586
+ // One command can carry several classes ("curl -o f https://x" is
587
+ // {network_access, write_enabling}). "unknown" means the classifier saw a
588
+ // command it cannot vouch for (not in its command lists, or variable
589
+ // expanded) and is a reason to deny, never to allow. The set is never empty,
590
+ // so every shell command lands in at least one section below.
591
+ //
592
+ // Posture per class when this template is active:
593
+ //
594
+ // readonly -> allowed silently
595
+ // network_access -> blocked (Section 1)
596
+ // write_enabling -> blocked (Section 2)
597
+ // execute_enabling -> step-up human approval (Section 3)
598
+ // unknown -> step-up human approval (Section 3)
599
+ //
600
+ // Together, Sections 1 and 3 close the side door where an operation denied
601
+ // as an MCP tool is replayed through the shell: recognized network
602
+ // primitives (curl, ssh, nc) are blocked outright, and unrecognized CLI
603
+ // clients (gh, aws) classify as "unknown" and require human approval. No
604
+ // shell command reaches the network or executes silently unless it is
605
+ // purely read-only.
606
+ //
607
+ // Section 3 carries @step_up_required (AARM R4, CAP-ENF-004): Shield
608
+ // suspends the command and issues an OpenID CIBA challenge; the command
609
+ // runs only after an approver carrying the named AuthN role approves it,
610
+ // and the timeout denies it (fail-closed). Customize before deploying:
611
+ // - role: must exist in authn.roles ("security_oncall" is a placeholder;
612
+ // unknown roles reject the policy at deploy time)
613
+ // - timeout_seconds: default 86400 (24h), bounds [60, 604800]
614
+ //
615
+ // Context keys consumed:
616
+ // - tool_operation_classes: Set<String>
617
+ //
618
+ // Compliance:
619
+ // - NIST 800-53 CM-7, AC-6(1); OWASP LLM06, ASI02; MITRE ATT&CK T1059
620
+ //
621
+ // Category: tools
622
+ // Namespace: Overwatch
623
+ // =============================================================================
624
+
625
+ // ---------------------------------------------------------------------------
626
+ // Section 1: Network access
627
+ // ---------------------------------------------------------------------------
628
+ // Recognized network primitives: curl, wget, ssh, scp, nc, rsync, dig, nmap.
629
+ // Blocking this class forces network operations back through MCP tools,
630
+ // where per-tool policies apply.
631
+
632
+ @id("tools.block-bash-network")
633
+ @name("Block network access from shell commands")
634
+ @description("Blocks call_tool when the bash AST classifier marks the command network_access (curl, wget, ssh, nc), closing the side door where an operation denied as an MCP tool is replayed through the shell.")
635
+ @severity("high")
636
+ @tags("category:tools,threat:exfiltration,detection:rule,surface:call-tool,owasp:asi02")
637
+ @reject_message("Tool execution blocked: network access from shell commands is restricted; use the approved MCP tool for this operation.")
638
+ forbid (
639
+ principal,
640
+ action == Overwatch::Action::"call_tool",
641
+ resource
642
+ )
643
+ when {
644
+ context has tool_operation_classes &&
645
+ context.tool_operation_classes.contains("network_access")
646
+ };
647
+
648
+ // ---------------------------------------------------------------------------
649
+ // Section 2: Filesystem and environment writes
650
+ // ---------------------------------------------------------------------------
651
+ // rm, mv, cp, chmod, dd, sed -i, output redirects (>, >>, &>), and
652
+ // environment mutation (export VAR=val, unset).
653
+
654
+ @id("tools.block-bash-write")
655
+ @name("Block filesystem and environment writes from shell")
656
+ @description("Blocks call_tool when the bash AST classifier marks the command write_enabling (rm, mv, sed -i, output redirects, environment mutation).")
657
+ @severity("high")
658
+ @tags("category:tools,detection:rule,surface:call-tool,owasp:asi02,mitre:t1059")
659
+ @reject_message("Tool execution blocked: filesystem and environment modification from shell commands is restricted in this environment.")
660
+ forbid (
661
+ principal,
662
+ action == Overwatch::Action::"call_tool",
663
+ resource
664
+ )
665
+ when {
666
+ context has tool_operation_classes &&
667
+ context.tool_operation_classes.contains("write_enabling")
668
+ };
669
+
670
+ // ---------------------------------------------------------------------------
671
+ // Section 3: Execute-enabling and unrecognized commands (step-up approval)
672
+ // ---------------------------------------------------------------------------
673
+ // Process spawning and sub-interpreters ($(...), python3, sudo, PATH=
674
+ // hijacks) plus any command the classifier could not recognize. Routed to
675
+ // human approval rather than blocked: these classes cover a large legitimate
676
+ // surface (build scripts, interpreters, CLI clients), so a human decides.
677
+ //
678
+ // Cedar is deny-overrides and Shield emits step-up only when the decision is
679
+ // Allow, so this permit must not coexist with a forbid on the same classes.
680
+ // To hard-block instead of gating on approval, replace this rule with:
681
+ //
682
+ // @id("tools.block-bash-execute-unknown")
683
+ // @name("Block execute-enabling and unknown shell commands")
684
+ // @description("Blocks call_tool when the bash AST classifier marks the command execute_enabling (spawns processes or sub-interpreters) or unknown (unrecognized or variable-expanded commands).")
685
+ // @severity("critical")
686
+ // @tags("category:tools,threat:command-injection,detection:rule,surface:call-tool,owasp:llm06,mitre:t1059")
687
+ // @reject_message("Tool execution blocked: the shell command spawns processes or contains commands that cannot be classified.")
688
+ // forbid (
689
+ // principal,
690
+ // action == Overwatch::Action::"call_tool",
691
+ // resource
692
+ // )
693
+ // when {
694
+ // context has tool_operation_classes &&
695
+ // (
696
+ // context.tool_operation_classes.contains("execute_enabling") ||
697
+ // context.tool_operation_classes.contains("unknown")
698
+ // )
699
+ // };
700
+
701
+ @id("tools.allow-bash-step-up")
702
+ @name("Permit risky shell commands with step-up approval")
703
+ @description("Permits call_tool but suspends it for human approval when the bash AST classifier marks the command execute_enabling or unknown; an approver with the configured AuthN role must approve before the command runs.")
704
+ @severity("high")
705
+ @tags("category:tools,threat:command-injection,surface:call-tool,mitre:t1059,step-up")
706
+ @step_up_required("role=security_oncall,timeout_seconds=3600")
707
+ permit (
708
+ principal,
709
+ action == Overwatch::Action::"call_tool",
710
+ resource
711
+ )
712
+ when {
713
+ context has tool_operation_classes &&
714
+ (
715
+ context.tool_operation_classes.contains("execute_enabling") ||
716
+ context.tool_operation_classes.contains("unknown")
717
+ )
718
+ };
719
+ `;
577
720
  const OVERWATCH_PRIVACY_DEFAULTS_CEDAR = `// =============================================================================
578
721
  // PII Detection (Default)
579
722
  // =============================================================================
@@ -1048,6 +1191,15 @@ export const OVERWATCH_TEMPLATES = [
1048
1191
  severity: 'critical',
1049
1192
  tags: ['category:tools', 'threat:command-injection', 'detection:rule', 'surface:call-tool', 'owasp:llm06', 'mitre:t1059'],
1050
1193
  },
1194
+ {
1195
+ id: 'tools.bash-operation-classes',
1196
+ name: 'Bash Operation Class Restrictions',
1197
+ description: 'Tiered shell control from the bash AST classifier: blocks network access and writes, requires step-up approval for execute-enabling or unrecognized commands; closes MCP guardrail bypass via the shell.',
1198
+ category: 'tools',
1199
+ cedarText: OVERWATCH_TOOLS_BASH_OPERATION_CLASSES_CEDAR,
1200
+ severity: 'high',
1201
+ tags: ['category:tools', 'threat:exfiltration', 'threat:command-injection', 'detection:rule', 'surface:call-tool', 'owasp:asi02', 'mitre:t1059', 'step-up'],
1202
+ },
1051
1203
  {
1052
1204
  id: 'privacy.defaults',
1053
1205
  name: 'PII Detection',
@@ -1118,7 +1270,7 @@ export const OVERWATCH_TEMPLATES = [
1118
1270
  /** Raw templates.json metadata for the Overwatch service. */
1119
1271
  export const OVERWATCH_TEMPLATES_JSON = `{
1120
1272
  "service": "overwatch",
1121
- "version": "5.0.0",
1273
+ "version": "5.1.0",
1122
1274
  "description": "Overwatch policy templates for IDE agent security",
1123
1275
  "categories": [
1124
1276
  {
@@ -1249,6 +1401,24 @@ export const OVERWATCH_TEMPLATES_JSON = `{
1249
1401
  "mitre:t1059"
1250
1402
  ]
1251
1403
  },
1404
+ {
1405
+ "id": "tools.bash-operation-classes",
1406
+ "name": "Bash Operation Class Restrictions",
1407
+ "description": "Tiered shell control from the bash AST classifier: blocks network access and writes, requires step-up approval for execute-enabling or unrecognized commands; closes MCP guardrail bypass via the shell.",
1408
+ "category": "tools",
1409
+ "file": "tools_bash_operation_classes.cedar",
1410
+ "severity": "high",
1411
+ "tags": [
1412
+ "category:tools",
1413
+ "threat:exfiltration",
1414
+ "threat:command-injection",
1415
+ "detection:rule",
1416
+ "surface:call-tool",
1417
+ "owasp:asi02",
1418
+ "mitre:t1059",
1419
+ "step-up"
1420
+ ]
1421
+ },
1252
1422
  {
1253
1423
  "id": "privacy.defaults",
1254
1424
  "name": "PII Detection",
@@ -0,0 +1,17 @@
1
+ export declare const PRIVILEGE_CATALOG_VERSION = "2026-06-08";
2
+ /** Every role in the catalog, in declaration order. */
3
+ export declare const PRIVILEGE_CATALOG_ROLES: readonly string[];
4
+ /** Every product the catalog applies to, in declaration order. */
5
+ export declare const PRIVILEGE_CATALOG_PRODUCTS: readonly string[];
6
+ /**
7
+ * Capabilities granted to `role` for `product`, as a fresh sorted
8
+ * `"<product>:<action>"[]`. Returns `[]` for an unknown role or product
9
+ * (least privilege). The returned array is a copy; callers may mutate it.
10
+ */
11
+ export declare function privilegeScopeForProduct(role: string, product: string): string[];
12
+ /**
13
+ * Capabilities granted to `role` across every catalog product, as a fresh
14
+ * sorted+deduped `"<product>:<action>"[]`. Returns `[]` for an unknown
15
+ * role (least privilege). The returned array is a copy.
16
+ */
17
+ export declare function privilegeScopeForRole(role: string): string[];
@@ -0,0 +1,97 @@
1
+ // Code generated by highflame-policy-codegen. DO NOT EDIT.
2
+ // Source: schemas/privilege_catalog.json
3
+ //
4
+ // Role -> capability privilege catalog. Maps each RBAC role to the
5
+ // namespaced "<product>:<action>" capabilities it is provisioned to
6
+ // drive. privilege_scope is minted into the trusted-service JWT and
7
+ // checked by role-conditioned Cedar policies. Grants are validated
8
+ // against each product's Cedar action schema at codegen time.
9
+ // See adrs/0006-privilege-scope-catalog.md.
10
+ export const PRIVILEGE_CATALOG_VERSION = '2026-06-08';
11
+ /** Every role in the catalog, in declaration order. */
12
+ export const PRIVILEGE_CATALOG_ROLES = [
13
+ 'auditor',
14
+ 'viewer',
15
+ 'support',
16
+ 'member',
17
+ 'admin',
18
+ 'partner-admin',
19
+ 'super-admin',
20
+ ];
21
+ /** Every product the catalog applies to, in declaration order. */
22
+ export const PRIVILEGE_CATALOG_PRODUCTS = [
23
+ 'guardrails',
24
+ 'overwatch',
25
+ 'ai_gateway',
26
+ ];
27
+ // Role -> product -> sorted namespaced capabilities for that exact
28
+ // (role, product). Read via privilegeScopeForProduct.
29
+ const PRIVILEGE_SCOPE_BY_ROLE_PRODUCT = {
30
+ 'auditor': {
31
+ 'guardrails': ['guardrails:read_file'],
32
+ 'overwatch': ['overwatch:read_file'],
33
+ 'ai_gateway': ['ai_gateway:read_file'],
34
+ },
35
+ 'viewer': {
36
+ 'guardrails': ['guardrails:process_prompt', 'guardrails:read_file'],
37
+ 'overwatch': ['overwatch:process_prompt', 'overwatch:read_file'],
38
+ 'ai_gateway': ['ai_gateway:process_prompt', 'ai_gateway:read_file'],
39
+ },
40
+ 'support': {
41
+ 'guardrails': ['guardrails:process_prompt', 'guardrails:read_file'],
42
+ 'overwatch': ['overwatch:process_prompt', 'overwatch:read_file'],
43
+ 'ai_gateway': ['ai_gateway:process_prompt', 'ai_gateway:read_file'],
44
+ },
45
+ 'member': {
46
+ 'guardrails': ['guardrails:call_tool', 'guardrails:connect_server', 'guardrails:process_prompt', 'guardrails:read_file'],
47
+ 'overwatch': ['overwatch:call_tool', 'overwatch:connect_server', 'overwatch:process_prompt', 'overwatch:read_file'],
48
+ 'ai_gateway': ['ai_gateway:call_tool', 'ai_gateway:connect_server', 'ai_gateway:process_prompt', 'ai_gateway:read_file'],
49
+ },
50
+ 'admin': {
51
+ 'guardrails': ['guardrails:call_tool', 'guardrails:connect_server', 'guardrails:process_prompt', 'guardrails:read_file', 'guardrails:write_file'],
52
+ 'overwatch': ['overwatch:call_tool', 'overwatch:connect_server', 'overwatch:process_prompt', 'overwatch:read_file', 'overwatch:write_file'],
53
+ 'ai_gateway': ['ai_gateway:call_tool', 'ai_gateway:connect_server', 'ai_gateway:process_prompt', 'ai_gateway:read_file', 'ai_gateway:write_file'],
54
+ },
55
+ 'partner-admin': {
56
+ 'guardrails': ['guardrails:call_tool', 'guardrails:connect_server', 'guardrails:process_prompt', 'guardrails:read_file', 'guardrails:write_file'],
57
+ 'overwatch': ['overwatch:call_tool', 'overwatch:connect_server', 'overwatch:process_prompt', 'overwatch:read_file', 'overwatch:write_file'],
58
+ 'ai_gateway': ['ai_gateway:call_tool', 'ai_gateway:connect_server', 'ai_gateway:process_prompt', 'ai_gateway:read_file', 'ai_gateway:write_file'],
59
+ },
60
+ 'super-admin': {
61
+ 'guardrails': ['guardrails:call_tool', 'guardrails:connect_server', 'guardrails:process_prompt', 'guardrails:read_file', 'guardrails:write_file'],
62
+ 'overwatch': ['overwatch:call_tool', 'overwatch:connect_server', 'overwatch:process_prompt', 'overwatch:read_file', 'overwatch:write_file'],
63
+ 'ai_gateway': ['ai_gateway:call_tool', 'ai_gateway:connect_server', 'ai_gateway:process_prompt', 'ai_gateway:read_file', 'ai_gateway:write_file'],
64
+ },
65
+ };
66
+ // Role -> sorted+deduped union of namespaced capabilities across every
67
+ // catalog product. Read via privilegeScopeForRole.
68
+ const PRIVILEGE_SCOPE_BY_ROLE = {
69
+ 'auditor': ['ai_gateway:read_file', 'guardrails:read_file', 'overwatch:read_file'],
70
+ 'viewer': ['ai_gateway:process_prompt', 'ai_gateway:read_file', 'guardrails:process_prompt', 'guardrails:read_file', 'overwatch:process_prompt', 'overwatch:read_file'],
71
+ 'support': ['ai_gateway:process_prompt', 'ai_gateway:read_file', 'guardrails:process_prompt', 'guardrails:read_file', 'overwatch:process_prompt', 'overwatch:read_file'],
72
+ 'member': ['ai_gateway:call_tool', 'ai_gateway:connect_server', 'ai_gateway:process_prompt', 'ai_gateway:read_file', 'guardrails:call_tool', 'guardrails:connect_server', 'guardrails:process_prompt', 'guardrails:read_file', 'overwatch:call_tool', 'overwatch:connect_server', 'overwatch:process_prompt', 'overwatch:read_file'],
73
+ 'admin': ['ai_gateway:call_tool', 'ai_gateway:connect_server', 'ai_gateway:process_prompt', 'ai_gateway:read_file', 'ai_gateway:write_file', 'guardrails:call_tool', 'guardrails:connect_server', 'guardrails:process_prompt', 'guardrails:read_file', 'guardrails:write_file', 'overwatch:call_tool', 'overwatch:connect_server', 'overwatch:process_prompt', 'overwatch:read_file', 'overwatch:write_file'],
74
+ 'partner-admin': ['ai_gateway:call_tool', 'ai_gateway:connect_server', 'ai_gateway:process_prompt', 'ai_gateway:read_file', 'ai_gateway:write_file', 'guardrails:call_tool', 'guardrails:connect_server', 'guardrails:process_prompt', 'guardrails:read_file', 'guardrails:write_file', 'overwatch:call_tool', 'overwatch:connect_server', 'overwatch:process_prompt', 'overwatch:read_file', 'overwatch:write_file'],
75
+ 'super-admin': ['ai_gateway:call_tool', 'ai_gateway:connect_server', 'ai_gateway:process_prompt', 'ai_gateway:read_file', 'ai_gateway:write_file', 'guardrails:call_tool', 'guardrails:connect_server', 'guardrails:process_prompt', 'guardrails:read_file', 'guardrails:write_file', 'overwatch:call_tool', 'overwatch:connect_server', 'overwatch:process_prompt', 'overwatch:read_file', 'overwatch:write_file'],
76
+ };
77
+ /**
78
+ * Capabilities granted to `role` for `product`, as a fresh sorted
79
+ * `"<product>:<action>"[]`. Returns `[]` for an unknown role or product
80
+ * (least privilege). The returned array is a copy; callers may mutate it.
81
+ */
82
+ export function privilegeScopeForProduct(role, product) {
83
+ const byProduct = PRIVILEGE_SCOPE_BY_ROLE_PRODUCT[role];
84
+ if (!byProduct)
85
+ return [];
86
+ const caps = byProduct[product];
87
+ return caps ? [...caps] : [];
88
+ }
89
+ /**
90
+ * Capabilities granted to `role` across every catalog product, as a fresh
91
+ * sorted+deduped `"<product>:<action>"[]`. Returns `[]` for an unknown
92
+ * role (least privilege). The returned array is a copy.
93
+ */
94
+ export function privilegeScopeForRole(role) {
95
+ const caps = PRIVILEGE_SCOPE_BY_ROLE[role];
96
+ return caps ? [...caps] : [];
97
+ }
package/dist/types.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from './schema.gen.js';
5
5
  export * from './decision-effects.gen.js';
6
6
  export * from './aarm-annotations.gen.js';
7
7
  export * from './aarm-annotation.js';
8
+ export * from './privilege-catalog.gen.js';
8
9
  export * from './builder.js';
9
10
  export * from './errors.js';
10
11
  export * from './annotations.js';
package/dist/types.js CHANGED
@@ -18,6 +18,9 @@ export * from './aarm-annotations.gen.js';
18
18
  // AARM annotation parser/validator (browser-safe — typed parse + fail-closed
19
19
  // validation; Studio lints with the exact rules Shield runs at sync time).
20
20
  export * from './aarm-annotation.js';
21
+ // Role -> capability privilege catalog (browser-safe — Studio surfaces
22
+ // the default scope per role; checked by role-conditioned Cedar policies).
23
+ export * from './privilege-catalog.gen.js';
21
24
  // PolicyBuilder - works in browser (no WASM dependency)
22
25
  export * from './builder.js';
23
26
  // Error types - works in browser (no WASM dependency)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highflame/policy",
3
- "version": "2.1.42",
3
+ "version": "2.1.44",
4
4
  "engines": {
5
5
  "node": ">=18"
6
6
  },