@highflame/policy 2.2.40 → 2.2.41

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.
@@ -1648,8 +1648,23 @@ const AGENT_OPS_TOOLS_MCP_SERVER_ALLOWLIST_CEDAR = `// =========================
1648
1648
  // MCP Server Allowlist
1649
1649
  // =============================================================================
1650
1650
  // Restricts MCP server connections to a pre-approved list. Customize the
1651
- // \`context.mcp_server\` values in the permit rule to match the allowed
1652
- // servers for your environment.
1651
+ // \`context.mcp_server\` values to match the allowed servers for your
1652
+ // environment the SAME list appears in both rules and both must be edited.
1653
+ //
1654
+ // Both rules are conditional on the allowlist, and both are needed:
1655
+ //
1656
+ // permit ... when { allowlisted } grants access; Cedar is default-deny,
1657
+ // so without this nothing allows the
1658
+ // allowlisted servers either.
1659
+ // forbid ... unless { allowlisted } claws back everything else, including
1660
+ // when a broad Baseline Permit is loaded.
1661
+ //
1662
+ // The forbid MUST carry the negation. \`forbid\` always beats \`permit\` in Cedar,
1663
+ // so an UNCONDITIONAL forbid denies the allowlisted servers too — which is
1664
+ // what this template used to do (highflame-policy#186).
1665
+ //
1666
+ // The \`context has mcp_server\` guard stays inside both conditions: a request
1667
+ // naming no server cannot be on the allowlist, so it is denied.
1653
1668
  //
1654
1669
  // Context keys consumed:
1655
1670
  // - mcp_server: String
@@ -1676,7 +1691,7 @@ when {
1676
1691
 
1677
1692
  @id("tools.deny-non-allowlisted-mcp")
1678
1693
  @name("Block non-allowlisted MCP servers")
1679
- @description("Blocks connect_server unconditionally so only the allowlist permit applies.")
1694
+ @description("Blocks connect_server unless mcp_server is in the allowlist.")
1680
1695
  @severity("medium")
1681
1696
  @tags("category:tools,surface:connect-server,scope:org-wide,posture:deny-default")
1682
1697
  @reject_message("MCP server connection blocked: server is not on the allowlist.")
@@ -1684,7 +1699,12 @@ forbid (
1684
1699
  principal,
1685
1700
  action == AgentOps::Action::"connect_server",
1686
1701
  resource
1687
- );
1702
+ )
1703
+ unless {
1704
+ context has mcp_server &&
1705
+ (context.mcp_server == "filesystem" ||
1706
+ context.mcp_server == "playwright")
1707
+ };
1688
1708
  `;
1689
1709
  const AGENT_OPS_SECURITY_CHAT_ASSISTANT_CEDAR = `// =============================================================================
1690
1710
  // Chat Assistant — Security
@@ -4460,8 +4480,8 @@ const AGENT_OPS_ORGANIZATION_DENY_BASELINE_CEDAR = `// =========================
4460
4480
  // =============================================================================
4461
4481
 
4462
4482
  @id("organization.deny-baseline")
4463
- @name("Block all actions (deny baseline)")
4464
- @description("Blocks all actions; pair with scoped permit rules for a deny-by-default posture.")
4483
+ @name("Block all actions (kill switch)")
4484
+ @description("Blocks all actions unconditionally. A kill switch, not a baseline: forbid beats permit in Cedar, so this also disables every permit in the set.")
4465
4485
  @severity("high")
4466
4486
  @tags("category:organization,posture:deny-default,scope:org-wide")
4467
4487
  @reject_message("Request blocked: this organization uses a deny-by-default baseline — only explicitly permitted actions are allowed.")
