@highflame/policy 2.2.41 → 2.2.42

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.
@@ -3471,6 +3471,205 @@ when {
3471
3471
  !(["treasury@example.com", "payroll@example.com"].contains(context.action_params.recipient))
3472
3472
  };
3473
3473
  `;
3474
+ const GUARDRAILS_PRIVACY_OUTPUT_PROTECTION_CEDAR = `// =============================================================================
3475
+ // Output Protection — Data Leakage
3476
+ // =============================================================================
3477
+ // Guards what the MODEL SAYS BACK, not what the user sends in.
3478
+ //
3479
+ // Every other guardrails template binds process_response alongside
3480
+ // process_prompt and the tool/file actions, which is right for a threat that
3481
+ // is the same in both directions. Data leakage is not that: the risk is
3482
+ // specific to egress, and a tenant who wants to police only their outputs had
3483
+ // no template to enable. That gap is why one deployed tenant hand-authored a
3484
+ // rule named "Block PII in outputs" and bound it to process_prompt — the only
3485
+ // action that covered responses before the direction split (ADR 0031).
3486
+ //
3487
+ // These rules bind process_response ALONE. That is the point: ADR 0031 created
3488
+ // the distinct trigger precisely so a policy could be scoped to one direction
3489
+ // from its head, and this profile is what that is for. Enabling it cannot
3490
+ // change how prompts are treated.
3491
+ //
3492
+ // Context keys consumed:
3493
+ // - pii_detected: Bool
3494
+ // - pii_count: Long
3495
+ //
3496
+ // Compliance:
3497
+ // - OWASP LLM02 (Sensitive Information Disclosure), OWASP LLM06
3498
+ //
3499
+ // Category: privacy
3500
+ // Namespace: Guardrails
3501
+ // =============================================================================
3502
+
3503
+ @id("privacy.output-block-pii")
3504
+ @name("Block PII in model responses")
3505
+ @description("Blocks process_response when the model's own output contains PII. Scoped to the response direction only — prompts are unaffected.")
3506
+ @severity("high")
3507
+ @tags("category:privacy,threat:data-leak,surface:process-response,detection:rule,owasp:llm02")
3508
+ @reject_message("Response blocked: the model's output contained personal data.")
3509
+ forbid (
3510
+ principal,
3511
+ action == Guardrails::Action::"process_response",
3512
+ resource
3513
+ )
3514
+ when {
3515
+ context has pii_detected && context.pii_detected == true
3516
+ };
3517
+
3518
+ // Bulk disclosure is a separate finding from a single incidental match: a
3519
+ // response carrying many distinct PII items is an exfiltration shape rather
3520
+ // than a mention, and is worth its own severity and message even though the
3521
+ // rule above already blocks it. Kept as a distinct rule so a tenant running
3522
+ // the first in monitor mode can still enforce on bulk.
3523
+ @id("privacy.output-block-bulk-pii")
3524
+ @name("Block bulk PII disclosure in model responses")
3525
+ @description("Blocks process_response when the output carries five or more PII items — a disclosure shape rather than an incidental mention.")
3526
+ @severity("critical")
3527
+ @tags("category:privacy,threat:data-leak,surface:process-response,detection:rule,owasp:llm06")
3528
+ @reject_message("Response blocked: the model's output contained bulk personal data.")
3529
+ forbid (
3530
+ principal,
3531
+ action == Guardrails::Action::"process_response",
3532
+ resource
3533
+ )
3534
+ when {
3535
+ context has pii_count && context.pii_count >= 5
3536
+ };
3537
+ `;
3538
+ const GUARDRAILS_TRUST_SAFETY_OUTPUT_PROTECTION_CEDAR = `// =============================================================================
3539
+ // Output Protection — Harmful Content
3540
+ // =============================================================================
3541
+ // The companion to data_leakage.cedar: what the model says back, judged for
3542
+ // safety rather than for disclosure.
3543
+ //
3544
+ // Same scoping rule and same reason — process_response ALONE (ADR 0031). A
3545
+ // tenant enabling this is saying "hold my model's answers to a standard",
3546
+ // which is a different decision from "police what users send me", and the two
3547
+ // should be separately enableable.
3548
+ //
3549
+ // Thresholds match the org-wide toxicity defaults rather than tightening them.
3550
+ // An output profile that silently enforced stricter limits than the tenant's
3551
+ // existing prompt rules would surprise anyone comparing the two.
3552
+ //
3553
+ // Context keys consumed:
3554
+ // - violence_score: Long (0-100)
3555
+ // - hate_speech_score: Long (0-100)
3556
+ // - sexual_score: Long (0-100)
3557
+ // - injection_score: Long (0-100)
3558
+ //
3559
+ // Compliance:
3560
+ // - OWASP LLM05 (Improper Output Handling), OWASP LLM01
3561
+ //
3562
+ // Category: trust-safety
3563
+ // Namespace: Guardrails
3564
+ // =============================================================================
3565
+
3566
+ @id("trust-safety.output-block-toxic")
3567
+ @name("Block toxic model responses")
3568
+ @description("Blocks process_response when the model's own output scores high for violence or hate speech.")
3569
+ @severity("high")
3570
+ @tags("category:trust-safety,threat:harmful,surface:process-response,detection:ml,owasp:llm05")
3571
+ @reject_message("Response blocked: the model's output was flagged as harmful.")
3572
+ forbid (
3573
+ principal,
3574
+ action == Guardrails::Action::"process_response",
3575
+ resource
3576
+ )
3577
+ when {
3578
+ context has violence_score && context.violence_score >= 70 ||
3579
+ context has hate_speech_score && context.hate_speech_score >= 70
3580
+ };
3581
+
3582
+ @id("trust-safety.output-block-sexual")
3583
+ @name("Block sexual content in model responses")
3584
+ @description("Blocks process_response when the model's own output scores high for sexual content.")
3585
+ @severity("critical")
3586
+ @tags("category:trust-safety,threat:sexual,surface:process-response,detection:ml,owasp:llm05")
3587
+ @reject_message("Response blocked: the model's output was flagged as explicit.")
3588
+ forbid (
3589
+ principal,
3590
+ action == Guardrails::Action::"process_response",
3591
+ resource
3592
+ )
3593
+ when {
3594
+ context has sexual_score && context.sexual_score >= 91
3595
+ };
3596
+ `;
3597
+ const GUARDRAILS_SECURITY_OUTPUT_BLOCK_INJECTION_CARRIED_BACK_CEDAR = `// =============================================================================
3598
+ // Output Protection — Injection Carried Back
3599
+ // =============================================================================
3600
+ // Split from harmful_content.cedar because its category is \`security\`, not
3601
+ // \`trust-safety\`, and a template wrapper may only carry rules of its own
3602
+ // category (HFP-LINT-TMPL-006). The split is the honest outcome: this is a
3603
+ // different threat with a different owner, not a toxicity variant.
3604
+ //
3605
+ // Same scoping rule as the rest of this profile — process_response ALONE.
3606
+ //
3607
+ // Context keys consumed:
3608
+ // - injection_score: Long (0-100)
3609
+ //
3610
+ // Compliance:
3611
+ // - OWASP LLM05 (Improper Output Handling)
3612
+ //
3613
+ // Category: security
3614
+ // Namespace: Guardrails
3615
+ // =============================================================================
3616
+
3617
+ // Injection scored on an OUTPUT is a different finding from injection scored on
3618
+ // a prompt. On the way in it is a user attacking the model; on the way out it
3619
+ // is content the model is handing to the caller — a downstream agent, a
3620
+ // renderer, or another tool — which is OWASP LLM05's improper output handling.
3621
+ // Worth its own rule so the two can be tuned and reasoned about separately.
3622
+ @id("security.output-block-injection-carried-back")
3623
+ @name("Block injection payloads carried back in model responses")
3624
+ @description("Blocks process_response when the output itself scores as an injection payload — content the caller may execute or forward.")
3625
+ @severity("high")
3626
+ @tags("category:security,threat:injection,surface:process-response,detection:ml,owasp:llm05")
3627
+ @reject_message("Response blocked: the model's output contained an injection payload.")
3628
+ forbid (
3629
+ principal,
3630
+ action == Guardrails::Action::"process_response",
3631
+ resource
3632
+ )
3633
+ when {
3634
+ context has injection_score && context.injection_score >= 80
3635
+ };
3636
+ `;
3637
+ const GUARDRAILS_DATA_PROTECTION_OUTPUT_BLOCK_SECRETS_CEDAR = `// =============================================================================
3638
+ // Output Protection — Credentials
3639
+ // =============================================================================
3640
+ // Split from data_leakage.cedar: secrets are category \`data-protection\`, PII is
3641
+ // \`privacy\`, and a template wrapper may only carry rules of its own category
3642
+ // (HFP-LINT-TMPL-006). Relabelling one to fit the other would put a wrong
3643
+ // category on the wire, where it drives signals[] and the severity rollup.
3644
+ //
3645
+ // Same scoping rule as the rest of this profile — process_response ALONE, so a
3646
+ // tenant can police what the model says without touching what users send.
3647
+ //
3648
+ // Context keys consumed:
3649
+ // - secrets_detected: Bool
3650
+ //
3651
+ // Compliance:
3652
+ // - OWASP LLM02 (Sensitive Information Disclosure)
3653
+ //
3654
+ // Category: data-protection
3655
+ // Namespace: Guardrails
3656
+ // =============================================================================
3657
+
3658
+ @id("data-protection.output-block-secrets")
3659
+ @name("Block secrets in model responses")
3660
+ @description("Blocks process_response when the model's own output contains credentials, API keys or tokens — the canonical way a leaked secret reaches a caller.")
3661
+ @severity("critical")
3662
+ @tags("category:data-protection,threat:secrets,surface:process-response,detection:rule,owasp:llm02")
3663
+ @reject_message("Response blocked: the model's output contained credentials.")
3664
+ forbid (
3665
+ principal,
3666
+ action == Guardrails::Action::"process_response",
3667
+ resource
3668
+ )
3669
+ when {
3670
+ context has secrets_detected && context.secrets_detected == true
3671
+ };
3672
+ `;
3474
3673
  // =============================================================================
3475
3674
  // CATEGORIES
3476
3675
  // =============================================================================
@@ -3810,6 +4009,42 @@ export const GUARDRAILS_TEMPLATES = [
3810
4009
  severity: 'high',
3811
4010
  tags: ['category:agent-security', 'surface:call-tool', 'aarm:r3', 'posture:deny-default'],
3812
4011
  },
4012
+ {
4013
+ id: 'privacy.output-protection',
4014
+ name: 'Output Protection — Data Leakage',
4015
+ description: 'Block PII, secrets and bulk disclosure in the model\'s own responses. Scoped to the response direction only, so prompts are unaffected.',
4016
+ category: 'privacy',
4017
+ cedarText: GUARDRAILS_PRIVACY_OUTPUT_PROTECTION_CEDAR,
4018
+ severity: 'critical',
4019
+ tags: ['category:privacy', 'threat:data-leak', 'surface:process-response', 'detection:rule'],
4020
+ },
4021
+ {
4022
+ id: 'trust-safety.output-protection',
4023
+ name: 'Output Protection — Harmful Content',
4024
+ description: 'Hold the model\'s own responses to a safety standard: toxicity, explicit content, and injection payloads carried back to the caller. Response direction only.',
4025
+ category: 'trust-safety',
4026
+ cedarText: GUARDRAILS_TRUST_SAFETY_OUTPUT_PROTECTION_CEDAR,
4027
+ severity: 'critical',
4028
+ tags: ['category:trust-safety', 'threat:harmful', 'surface:process-response', 'detection:ml'],
4029
+ },
4030
+ {
4031
+ id: 'security.output-block-injection-carried-back',
4032
+ name: 'Output Protection — Injection Carried Back',
4033
+ description: 'Block responses that themselves score as an injection payload — content a caller, downstream agent or renderer may execute. Response direction only.',
4034
+ category: 'security',
4035
+ cedarText: GUARDRAILS_SECURITY_OUTPUT_BLOCK_INJECTION_CARRIED_BACK_CEDAR,
4036
+ severity: 'high',
4037
+ tags: ['category:security', 'threat:injection', 'surface:process-response', 'detection:ml'],
4038
+ },
4039
+ {
4040
+ id: 'data-protection.output-block-secrets',
4041
+ name: 'Output Protection — Credentials',
4042
+ description: 'Block responses whose own content contains credentials, API keys or tokens — the canonical way a leaked secret reaches a caller. Response direction only.',
4043
+ category: 'data-protection',
4044
+ cedarText: GUARDRAILS_DATA_PROTECTION_OUTPUT_BLOCK_SECRETS_CEDAR,
4045
+ severity: 'critical',
4046
+ tags: ['category:data-protection', 'threat:secrets', 'surface:process-response', 'detection:rule'],
4047
+ },
3813
4048
  ];
3814
4049
  // =============================================================================
3815
4050
  // TEMPLATES METADATA
@@ -3877,7 +4112,10 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
3877
4112
  "category": "organization",
3878
4113
  "file": "defaults/baseline.cedar",
3879
4114
  "severity": "low",
3880
- "tags": ["category:organization", "posture:permit-default"],
4115
+ "tags": [
4116
+ "category:organization",
4117
+ "posture:permit-default"
4118
+ ],
3881
4119
  "is_active": true
3882
4120
  }
3883
4121
  ],
@@ -3889,7 +4127,10 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
3889
4127
  "category": "organization",
3890
4128
  "file": "defaults/baseline.cedar",
3891
4129
  "severity": "low",
3892
- "tags": ["category:organization", "posture:permit-default"],
4130
+ "tags": [
4131
+ "category:organization",
4132
+ "posture:permit-default"
4133
+ ],
3893
4134
  "auto_deploy": true
3894
4135
  },
3895
4136
  {
@@ -3899,7 +4140,11 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
3899
4140
  "category": "data-protection",
3900
4141
  "file": "defaults/secrets.cedar",
3901
4142
  "severity": "critical",
3902
- "tags": ["category:data-protection", "threat:secrets", "owasp:llm06"]
4143
+ "tags": [
4144
+ "category:data-protection",
4145
+ "threat:secrets",
4146
+ "owasp:llm06"
4147
+ ]
3903
4148
  },
3904
4149
  {
3905
4150
  "id": "security.injection",
@@ -4043,7 +4288,11 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
4043
4288
  "category": "agent-identity",
4044
4289
  "file": "defaults/agent_identity.cedar",
4045
4290
  "severity": "critical",
4046
- "tags": ["category:agent-identity", "scope:per-agent", "owasp:llm01"]
4291
+ "tags": [
4292
+ "category:agent-identity",
4293
+ "scope:per-agent",
4294
+ "owasp:llm01"
4295
+ ]
4047
4296
  },
4048
4297
  {
4049
4298
  "id": "tools.mcp-tool-permissions",
@@ -4052,7 +4301,11 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
4052
4301
  "category": "tools",
4053
4302
  "file": "mcp_tool_permissions.cedar",
4054
4303
  "severity": "critical",
4055
- "tags": ["category:tools", "threat:supply-chain", "posture:deny-default"]
4304
+ "tags": [
4305
+ "category:tools",
4306
+ "threat:supply-chain",
4307
+ "posture:deny-default"
4308
+ ]
4056
4309
  },
4057
4310
  {
4058
4311
  "id": "tools.mcp-server-allowlist",
@@ -4102,7 +4355,10 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
4102
4355
  "category": "data-protection",
4103
4356
  "file": "profiles/code_agent/security.cedar",
4104
4357
  "severity": "critical",
4105
- "tags": ["category:data-protection", "threat:secrets"]
4358
+ "tags": [
4359
+ "category:data-protection",
4360
+ "threat:secrets"
4361
+ ]
4106
4362
  },
4107
4363
  {
4108
4364
  "id": "security.code-agent-encoding",
@@ -4124,7 +4380,11 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
4124
4380
  "category": "security",
4125
4381
  "file": "profiles/code_agent/path_security.cedar",
4126
4382
  "severity": "critical",
4127
- "tags": ["category:security", "threat:secrets", "threat:path-traversal"]
4383
+ "tags": [
4384
+ "category:security",
4385
+ "threat:secrets",
4386
+ "threat:path-traversal"
4387
+ ]
4128
4388
  },
4129
4389
  {
4130
4390
  "id": "agent-security.code-agent",
@@ -4163,7 +4423,11 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
4163
4423
  "category": "data-protection",
4164
4424
  "file": "profiles/data_pipeline/data_protection.cedar",
4165
4425
  "severity": "critical",
4166
- "tags": ["category:data-protection", "threat:secrets", "owasp:llm06"]
4426
+ "tags": [
4427
+ "category:data-protection",
4428
+ "threat:secrets",
4429
+ "owasp:llm06"
4430
+ ]
4167
4431
  },
4168
4432
  {
4169
4433
  "id": "security.data-pipeline-block-injection",
@@ -4172,7 +4436,11 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
4172
4436
  "category": "security",
4173
4437
  "file": "profiles/data_pipeline/security.cedar",
4174
4438
  "severity": "high",
4175
- "tags": ["category:security", "threat:injection", "owasp:llm01"]
4439
+ "tags": [
4440
+ "category:security",
4441
+ "threat:injection",
4442
+ "owasp:llm01"
4443
+ ]
4176
4444
  },
4177
4445
  {
4178
4446
  "id": "agent-security.data-pipeline",
@@ -4181,7 +4449,10 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
4181
4449
  "category": "agent-security",
4182
4450
  "file": "profiles/data_pipeline/agentic_security.cedar",
4183
4451
  "severity": "critical",
4184
- "tags": ["category:agent-security", "threat:exfiltration"]
4452
+ "tags": [
4453
+ "category:agent-security",
4454
+ "threat:exfiltration"
4455
+ ]
4185
4456
  },
4186
4457
  {
4187
4458
  "id": "agent-identity.multi-agent-trust",
@@ -4290,7 +4561,11 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
4290
4561
  "category": "data-protection",
4291
4562
  "file": "profiles/advanced_detection/secrets.cedar",
4292
4563
  "severity": "critical",
4293
- "tags": ["category:data-protection", "threat:secrets", "owasp:llm06"]
4564
+ "tags": [
4565
+ "category:data-protection",
4566
+ "threat:secrets",
4567
+ "owasp:llm06"
4568
+ ]
4294
4569
  },
4295
4570
  {
4296
4571
  "id": "privacy.advanced-pii",
@@ -4320,6 +4595,62 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
4320
4595
  "aarm:r3",
4321
4596
  "posture:deny-default"
4322
4597
  ]
4598
+ },
4599
+ {
4600
+ "id": "privacy.output-protection",
4601
+ "name": "Output Protection — Data Leakage",
4602
+ "description": "Block PII, secrets and bulk disclosure in the model's own responses. Scoped to the response direction only, so prompts are unaffected.",
4603
+ "category": "privacy",
4604
+ "file": "profiles/output_protection/data_leakage.cedar",
4605
+ "severity": "critical",
4606
+ "tags": [
4607
+ "category:privacy",
4608
+ "threat:data-leak",
4609
+ "surface:process-response",
4610
+ "detection:rule"
4611
+ ]
4612
+ },
4613
+ {
4614
+ "id": "trust-safety.output-protection",
4615
+ "name": "Output Protection — Harmful Content",
4616
+ "description": "Hold the model's own responses to a safety standard: toxicity, explicit content, and injection payloads carried back to the caller. Response direction only.",
4617
+ "category": "trust-safety",
4618
+ "file": "profiles/output_protection/harmful_content.cedar",
4619
+ "severity": "critical",
4620
+ "tags": [
4621
+ "category:trust-safety",
4622
+ "threat:harmful",
4623
+ "surface:process-response",
4624
+ "detection:ml"
4625
+ ]
4626
+ },
4627
+ {
4628
+ "id": "security.output-block-injection-carried-back",
4629
+ "name": "Output Protection — Injection Carried Back",
4630
+ "description": "Block responses that themselves score as an injection payload — content a caller, downstream agent or renderer may execute. Response direction only.",
4631
+ "category": "security",
4632
+ "file": "profiles/output_protection/injection_carried_back.cedar",
4633
+ "severity": "high",
4634
+ "tags": [
4635
+ "category:security",
4636
+ "threat:injection",
4637
+ "surface:process-response",
4638
+ "detection:ml"
4639
+ ]
4640
+ },
4641
+ {
4642
+ "id": "data-protection.output-block-secrets",
4643
+ "name": "Output Protection — Credentials",
4644
+ "description": "Block responses whose own content contains credentials, API keys or tokens — the canonical way a leaked secret reaches a caller. Response direction only.",
4645
+ "category": "data-protection",
4646
+ "file": "profiles/output_protection/credentials.cedar",
4647
+ "severity": "critical",
4648
+ "tags": [
4649
+ "category:data-protection",
4650
+ "threat:secrets",
4651
+ "surface:process-response",
4652
+ "detection:rule"
4653
+ ]
4323
4654
  }
4324
4655
  ]
4325
4656
  }
@@ -5,6 +5,8 @@
5
5
  * Overwatch Cedar schema and are used at policy evaluation time.
6
6
  */
7
7
  export declare const OverwatchContextKey: {
8
+ readonly BudgetExceeded: "budget_exceeded";
9
+ readonly BudgetRemainingPct: "budget_remaining_pct";
8
10
  readonly Content: "content";
9
11
  readonly CrimeScore: "crime_score";
10
12
  readonly Cwd: "cwd";
@@ -99,4 +101,4 @@ export type OverwatchContextKey = (typeof OverwatchContextKey)[keyof typeof Over
99
101
  * The full set of authorable context attribute keys for Overwatch.
100
102
  * Iterate this to enumerate the authorable surface (cockpit, conformance).
101
103
  */
102
- export declare const OverwatchContextKeys: readonly ["content", "crime_score", "cwd", "detected_threats", "event", "exec_target_paths", "exit_code", "hate_speech_score", "highest_severity", "indirect_injection_score", "injection_deep_context_score", "injection_pulse_score", "injection_score", "invisible_chars_detected", "invisible_chars_score", "jailbreak_deep_context_score", "jailbreak_pulse_score", "jailbreak_score", "loop_count", "loop_detected", "loop_tool", "malicious_package_detected", "malicious_package_score", "malicious_packages", "max_threat_severity", "mcp_config_risk", "mcp_risk_score", "mcp_server", "mcp_server_verified", "mcp_tool", "package_advisory_count", "package_check_status", "package_ecosystems", "package_install_detected", "package_names", "package_risk_score", "packages_checked", "path", "pattern_type", "pii_count", "pii_detected", "pii_score", "pii_types", "privilege_scope", "profanity_score", "prompt_text", "read_target_paths", "resolved_target_paths", "response_content", "role", "rug_pull_detected", "rug_pull_score", "secret_count", "secret_types", "secrets_detected", "sequence_risk", "session_command_injection", "session_cumulative_risk_score", "session_injection_detected", "session_max_command_injection_score", "session_max_injection_score", "session_max_jailbreak_score", "session_max_pii_score", "session_max_secret_score", "session_pii_detected", "session_pii_types", "session_secret_types", "session_secrets_detected", "session_threat_turns", "sexual_score", "source", "suspicious_pattern", "threat_categories", "threat_count", "tool_category", "tool_is_builtin", "tool_is_sensitive", "tool_name", "tool_operation_classes", "tool_poisoning_detected", "tool_poisoning_score", "tool_risk_score", "unresolved_target", "user_email", "violence_score", "weapons_score", "workspace_root", "write_target_paths"];
104
+ export declare const OverwatchContextKeys: readonly ["budget_exceeded", "budget_remaining_pct", "content", "crime_score", "cwd", "detected_threats", "event", "exec_target_paths", "exit_code", "hate_speech_score", "highest_severity", "indirect_injection_score", "injection_deep_context_score", "injection_pulse_score", "injection_score", "invisible_chars_detected", "invisible_chars_score", "jailbreak_deep_context_score", "jailbreak_pulse_score", "jailbreak_score", "loop_count", "loop_detected", "loop_tool", "malicious_package_detected", "malicious_package_score", "malicious_packages", "max_threat_severity", "mcp_config_risk", "mcp_risk_score", "mcp_server", "mcp_server_verified", "mcp_tool", "package_advisory_count", "package_check_status", "package_ecosystems", "package_install_detected", "package_names", "package_risk_score", "packages_checked", "path", "pattern_type", "pii_count", "pii_detected", "pii_score", "pii_types", "privilege_scope", "profanity_score", "prompt_text", "read_target_paths", "resolved_target_paths", "response_content", "role", "rug_pull_detected", "rug_pull_score", "secret_count", "secret_types", "secrets_detected", "sequence_risk", "session_command_injection", "session_cumulative_risk_score", "session_injection_detected", "session_max_command_injection_score", "session_max_injection_score", "session_max_jailbreak_score", "session_max_pii_score", "session_max_secret_score", "session_pii_detected", "session_pii_types", "session_secret_types", "session_secrets_detected", "session_threat_turns", "sexual_score", "source", "suspicious_pattern", "threat_categories", "threat_count", "tool_category", "tool_is_builtin", "tool_is_sensitive", "tool_name", "tool_operation_classes", "tool_poisoning_detected", "tool_poisoning_score", "tool_risk_score", "unresolved_target", "user_email", "violence_score", "weapons_score", "workspace_root", "write_target_paths"];
@@ -7,6 +7,8 @@
7
7
  * Overwatch Cedar schema and are used at policy evaluation time.
8
8
  */
9
9
  export const OverwatchContextKey = {
10
+ BudgetExceeded: 'budget_exceeded',
11
+ BudgetRemainingPct: 'budget_remaining_pct',
10
12
  Content: 'content',
11
13
  CrimeScore: 'crime_score',
12
14
  Cwd: 'cwd',
@@ -101,6 +103,8 @@ export const OverwatchContextKey = {
101
103
  * Iterate this to enumerate the authorable surface (cockpit, conformance).
102
104
  */
103
105
  export const OverwatchContextKeys = [
106
+ OverwatchContextKey.BudgetExceeded,
107
+ OverwatchContextKey.BudgetRemainingPct,
104
108
  OverwatchContextKey.Content,
105
109
  OverwatchContextKey.CrimeScore,
106
110
  OverwatchContextKey.Cwd,
@@ -84,6 +84,20 @@ export const OVERWATCH_DETECTORS = [
84
84
  defendsAgainst: ["unbounded_consumption", "excessive_agency"],
85
85
  exampleAttacks: [{ title: "Runaway tool loop", vulnerabilityId: "unbounded_consumption", snippet: "(agentic) the same shell command is invoked 30x in a row", expectedSignal: { "loop_detected": true } }],
86
86
  },
87
+ {
88
+ id: "budget_checker",
89
+ displayName: "Budget Checker",
90
+ category: "agent_behavior",
91
+ stability: "stable",
92
+ tier: "fast",
93
+ inhouse: false,
94
+ model: null,
95
+ latencyP50Ms: null,
96
+ emits: [{ name: "budget_remaining_pct", type: "Long", modifiable: false, semantic: "severity_0_100", description: "Remaining session token budget (0-100). Default-filled to 100 when no metering ran." }, { name: "budget_exceeded", type: "Bool", modifiable: false, semantic: "boolean_flag", description: "True iff the session token budget has been exceeded. Absent when no metering ran, so policies must guard with `context has`." }],
97
+ supportedModes: ["enforce", "monitor", "alert"],
98
+ defendsAgainst: ["unbounded_consumption"],
99
+ exampleAttacks: [{ title: "Runaway autonomous session", vulnerabilityId: "unbounded_consumption", snippet: "(agentic) one prompt drives hundreds of tool calls, burning the session's token budget unattended", expectedSignal: { "budget_exceeded": true } }],
100
+ },
87
101
  {
88
102
  id: "tool_risk",
89
103
  displayName: "Tool Risk",
@@ -214,6 +228,8 @@ export const OVERWATCH_DETECTORS = [
214
228
  // Semantic field → contributing detector ids (producesAttrs + normalizationAliases,
215
229
  // resolved at codegen). Used by the client field→detector resolver — no Shield round-trip.
216
230
  export const OVERWATCH_FIELD_TO_DETECTORS = {
231
+ "budget_exceeded": ["budget_checker"],
232
+ "budget_remaining_pct": ["budget_checker"],
217
233
  "crime_score": ["toxicity"],
218
234
  "exec_target_paths": ["bash_ast_classifier"],
219
235
  "hate_speech_score": ["toxicity"],