@@ -370,8 +370,23 @@ const AI_GATEWAY_TOOLS_MCP_SERVER_ALLOWLIST_CEDAR = `// ========================
370
370
  // MCP Server Allowlist
371
371
  // =============================================================================
372
372
  // Restricts MCP server connections to a pre-approved list. Customize the
373
- // \`context.mcp_server\` values in the permit rule to match the allowed
374
- // servers for your environment.
373
+ // \`context.mcp_server\` values to match the allowed servers for your
374
+ // environment the SAME list appears in both rules and both must be edited.
375
+ //
376
+ // Both rules are conditional on the allowlist, and both are needed:
377
+ //
378
+ // permit ... when { allowlisted } grants access; Cedar is default-deny,
379
+ // so without this nothing allows the
380
+ // allowlisted servers either.
381
+ // forbid ... unless { allowlisted } claws back everything else, including
382
+ // when a broad Baseline Permit is loaded.
383
+ //
384
+ // The forbid MUST carry the negation. \`forbid\` always beats \`permit\` in Cedar,
385
+ // so an UNCONDITIONAL forbid denies the allowlisted servers too — which is
386
+ // what this template used to do (highflame-policy#186).
387
+ //
388
+ // The \`context has mcp_server\` guard stays inside both conditions: a request
389
+ // naming no server cannot be on the allowlist, so it is denied.
375
390
  //
376
391
  // Context keys consumed:
377
392
  // - mcp_server: String
@@ -398,7 +413,7 @@ when {
398
413
 
399
414
  @id("tools.deny-non-allowlisted-mcp")
400
415
  @name("Block non-allowlisted MCP servers")
401
- @description("Blocks connect_server unconditionally so only the allowlist permit applies.")
416
+ @description("Blocks connect_server unless mcp_server is in the allowlist.")
402
417
  @severity("medium")
403
418
  @tags("category:tools,surface:connect-server,scope:org-wide,posture:deny-default")
404
419
  @reject_message("MCP server connection blocked: server is not on the allowlist.")
@@ -406,7 +421,12 @@ forbid (
406
421
  principal,
407
422
  action == AIGateway::Action::"connect_server",
408
423
  resource
409
- );
424
+ )
425
+ unless {
426
+ context has mcp_server &&
427
+ (context.mcp_server == "filesystem" ||
428
+ context.mcp_server == "playwright")
429
+ };
410
430
  `;
411
431
  const AI_GATEWAY_TOOLS_MCP_TOOL_PERMISSIONS_CEDAR = `// =============================================================================
412
432
  // MCP Tool Permissions
@@ -1,5 +1,5 @@
1
1
  import type { DetectorCard } from './detector-card-types.gen';
2
- export declare const AI_GATEWAY_DETECTOR_SPEC_VERSION = "1.0.0";
2
+ export declare const AI_GATEWAY_DETECTOR_SPEC_VERSION = "1.1.0";
3
3
  export declare const AI_GATEWAY_DETECTORS: readonly DetectorCard[];
4
4
  export declare const AI_GATEWAY_FIELD_TO_DETECTORS: Readonly<Record<string, readonly string[]>>;
5
5
  export declare function aiGatewayDetectorById(id: string): DetectorCard | undefined;
@@ -1,4 +1,4 @@
1
- export const AI_GATEWAY_DETECTOR_SPEC_VERSION = "1.0.0";
1
+ export const AI_GATEWAY_DETECTOR_SPEC_VERSION = "1.1.0";
2
2
  export const AI_GATEWAY_DETECTORS = [
3
3
  {
4
4
  id: "injection",
@@ -177,11 +177,25 @@ export const AI_GATEWAY_DETECTORS = [
177
177
  inhouse: false,
178
178
  model: null,
179
179
  latencyP50Ms: 250,
180
- emits: [{ name: "package_install_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff a package install/download command was recognized in the event." }, { name: "packages_checked", type: "Long", modifiable: false, semantic: "count", description: "Number of unique packages checked against osv.dev." }, { name: "malicious_package_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff any referenced package has a known-malicious (MAL-) OSV advisory." }, { name: "malicious_package_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "100 when a malicious package was detected, 0 otherwise." }, { name: "malicious_packages", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Malicious packages as ecosystem/name[@version] strings." }, { name: "package_names", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Names of all packages parsed from install commands (without ecosystem prefix), e.g. 'requests', 'left-pad'. Available even when the osv.dev lookup fails." }, { name: "package_ecosystems", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Unique package ecosystems seen in this event, e.g. 'PyPI', 'npm'. Useful for policies that restrict which ecosystems are permitted." }, { name: "package_advisory_count", type: "Long", modifiable: false, semantic: "count", description: "Total non-malicious OSV advisories (GHSA- / CVE- IDs) found across all checked packages. Excludes MAL- entries counted in malicious_package_detected." }, { name: "package_risk_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "Graduated supply-chain risk score (0-100). 100 when any malicious package is found; proportional to advisory_count for advisory-only packages; 0 when all packages are clean." }, { name: "package_check_status", type: "String", modifiable: false, semantic: "category_label", description: "Set to 'degraded' when the osv.dev lookup failed (fail-open); empty otherwise." }],
180
+ emits: [{ name: "package_install_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff a package install/download command was recognized in the event." }, { name: "packages_checked", type: "Long", modifiable: false, semantic: "count", description: "Number of unique packages checked against osv.dev." }, { name: "malicious_package_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff any referenced package has a known-malicious (MAL-) OSV advisory." }, { name: "malicious_package_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "100 when a malicious package was detected, 0 otherwise." }, { name: "malicious_packages", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Malicious packages as ecosystem/name[@version] strings." }, { name: "package_check_status", type: "String", modifiable: false, semantic: "category_label", description: "Set to 'degraded' when the osv.dev lookup failed (fail-open); empty otherwise." }],
181
181
  supportedModes: ["enforce", "monitor", "alert"],
182
182
  defendsAgainst: ["supply_chain"],
183
183
  exampleAttacks: [{ title: "Typosquatted PyPI install via MCP bash tool", vulnerabilityId: "supply_chain", snippet: "pip install reqeusts", expectedSignal: {} }],
184
184
  },
185
+ {
186
+ id: "package_reputation",
187
+ displayName: "Package Reputation (OSV)",
188
+ category: "tool_safety",
189
+ stability: "preview",
190
+ tier: "slow",
191
+ inhouse: false,
192
+ model: null,
193
+ latencyP50Ms: 250,
194
+ emits: [{ name: "malicious_package_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff any referenced package has a known-malicious (MAL-) OSV advisory." }, { name: "malicious_packages", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Malicious packages as ecosystem/name[@version] strings." }, { name: "package_names", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Names of all packages parsed from install commands (without ecosystem prefix), e.g. 'requests', 'left-pad'. Available even when the osv.dev lookup fails." }, { name: "package_ecosystems", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Unique package ecosystems seen in this event, e.g. 'PyPI', 'npm'. Useful for policies that restrict which ecosystems are permitted." }, { name: "package_advisory_count", type: "Long", modifiable: false, semantic: "count", description: "Total non-malicious OSV advisories (GHSA- / CVE- IDs) found across all checked packages. Excludes MAL- entries counted in malicious_package_detected." }, { name: "package_risk_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "Graduated supply-chain risk score (0-100). 100 when any malicious package is found; proportional to advisory_count for advisory-only packages; 0 when all packages are clean." }],
195
+ supportedModes: ["enforce", "monitor", "alert"],
196
+ defendsAgainst: ["supply_chain"],
197
+ exampleAttacks: [{ title: "Install of a package carrying a malicious advisory", vulnerabilityId: "supply_chain", snippet: "pip install evil-pkg", expectedSignal: { "malicious_package_detected": true } }],
198
+ },
185
199
  ];
186
200
  // Semantic field → contributing detector ids (producesAttrs + normalizationAliases,
187
201
  // resolved at codegen). Used by the client field→detector resolver — no Shield round-trip.
@@ -196,20 +210,20 @@ export const AI_GATEWAY_FIELD_TO_DETECTORS = {
196
210
  "jailbreak_score": ["injection"],
197
211
  "loop_count": ["loop_detector"],
198
212
  "loop_detected": ["loop_detector"],
199
- "malicious_package_detected": ["malicious_package"],
213
+ "malicious_package_detected": ["malicious_package", "package_reputation"],
200
214
  "malicious_package_score": ["malicious_package"],
201
- "malicious_packages": ["malicious_package"],
215
+ "malicious_packages": ["malicious_package", "package_reputation"],
202
216
  "mcp_config_risk": ["mcp_risk"],
203
217
  "mcp_risk_score": ["mcp_risk"],
204
218
  "mcp_server": ["mcp_risk"],
205
219
  "mcp_server_verified": ["mcp_risk"],
206
220
  "mcp_tool": ["mcp_risk"],
207
- "package_advisory_count": ["malicious_package"],
221
+ "package_advisory_count": ["package_reputation"],
208
222
  "package_check_status": ["malicious_package"],
209
- "package_ecosystems": ["malicious_package"],
223
+ "package_ecosystems": ["package_reputation"],
210
224
  "package_install_detected": ["malicious_package"],
211
- "package_names": ["malicious_package"],
212
- "package_risk_score": ["malicious_package"],
225
+ "package_names": ["package_reputation"],
226
+ "package_risk_score": ["package_reputation"],
213
227
  "packages_checked": ["malicious_package"],
214
228
  "pattern_type": ["action_pattern"],
215
229
  "pii_count": ["pii"],
@@ -1193,8 +1193,23 @@ const GUARDRAILS_TOOLS_MCP_SERVER_ALLOWLIST_CEDAR = `// ========================
1193
1193
  // MCP Server Allowlist
1194
1194
  // =============================================================================
1195
1195
  // Restricts MCP server connections to a pre-approved list. Customize the
1196
- // \`context.mcp_server\` values in the permit rule to match the allowed
1197
- // servers for your environment.
1196
+ // \`context.mcp_server\` values to match the allowed servers for your
1197
+ // environment the SAME list appears in both rules and both must be edited.
1198
+ //
1199
+ // Both rules are conditional on the allowlist, and both are needed:
1200
+ //
1201
+ // permit ... when { allowlisted } grants access; Cedar is default-deny,
1202
+ // so without this nothing allows the
1203
+ // allowlisted servers either.
1204
+ // forbid ... unless { allowlisted } claws back everything else, including
1205
+ // when a broad Baseline Permit is loaded.
1206
+ //
1207
+ // The forbid MUST carry the negation. \`forbid\` always beats \`permit\` in Cedar,
1208
+ // so an UNCONDITIONAL forbid denies the allowlisted servers too — which is
1209
+ // what this template used to do (highflame-policy#186).
1210
+ //
1211
+ // The \`context has mcp_server\` guard stays inside both conditions: a request
1212
+ // naming no server cannot be on the allowlist, so it is denied.
1198
1213
  //
1199
1214
  // Context keys consumed:
1200
1215
  // - mcp_server: String
@@ -1221,7 +1236,7 @@ when {
1221
1236
 
1222
1237
  @id("tools.deny-non-allowlisted-mcp")
1223
1238
  @name("Block non-allowlisted MCP servers")
1224
- @description("Blocks connect_server unconditionally so only the allowlist permit applies.")
1239
+ @description("Blocks connect_server unless mcp_server is in the allowlist.")
1225
1240
  @severity("medium")
1226
1241
  @tags("category:tools,surface:connect-server,scope:org-wide,posture:deny-default")
1227
1242
  @reject_message("MCP server connection blocked: server is not on the allowlist.")
@@ -1229,7 +1244,12 @@ forbid (
1229
1244
  principal,
1230
1245
  action == Guardrails::Action::"connect_server",
1231
1246
  resource
1232
- );
1247
+ )
1248
+ unless {
1249
+ context has mcp_server &&
1250
+ (context.mcp_server == "filesystem" ||
1251
+ context.mcp_server == "playwright")
1252
+ };
1233
1253
  `;
1234
1254
  const GUARDRAILS_SECURITY_CHAT_ASSISTANT_CEDAR = `// =============================================================================
1235
1255
  // Chat Assistant — Security
@@ -1,5 +1,5 @@
1
1
  import type { DetectorCard } from './detector-card-types.gen';
2
- export declare const GUARDRAILS_DETECTOR_SPEC_VERSION = "1.3.0";
2
+ export declare const GUARDRAILS_DETECTOR_SPEC_VERSION = "1.4.0";
3
3
  export declare const GUARDRAILS_DETECTORS: readonly DetectorCard[];
4
4
  export declare const GUARDRAILS_FIELD_TO_DETECTORS: Readonly<Record<string, readonly string[]>>;
5
5
  export declare function guardrailsDetectorById(id: string): DetectorCard | undefined;
@@ -1,4 +1,4 @@
1
- export const GUARDRAILS_DETECTOR_SPEC_VERSION = "1.3.0";
1
+ export const GUARDRAILS_DETECTOR_SPEC_VERSION = "1.4.0";
2
2
  export const GUARDRAILS_DETECTORS = [
3
3
  {
4
4
  id: "pii",
@@ -401,11 +401,25 @@ export const GUARDRAILS_DETECTORS = [
401
401
  inhouse: false,
402
402
  model: null,
403
403
  latencyP50Ms: 250,
404
- emits: [{ name: "package_install_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff a package install/download command was recognized in the event." }, { name: "packages_checked", type: "Long", modifiable: false, semantic: "count", description: "Number of unique packages checked against osv.dev." }, { name: "malicious_package_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff any referenced package has a known-malicious (MAL-) OSV advisory." }, { name: "malicious_package_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "100 when a malicious package was detected, 0 otherwise." }, { name: "malicious_packages", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Malicious packages as ecosystem/name[@version] strings." }, { name: "package_names", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Names of all packages parsed from install commands (without ecosystem prefix), e.g. 'requests', 'left-pad'. Available even when the osv.dev lookup fails." }, { name: "package_ecosystems", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Unique package ecosystems seen in this event, e.g. 'PyPI', 'npm'. Useful for policies that restrict which ecosystems are permitted." }, { name: "package_advisory_count", type: "Long", modifiable: false, semantic: "count", description: "Total non-malicious OSV advisories (GHSA- / CVE- IDs) found across all checked packages. Excludes MAL- entries counted in malicious_package_detected." }, { name: "package_risk_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "Graduated supply-chain risk score (0-100). 100 when any malicious package is found; proportional to advisory_count for advisory-only packages; 0 when all packages are clean." }, { name: "package_check_status", type: "String", modifiable: false, semantic: "category_label", description: "Set to 'degraded' when the osv.dev lookup failed (fail-open); empty otherwise." }],
404
+ emits: [{ name: "package_install_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff a package install/download command was recognized in the event." }, { name: "packages_checked", type: "Long", modifiable: false, semantic: "count", description: "Number of unique packages checked against osv.dev." }, { name: "malicious_package_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff any referenced package has a known-malicious (MAL-) OSV advisory." }, { name: "malicious_package_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "100 when a malicious package was detected, 0 otherwise." }, { name: "malicious_packages", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Malicious packages as ecosystem/name[@version] strings." }, { name: "package_check_status", type: "String", modifiable: false, semantic: "category_label", description: "Set to 'degraded' when the osv.dev lookup failed (fail-open); empty otherwise." }],
405
405
  supportedModes: ["enforce", "monitor", "alert"],
406
406
  defendsAgainst: ["supply_chain"],
407
407
  exampleAttacks: [{ title: "Typosquatted PyPI package install", vulnerabilityId: "supply_chain", snippet: "pip install reqeusts", expectedSignal: {} }],
408
408
  },
409
+ {
410
+ id: "package_reputation",
411
+ displayName: "Package Reputation (OSV)",
412
+ category: "tool_safety",
413
+ stability: "preview",
414
+ tier: "slow",
415
+ inhouse: false,
416
+ model: null,
417
+ latencyP50Ms: 250,
418
+ emits: [{ name: "malicious_package_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff any referenced package has a known-malicious (MAL-) OSV advisory." }, { name: "malicious_packages", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Malicious packages as ecosystem/name[@version] strings." }, { name: "package_names", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Names of all packages parsed from install commands (without ecosystem prefix), e.g. 'requests', 'left-pad'. Available even when the osv.dev lookup fails." }, { name: "package_ecosystems", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Unique package ecosystems seen in this event, e.g. 'PyPI', 'npm'. Useful for policies that restrict which ecosystems are permitted." }, { name: "package_advisory_count", type: "Long", modifiable: false, semantic: "count", description: "Total non-malicious OSV advisories (GHSA- / CVE- IDs) found across all checked packages. Excludes MAL- entries counted in malicious_package_detected." }, { name: "package_risk_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "Graduated supply-chain risk score (0-100). 100 when any malicious package is found; proportional to advisory_count for advisory-only packages; 0 when all packages are clean." }],
419
+ supportedModes: ["enforce", "monitor", "alert"],
420
+ defendsAgainst: ["supply_chain"],
421
+ exampleAttacks: [{ title: "Install of a package carrying a malicious advisory", vulnerabilityId: "supply_chain", snippet: "pip install evil-pkg", expectedSignal: { "malicious_package_detected": true } }],
422
+ },
409
423
  {
410
424
  id: "sentiment",
411
425
  displayName: "Sentiment Analysis",
@@ -510,9 +524,9 @@ export const GUARDRAILS_FIELD_TO_DETECTORS = {
510
524
  "loop_count": ["loop_detector"],
511
525
  "loop_detected": ["loop_detector"],
512
526
  "loop_tool": ["loop_detector"],
513
- "malicious_package_detected": ["malicious_package"],
527
+ "malicious_package_detected": ["malicious_package", "package_reputation"],
514
528
  "malicious_package_score": ["malicious_package"],
515
- "malicious_packages": ["malicious_package"],
529
+ "malicious_packages": ["malicious_package", "package_reputation"],
516
530
  "mcp_config_risk": ["mcp_risk"],
517
531
  "mcp_input_request_detected": ["mcp_input_request"],
518
532
  "mcp_input_request_methods": ["mcp_input_request"],
@@ -527,12 +541,12 @@ export const GUARDRAILS_FIELD_TO_DETECTORS = {
527
541
  "mip_label_name": ["file_metadata"],
528
542
  "multi_turn_detection": ["deepcontext"],
529
543
  "non_ascii_score": ["security"],
530
- "package_advisory_count": ["malicious_package"],
544
+ "package_advisory_count": ["package_reputation"],
531
545
  "package_check_status": ["malicious_package"],
532
- "package_ecosystems": ["malicious_package"],
546
+ "package_ecosystems": ["package_reputation"],
533
547
  "package_install_detected": ["malicious_package"],
534
- "package_names": ["malicious_package"],
535
- "package_risk_score": ["malicious_package"],
548
+ "package_names": ["package_reputation"],
549
+ "package_risk_score": ["package_reputation"],
536
550
  "packages_checked": ["malicious_package"],
537
551
  "path_traversal_detected": ["path_traversal"],
538
552
  "path_traversal_severity": ["path_traversal"],
@@ -1547,7 +1547,23 @@ const OVERWATCH_TOOLS_MCP_SERVER_ALLOWLIST_CEDAR = `// =========================
1547
1547
  // MCP Server Allowlist
1548
1548
  // =============================================================================
1549
1549
  // Restricts MCP server connections to a pre-approved list. Customize the
1550
- // \`context.mcp_server\` values in the permit rule to match allowed servers.
1550
+ // \`context.mcp_server\` values to match the allowed servers for your
1551
+ // environment — the SAME list appears in both rules and both must be edited.
1552
+ //
1553
+ // Both rules are conditional on the allowlist, and both are needed:
1554
+ //
1555
+ // permit ... when { allowlisted } grants access; Cedar is default-deny,
1556
+ // so without this nothing allows the
1557
+ // allowlisted servers either.
1558
+ // forbid ... unless { allowlisted } claws back everything else, including
1559
+ // when a broad Baseline Permit is loaded.
1560
+ //
1561
+ // The forbid MUST carry the negation. \`forbid\` always beats \`permit\` in Cedar,
1562
+ // so an UNCONDITIONAL forbid denies the allowlisted servers too — which is
1563
+ // what this template used to do (highflame-policy#186).
1564
+ //
1565
+ // The \`context has mcp_server\` guard stays inside both conditions: a request
1566
+ // naming no server cannot be on the allowlist, so it is denied.
1551
1567
  //
1552
1568
  // Context keys consumed:
1553
1569
  // - mcp_server: String
@@ -1573,7 +1589,7 @@ when {
1573
1589
 
1574
1590
  @id("tools.deny-non-allowlisted-mcp")
1575
1591
  @name("Block non-allowlisted MCP servers")
1576
- @description("Blocks connect_server unconditionally so only the allowlist permit applies.")
1592
+ @description("Blocks connect_server unless mcp_server is in the allowlist.")
1577
1593
  @severity("medium")
1578
1594
  @tags("category:tools,surface:connect-server,scope:org-wide,posture:deny-default")
1579
1595
  @reject_message("MCP server connection blocked: server is not on the allowlist.")
@@ -1581,7 +1597,11 @@ forbid (
1581
1597
  principal,
1582
1598
  action == Overwatch::Action::"connect_server",
1583
1599
  resource
1584
- );
1600
+ )
1601
+ unless {
1602
+ context has mcp_server &&
1603
+ (context.mcp_server == "filesystem" || context.mcp_server == "playwright")
1604
+ };
1585
1605
  `;
1586
1606
  const OVERWATCH_TOOLS_MCP_TOOL_PERMISSIONS_CEDAR = `// =============================================================================
1587
1607
  // MCP Tool Permissions
@@ -1652,8 +1672,8 @@ const OVERWATCH_ORGANIZATION_DENY_BASELINE_CEDAR = `// =========================
1652
1672
  // =============================================================================
1653
1673
 
1654
1674
  @id("organization.deny-baseline")
1655
- @name("Block all actions (deny baseline)")
1656
- @description("Blocks all actions; pair with scoped permit rules for a deny-by-default posture.")
1675
+ @name("Block all actions (kill switch)")
1676
+ @description("Blocks all actions unconditionally. A kill switch, not a baseline: forbid beats permit in Cedar, so this also disables every permit in the set.")
1657
1677
  @severity("high")
1658
1678
  @tags("category:organization,posture:deny-default,scope:org-wide")
1659
1679
  @reject_message("Request blocked: this organization uses a deny-by-default baseline — only explicitly permitted actions are allowed.")
@@ -1,5 +1,5 @@
1
1
  import type { DetectorCard } from './detector-card-types.gen';
2
- export declare const OVERWATCH_DETECTOR_SPEC_VERSION = "1.0.0";
2
+ export declare const OVERWATCH_DETECTOR_SPEC_VERSION = "1.1.0";
3
3
  export declare const OVERWATCH_DETECTORS: readonly DetectorCard[];
4
4
  export declare const OVERWATCH_FIELD_TO_DETECTORS: Readonly<Record<string, readonly string[]>>;
5
5
  export declare function overwatchDetectorById(id: string): DetectorCard | undefined;
@@ -1,4 +1,4 @@
1
- export const OVERWATCH_DETECTOR_SPEC_VERSION = "1.0.0";
1
+ export const OVERWATCH_DETECTOR_SPEC_VERSION = "1.1.0";
2
2
  export const OVERWATCH_DETECTORS = [
3
3
  {
4
4
  id: "injection",
@@ -191,11 +191,25 @@ export const OVERWATCH_DETECTORS = [
191
191
  inhouse: false,
192
192
  model: null,
193
193
  latencyP50Ms: 250,
194
- emits: [{ name: "package_install_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff a package install/download command was recognized in the tool call." }, { name: "malicious_package_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff any referenced package has a known-malicious (MAL-) OSV advisory." }, { name: "malicious_package_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "100 when a malicious package was detected, 0 otherwise." }, { name: "malicious_packages", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Malicious packages as ecosystem/name[@version] strings." }, { name: "packages_checked", type: "Long", modifiable: false, semantic: "count", description: "Number of unique packages checked against osv.dev." }, { name: "package_names", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Names of all packages parsed from install commands." }, { name: "package_ecosystems", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Unique package ecosystems seen in this event, e.g. 'PyPI', 'npm'." }, { name: "package_advisory_count", type: "Long", modifiable: false, semantic: "count", description: "Total non-malicious OSV advisories found across all checked packages." }, { name: "package_risk_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "Graduated supply-chain risk score (0-100)." }, { name: "package_check_status", type: "String", modifiable: false, semantic: "category_label", description: "Set to 'degraded' when the osv.dev lookup failed (fail-open); empty otherwise." }],
194
+ emits: [{ name: "package_install_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff a package install/download command was recognized in the event." }, { name: "packages_checked", type: "Long", modifiable: false, semantic: "count", description: "Number of unique packages checked against osv.dev." }, { name: "malicious_package_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff any referenced package has a known-malicious (MAL-) OSV advisory." }, { name: "malicious_package_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "100 when a malicious package was detected, 0 otherwise." }, { name: "malicious_packages", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Malicious packages as ecosystem/name[@version] strings." }, { name: "package_check_status", type: "String", modifiable: false, semantic: "category_label", description: "Set to 'degraded' when the osv.dev lookup failed (fail-open); empty otherwise." }],
195
195
  supportedModes: ["enforce", "monitor", "alert"],
196
196
  defendsAgainst: ["supply_chain"],
197
197
  exampleAttacks: [{ title: "Typosquatted PyPI package install", vulnerabilityId: "supply_chain", snippet: "pip install reqeusts", expectedSignal: {} }, { title: "Malicious npm package install", vulnerabilityId: "supply_chain", snippet: "npm install event-stream@3.3.6", expectedSignal: {} }],
198
198
  },
199
+ {
200
+ id: "package_reputation",
201
+ displayName: "Package Reputation (OSV)",
202
+ category: "tool_safety",
203
+ stability: "preview",
204
+ tier: "slow",
205
+ inhouse: false,
206
+ model: null,
207
+ latencyP50Ms: 250,
208
+ emits: [{ name: "malicious_package_detected", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff any referenced package has a known-malicious (MAL-) OSV advisory." }, { name: "malicious_packages", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Malicious packages as ecosystem/name[@version] strings." }, { name: "package_names", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Names of all packages parsed from install commands (without ecosystem prefix), e.g. 'requests', 'left-pad'. Available even when the osv.dev lookup fails." }, { name: "package_ecosystems", type: "Set<String>", modifiable: false, semantic: "category_set", description: "Unique package ecosystems seen in this event, e.g. 'PyPI', 'npm'. Useful for policies that restrict which ecosystems are permitted." }, { name: "package_advisory_count", type: "Long", modifiable: false, semantic: "count", description: "Total non-malicious OSV advisories (GHSA- / CVE- IDs) found across all checked packages. Excludes MAL- entries counted in malicious_package_detected." }, { name: "package_risk_score", type: "Long", modifiable: false, semantic: "severity_0_100", description: "Graduated supply-chain risk score (0-100). 100 when any malicious package is found; proportional to advisory_count for advisory-only packages; 0 when all packages are clean." }],
209
+ supportedModes: ["enforce", "monitor", "alert"],
210
+ defendsAgainst: ["supply_chain"],
211
+ exampleAttacks: [{ title: "Install of a package carrying a malicious advisory", vulnerabilityId: "supply_chain", snippet: "pip install evil-pkg", expectedSignal: { "malicious_package_detected": true } }],
212
+ },
199
213
  ];
200
214
  // Semantic field → contributing detector ids (producesAttrs + normalizationAliases,
201
215
  // resolved at codegen). Used by the client field→detector resolver — no Shield round-trip.
@@ -215,20 +229,20 @@ export const OVERWATCH_FIELD_TO_DETECTORS = {
215
229
  "loop_count": ["loop_detector"],
216
230
  "loop_detected": ["loop_detector"],
217
231
  "loop_tool": ["loop_detector"],
218
- "malicious_package_detected": ["malicious_package"],
232
+ "malicious_package_detected": ["malicious_package", "package_reputation"],
219
233
  "malicious_package_score": ["malicious_package"],
220
- "malicious_packages": ["malicious_package"],
234
+ "malicious_packages": ["malicious_package", "package_reputation"],
221
235
  "mcp_config_risk": ["mcp_risk"],
222
236
  "mcp_risk_score": ["mcp_risk"],
223
237
  "mcp_server": ["mcp_risk"],
224
238
  "mcp_server_verified": ["mcp_risk"],
225
239
  "mcp_tool": ["mcp_risk"],
226
- "package_advisory_count": ["malicious_package"],
240
+ "package_advisory_count": ["package_reputation"],
227
241
  "package_check_status": ["malicious_package"],
228
- "package_ecosystems": ["malicious_package"],
242
+ "package_ecosystems": ["package_reputation"],
229
243
  "package_install_detected": ["malicious_package"],
230
- "package_names": ["malicious_package"],
231
- "package_risk_score": ["malicious_package"],
244
+ "package_names": ["package_reputation"],
245
+ "package_risk_score": ["package_reputation"],
232
246
  "packages_checked": ["malicious_package"],
233
247
  "pattern_type": ["action_pattern"],
234
248
  "pii_count": ["pii"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@highflame/policy",
3
- "version": "2.2.40",
3
+ "version": "2.2.41",
4
4
  "engines": {
5
5
  "node": ">=18"
6
6
  },