@highflame/policy 2.2.41 → 2.2.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_schemas/agent_ops/context.json +0 -42
- package/_schemas/agent_ops/schema.cedarschema +44 -6
- package/_schemas/ai_gateway/context.json +300 -0
- package/_schemas/ai_gateway/schema.cedarschema +107 -3
- package/_schemas/guardrails/context.json +0 -36
- package/_schemas/guardrails/detectors.json +0 -1
- package/_schemas/guardrails/schema.cedarschema +43 -5
- package/_schemas/guardrails/templates/dual_attribution.cedar +110 -0
- package/_schemas/guardrails/templates/multi_turn_trajectory.cedar +116 -0
- package/_schemas/guardrails/templates/profiles/output_protection/credentials.cedar +35 -0
- package/_schemas/guardrails/templates/profiles/output_protection/data_leakage.cedar +63 -0
- package/_schemas/guardrails/templates/profiles/output_protection/harmful_content.cedar +58 -0
- package/_schemas/guardrails/templates/profiles/output_protection/injection_carried_back.cedar +39 -0
- package/_schemas/guardrails/templates/session_risk_accumulation.cedar +103 -0
- package/_schemas/guardrails/templates/templates.json +157 -11
- package/_schemas/overwatch/context.json +65 -0
- package/_schemas/overwatch/detectors.json +47 -0
- package/_schemas/overwatch/schema.cedarschema +90 -0
- package/dist/agent_ops-context.gen.d.ts +1 -2
- package/dist/agent_ops-context.gen.js +0 -2
- package/dist/ai_gateway-entities.gen.js +5 -1
- package/dist/guardrails-context.gen.d.ts +1 -2
- package/dist/guardrails-context.gen.js +0 -2
- package/dist/guardrails-defaults.gen.js +751 -11
- package/dist/overwatch-context.gen.d.ts +3 -1
- package/dist/overwatch-context.gen.js +4 -0
- package/dist/overwatch-detectors.gen.js +16 -0
- package/dist/parser.d.ts +2 -1
- package/dist/parser.js +124 -37
- package/dist/service-schemas.gen.d.ts +4 -4
- package/dist/service-schemas.gen.js +354 -32
- package/package.json +1 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Session Risk Accumulation
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Blocks the privileged action a probing conversation is working toward. A
|
|
5
|
+
// patient attacker expects some turns to be refused; what they want is one
|
|
6
|
+
// tool call at the end — send the email, move the money, read the file. So
|
|
7
|
+
// the useful question at a tool call is not "is this call suspicious?" but
|
|
8
|
+
// "what has this conversation been doing up to now?"
|
|
9
|
+
//
|
|
10
|
+
// Shield accumulates that history on the session and projects it three ways,
|
|
11
|
+
// because attackers come in three shapes:
|
|
12
|
+
// - session_max_* — a high-water mark that never decays. "Did this session
|
|
13
|
+
// EVER cross a line?" Catches the attacker who probes hard, is refused,
|
|
14
|
+
// goes quiet, then calmly asks for the tool.
|
|
15
|
+
// - session_cumulative_risk_score — an uncapped running sum. "How much
|
|
16
|
+
// total pressure has this session applied?" Catches death by a thousand
|
|
17
|
+
// cuts, where no single turn is alarming.
|
|
18
|
+
// - session_threat_turns — a count of turns that tripped a detector. "Is
|
|
19
|
+
// this sustained, or a one-off?" Separates probing from a false positive.
|
|
20
|
+
//
|
|
21
|
+
// Requires a stable session_id on every request, prompts AND tool calls. The
|
|
22
|
+
// tool call must ride the same session as the conversation, or it reads 0.
|
|
23
|
+
//
|
|
24
|
+
// Detection layers:
|
|
25
|
+
// - session (aggregate over detection history, always available)
|
|
26
|
+
// - tool_validator (tool_is_sensitive, always available)
|
|
27
|
+
//
|
|
28
|
+
// Context keys consumed:
|
|
29
|
+
// - session_max_injection_score, session_max_jailbreak_score: Long (0-100)
|
|
30
|
+
// - session_cumulative_risk_score: Long — uncapped sum
|
|
31
|
+
// - session_threat_turns: Long — count
|
|
32
|
+
// - tool_is_sensitive: Bool
|
|
33
|
+
//
|
|
34
|
+
// Compliance:
|
|
35
|
+
// - OWASP LLM01, OWASP LLM06, OWASP ASI01, OWASP ASI04
|
|
36
|
+
//
|
|
37
|
+
// Category: agent-security
|
|
38
|
+
// Namespace: Guardrails
|
|
39
|
+
// =============================================================================
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Section 1: A session that ever crossed the line does not get to act
|
|
43
|
+
// The conversation is what scored high; the tool call is what gets stopped.
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
@id("agent-security.block-tool-after-injection-in-session")
|
|
47
|
+
@name("Block tools after an injection or jailbreak in the session")
|
|
48
|
+
@description("Blocks call_tool when session_max_injection_score >= 60 or session_max_jailbreak_score >= 60, because an earlier turn attempted injection or jailbreak.")
|
|
49
|
+
@severity("critical")
|
|
50
|
+
@tags("category:agent-security,threat:escalation,detection:aggregate,surface:call-tool,scope:multi-turn,owasp:llm01")
|
|
51
|
+
@reject_message("Tool execution blocked: an earlier turn in this session attempted prompt injection or jailbreak. Start a new session to use tools.")
|
|
52
|
+
forbid (
|
|
53
|
+
principal,
|
|
54
|
+
action == Guardrails::Action::"call_tool",
|
|
55
|
+
resource
|
|
56
|
+
)
|
|
57
|
+
when {
|
|
58
|
+
(context has session_max_injection_score && context.session_max_injection_score >= 60) ||
|
|
59
|
+
(context has session_max_jailbreak_score && context.session_max_jailbreak_score >= 60)
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Section 2: Accumulated pressure gates sensitive tools
|
|
64
|
+
// No single turn set a max, but the session as a whole kept pushing.
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
@id("agent-security.block-sensitive-tool-on-session-risk")
|
|
68
|
+
@name("Block sensitive tools once the session has accumulated risk")
|
|
69
|
+
@description("Blocks call_tool when session_cumulative_risk_score >= 151 and tool_is_sensitive is true.")
|
|
70
|
+
@severity("high")
|
|
71
|
+
@tags("category:agent-security,threat:escalation,detection:aggregate,surface:call-tool,scope:multi-turn,owasp:asi01")
|
|
72
|
+
@reject_message("Tool execution blocked: this session has accumulated significant risk across earlier turns. Sensitive tools are withheld for the remainder of the session.")
|
|
73
|
+
forbid (
|
|
74
|
+
principal,
|
|
75
|
+
action == Guardrails::Action::"call_tool",
|
|
76
|
+
resource
|
|
77
|
+
)
|
|
78
|
+
when {
|
|
79
|
+
context has session_cumulative_risk_score &&
|
|
80
|
+
context.session_cumulative_risk_score >= 151 &&
|
|
81
|
+
context has tool_is_sensitive && context.tool_is_sensitive == true
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
// Section 3: Sustained probing, independent of any single score
|
|
86
|
+
// Two is the smallest bar that separates repeated from one-off. Long-running
|
|
87
|
+
// agent sessions accumulate turns faster and warrant a higher bar.
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
@id("agent-security.block-tool-on-repeated-threat-turns")
|
|
91
|
+
@name("Block tool use in a session with repeated threat turns")
|
|
92
|
+
@description("Blocks call_tool when session_threat_turns >= 2, because more than one turn in this session tripped a detector.")
|
|
93
|
+
@severity("high")
|
|
94
|
+
@tags("category:agent-security,threat:escalation,detection:aggregate,surface:call-tool,scope:multi-turn,owasp:asi04")
|
|
95
|
+
@reject_message("Tool execution blocked: more than one turn in this session tripped a detector. This is sustained probing, not a one-off false positive.")
|
|
96
|
+
forbid (
|
|
97
|
+
principal,
|
|
98
|
+
action == Guardrails::Action::"call_tool",
|
|
99
|
+
resource
|
|
100
|
+
)
|
|
101
|
+
when {
|
|
102
|
+
context has session_threat_turns && context.session_threat_turns >= 2
|
|
103
|
+
};
|
|
@@ -60,7 +60,10 @@
|
|
|
60
60
|
"category": "organization",
|
|
61
61
|
"file": "defaults/baseline.cedar",
|
|
62
62
|
"severity": "low",
|
|
63
|
-
"tags": [
|
|
63
|
+
"tags": [
|
|
64
|
+
"category:organization",
|
|
65
|
+
"posture:permit-default"
|
|
66
|
+
],
|
|
64
67
|
"is_active": true
|
|
65
68
|
}
|
|
66
69
|
],
|
|
@@ -72,7 +75,10 @@
|
|
|
72
75
|
"category": "organization",
|
|
73
76
|
"file": "defaults/baseline.cedar",
|
|
74
77
|
"severity": "low",
|
|
75
|
-
"tags": [
|
|
78
|
+
"tags": [
|
|
79
|
+
"category:organization",
|
|
80
|
+
"posture:permit-default"
|
|
81
|
+
],
|
|
76
82
|
"auto_deploy": true
|
|
77
83
|
},
|
|
78
84
|
{
|
|
@@ -82,7 +88,11 @@
|
|
|
82
88
|
"category": "data-protection",
|
|
83
89
|
"file": "defaults/secrets.cedar",
|
|
84
90
|
"severity": "critical",
|
|
85
|
-
"tags": [
|
|
91
|
+
"tags": [
|
|
92
|
+
"category:data-protection",
|
|
93
|
+
"threat:secrets",
|
|
94
|
+
"owasp:llm06"
|
|
95
|
+
]
|
|
86
96
|
},
|
|
87
97
|
{
|
|
88
98
|
"id": "security.injection",
|
|
@@ -226,7 +236,11 @@
|
|
|
226
236
|
"category": "agent-identity",
|
|
227
237
|
"file": "defaults/agent_identity.cedar",
|
|
228
238
|
"severity": "critical",
|
|
229
|
-
"tags": [
|
|
239
|
+
"tags": [
|
|
240
|
+
"category:agent-identity",
|
|
241
|
+
"scope:per-agent",
|
|
242
|
+
"owasp:llm01"
|
|
243
|
+
]
|
|
230
244
|
},
|
|
231
245
|
{
|
|
232
246
|
"id": "tools.mcp-tool-permissions",
|
|
@@ -235,7 +249,11 @@
|
|
|
235
249
|
"category": "tools",
|
|
236
250
|
"file": "mcp_tool_permissions.cedar",
|
|
237
251
|
"severity": "critical",
|
|
238
|
-
"tags": [
|
|
252
|
+
"tags": [
|
|
253
|
+
"category:tools",
|
|
254
|
+
"threat:supply-chain",
|
|
255
|
+
"posture:deny-default"
|
|
256
|
+
]
|
|
239
257
|
},
|
|
240
258
|
{
|
|
241
259
|
"id": "tools.mcp-server-allowlist",
|
|
@@ -285,7 +303,10 @@
|
|
|
285
303
|
"category": "data-protection",
|
|
286
304
|
"file": "profiles/code_agent/security.cedar",
|
|
287
305
|
"severity": "critical",
|
|
288
|
-
"tags": [
|
|
306
|
+
"tags": [
|
|
307
|
+
"category:data-protection",
|
|
308
|
+
"threat:secrets"
|
|
309
|
+
]
|
|
289
310
|
},
|
|
290
311
|
{
|
|
291
312
|
"id": "security.code-agent-encoding",
|
|
@@ -307,7 +328,11 @@
|
|
|
307
328
|
"category": "security",
|
|
308
329
|
"file": "profiles/code_agent/path_security.cedar",
|
|
309
330
|
"severity": "critical",
|
|
310
|
-
"tags": [
|
|
331
|
+
"tags": [
|
|
332
|
+
"category:security",
|
|
333
|
+
"threat:secrets",
|
|
334
|
+
"threat:path-traversal"
|
|
335
|
+
]
|
|
311
336
|
},
|
|
312
337
|
{
|
|
313
338
|
"id": "agent-security.code-agent",
|
|
@@ -346,7 +371,11 @@
|
|
|
346
371
|
"category": "data-protection",
|
|
347
372
|
"file": "profiles/data_pipeline/data_protection.cedar",
|
|
348
373
|
"severity": "critical",
|
|
349
|
-
"tags": [
|
|
374
|
+
"tags": [
|
|
375
|
+
"category:data-protection",
|
|
376
|
+
"threat:secrets",
|
|
377
|
+
"owasp:llm06"
|
|
378
|
+
]
|
|
350
379
|
},
|
|
351
380
|
{
|
|
352
381
|
"id": "security.data-pipeline-block-injection",
|
|
@@ -355,7 +384,11 @@
|
|
|
355
384
|
"category": "security",
|
|
356
385
|
"file": "profiles/data_pipeline/security.cedar",
|
|
357
386
|
"severity": "high",
|
|
358
|
-
"tags": [
|
|
387
|
+
"tags": [
|
|
388
|
+
"category:security",
|
|
389
|
+
"threat:injection",
|
|
390
|
+
"owasp:llm01"
|
|
391
|
+
]
|
|
359
392
|
},
|
|
360
393
|
{
|
|
361
394
|
"id": "agent-security.data-pipeline",
|
|
@@ -364,7 +397,10 @@
|
|
|
364
397
|
"category": "agent-security",
|
|
365
398
|
"file": "profiles/data_pipeline/agentic_security.cedar",
|
|
366
399
|
"severity": "critical",
|
|
367
|
-
"tags": [
|
|
400
|
+
"tags": [
|
|
401
|
+
"category:agent-security",
|
|
402
|
+
"threat:exfiltration"
|
|
403
|
+
]
|
|
368
404
|
},
|
|
369
405
|
{
|
|
370
406
|
"id": "agent-identity.multi-agent-trust",
|
|
@@ -473,7 +509,11 @@
|
|
|
473
509
|
"category": "data-protection",
|
|
474
510
|
"file": "profiles/advanced_detection/secrets.cedar",
|
|
475
511
|
"severity": "critical",
|
|
476
|
-
"tags": [
|
|
512
|
+
"tags": [
|
|
513
|
+
"category:data-protection",
|
|
514
|
+
"threat:secrets",
|
|
515
|
+
"owasp:llm06"
|
|
516
|
+
]
|
|
477
517
|
},
|
|
478
518
|
{
|
|
479
519
|
"id": "privacy.advanced-pii",
|
|
@@ -503,6 +543,112 @@
|
|
|
503
543
|
"aarm:r3",
|
|
504
544
|
"posture:deny-default"
|
|
505
545
|
]
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
"id": "privacy.output-protection",
|
|
549
|
+
"name": "Output Protection — Data Leakage",
|
|
550
|
+
"description": "Block PII, secrets and bulk disclosure in the model's own responses. Scoped to the response direction only, so prompts are unaffected.",
|
|
551
|
+
"category": "privacy",
|
|
552
|
+
"file": "profiles/output_protection/data_leakage.cedar",
|
|
553
|
+
"severity": "critical",
|
|
554
|
+
"tags": [
|
|
555
|
+
"category:privacy",
|
|
556
|
+
"threat:data-leak",
|
|
557
|
+
"surface:process-response",
|
|
558
|
+
"detection:rule"
|
|
559
|
+
]
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
"id": "trust-safety.output-protection",
|
|
563
|
+
"name": "Output Protection — Harmful Content",
|
|
564
|
+
"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.",
|
|
565
|
+
"category": "trust-safety",
|
|
566
|
+
"file": "profiles/output_protection/harmful_content.cedar",
|
|
567
|
+
"severity": "critical",
|
|
568
|
+
"tags": [
|
|
569
|
+
"category:trust-safety",
|
|
570
|
+
"threat:harmful",
|
|
571
|
+
"surface:process-response",
|
|
572
|
+
"detection:ml"
|
|
573
|
+
]
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
"id": "security.output-block-injection-carried-back",
|
|
577
|
+
"name": "Output Protection — Injection Carried Back",
|
|
578
|
+
"description": "Block responses that themselves score as an injection payload — content a caller, downstream agent or renderer may execute. Response direction only.",
|
|
579
|
+
"category": "security",
|
|
580
|
+
"file": "profiles/output_protection/injection_carried_back.cedar",
|
|
581
|
+
"severity": "high",
|
|
582
|
+
"tags": [
|
|
583
|
+
"category:security",
|
|
584
|
+
"threat:injection",
|
|
585
|
+
"surface:process-response",
|
|
586
|
+
"detection:ml"
|
|
587
|
+
]
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
"id": "data-protection.output-block-secrets",
|
|
591
|
+
"name": "Output Protection — Credentials",
|
|
592
|
+
"description": "Block responses whose own content contains credentials, API keys or tokens — the canonical way a leaked secret reaches a caller. Response direction only.",
|
|
593
|
+
"category": "data-protection",
|
|
594
|
+
"file": "profiles/output_protection/credentials.cedar",
|
|
595
|
+
"severity": "critical",
|
|
596
|
+
"tags": [
|
|
597
|
+
"category:data-protection",
|
|
598
|
+
"threat:secrets",
|
|
599
|
+
"surface:process-response",
|
|
600
|
+
"detection:rule"
|
|
601
|
+
]
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
"id": "security.multi-turn-trajectory",
|
|
605
|
+
"name": "Multi-Turn Trajectory Escalation",
|
|
606
|
+
"description": "Block a conversation whose trajectory is an attack even when no single message is: fires on the gap between the multi-turn model and the single-turn classifier.",
|
|
607
|
+
"category": "security",
|
|
608
|
+
"file": "multi_turn_trajectory.cedar",
|
|
609
|
+
"severity": "critical",
|
|
610
|
+
"tags": [
|
|
611
|
+
"category:security",
|
|
612
|
+
"threat:injection",
|
|
613
|
+
"threat:jailbreak",
|
|
614
|
+
"detection:ml",
|
|
615
|
+
"scope:multi-turn",
|
|
616
|
+
"owasp:llm01",
|
|
617
|
+
"owasp:llm02"
|
|
618
|
+
]
|
|
619
|
+
},
|
|
620
|
+
{
|
|
621
|
+
"id": "agent-security.session-risk-accumulation",
|
|
622
|
+
"name": "Session Risk Accumulation",
|
|
623
|
+
"description": "Block the privileged action a probing conversation is working toward, using the session's high-water mark, cumulative risk, and count of threat turns.",
|
|
624
|
+
"category": "agent-security",
|
|
625
|
+
"file": "session_risk_accumulation.cedar",
|
|
626
|
+
"severity": "critical",
|
|
627
|
+
"tags": [
|
|
628
|
+
"category:agent-security",
|
|
629
|
+
"threat:escalation",
|
|
630
|
+
"detection:aggregate",
|
|
631
|
+
"surface:call-tool",
|
|
632
|
+
"scope:multi-turn",
|
|
633
|
+
"owasp:asi01",
|
|
634
|
+
"owasp:asi04"
|
|
635
|
+
]
|
|
636
|
+
},
|
|
637
|
+
{
|
|
638
|
+
"id": "agent-identity.dual-attribution",
|
|
639
|
+
"name": "Dual Attribution",
|
|
640
|
+
"description": "Block privileged agent actions that cannot be attributed to a human. Deploy in monitor mode first; a service key is unverified until the agent is adopted.",
|
|
641
|
+
"category": "agent-identity",
|
|
642
|
+
"file": "dual_attribution.cedar",
|
|
643
|
+
"severity": "critical",
|
|
644
|
+
"tags": [
|
|
645
|
+
"category:agent-identity",
|
|
646
|
+
"detection:rule",
|
|
647
|
+
"surface:call-tool",
|
|
648
|
+
"scope:per-agent",
|
|
649
|
+
"posture:deny-default",
|
|
650
|
+
"owasp:asi01"
|
|
651
|
+
]
|
|
506
652
|
}
|
|
507
653
|
]
|
|
508
654
|
}
|
|
@@ -333,6 +333,19 @@
|
|
|
333
333
|
"type": "number",
|
|
334
334
|
"required": false,
|
|
335
335
|
"description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant"
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"key": "budget_remaining_pct",
|
|
339
|
+
"type": "number",
|
|
340
|
+
"required": false,
|
|
341
|
+
"description": "Remaining session token budget as a percentage (0-100). Optional, so a policy must guard it: `context has budget_remaining_pct && context.budget_remaining_pct < 10` — a bare comparison fails Cedar validation. Runtime default-fills it to 100 whenever the detector runs, so a high value is not evidence that a budget rule exists.",
|
|
342
|
+
"range": "0-100"
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
"key": "budget_exceeded",
|
|
346
|
+
"type": "boolean",
|
|
347
|
+
"required": false,
|
|
348
|
+
"description": "Whether the session token budget has been exceeded. Optional, so a policy must guard it with `context has budget_exceeded`. The guard is a validation requirement, not a fail-closed property: the detector emits false whenever a session loads, including when no budget rule is configured, so a grant conditioned on `!context.budget_exceeded` will fire on unbudgeted sessions."
|
|
336
349
|
}
|
|
337
350
|
]
|
|
338
351
|
},
|
|
@@ -854,6 +867,19 @@
|
|
|
854
867
|
"type": "number",
|
|
855
868
|
"required": false,
|
|
856
869
|
"description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant"
|
|
870
|
+
},
|
|
871
|
+
{
|
|
872
|
+
"key": "budget_remaining_pct",
|
|
873
|
+
"type": "number",
|
|
874
|
+
"required": false,
|
|
875
|
+
"description": "Remaining session token budget as a percentage (0-100). Optional, so a policy must guard it: `context has budget_remaining_pct && context.budget_remaining_pct < 10` — a bare comparison fails Cedar validation. Runtime default-fills it to 100 whenever the detector runs, so a high value is not evidence that a budget rule exists.",
|
|
876
|
+
"range": "0-100"
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
"key": "budget_exceeded",
|
|
880
|
+
"type": "boolean",
|
|
881
|
+
"required": false,
|
|
882
|
+
"description": "Whether the session token budget has been exceeded. Optional, so a policy must guard it with `context has budget_exceeded`. The guard is a validation requirement, not a fail-closed property: the detector emits false whenever a session loads, including when no budget rule is configured, so a grant conditioned on `!context.budget_exceeded` will fire on unbudgeted sessions."
|
|
857
883
|
}
|
|
858
884
|
]
|
|
859
885
|
},
|
|
@@ -1285,6 +1311,19 @@
|
|
|
1285
1311
|
"type": "number",
|
|
1286
1312
|
"required": false,
|
|
1287
1313
|
"description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant"
|
|
1314
|
+
},
|
|
1315
|
+
{
|
|
1316
|
+
"key": "budget_remaining_pct",
|
|
1317
|
+
"type": "number",
|
|
1318
|
+
"required": false,
|
|
1319
|
+
"description": "Remaining session token budget as a percentage (0-100). Optional, so a policy must guard it: `context has budget_remaining_pct && context.budget_remaining_pct < 10` — a bare comparison fails Cedar validation. Runtime default-fills it to 100 whenever the detector runs, so a high value is not evidence that a budget rule exists.",
|
|
1320
|
+
"range": "0-100"
|
|
1321
|
+
},
|
|
1322
|
+
{
|
|
1323
|
+
"key": "budget_exceeded",
|
|
1324
|
+
"type": "boolean",
|
|
1325
|
+
"required": false,
|
|
1326
|
+
"description": "Whether the session token budget has been exceeded. Optional, so a policy must guard it with `context has budget_exceeded`. The guard is a validation requirement, not a fail-closed property: the detector emits false whenever a session loads, including when no budget rule is configured, so a grant conditioned on `!context.budget_exceeded` will fire on unbudgeted sessions."
|
|
1288
1327
|
}
|
|
1289
1328
|
]
|
|
1290
1329
|
},
|
|
@@ -1494,6 +1533,19 @@
|
|
|
1494
1533
|
"type": "number",
|
|
1495
1534
|
"required": false,
|
|
1496
1535
|
"description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant"
|
|
1536
|
+
},
|
|
1537
|
+
{
|
|
1538
|
+
"key": "budget_remaining_pct",
|
|
1539
|
+
"type": "number",
|
|
1540
|
+
"required": false,
|
|
1541
|
+
"description": "Remaining session token budget as a percentage (0-100). Optional, so a policy must guard it: `context has budget_remaining_pct && context.budget_remaining_pct < 10` — a bare comparison fails Cedar validation. Runtime default-fills it to 100 whenever the detector runs, so a high value is not evidence that a budget rule exists.",
|
|
1542
|
+
"range": "0-100"
|
|
1543
|
+
},
|
|
1544
|
+
{
|
|
1545
|
+
"key": "budget_exceeded",
|
|
1546
|
+
"type": "boolean",
|
|
1547
|
+
"required": false,
|
|
1548
|
+
"description": "Whether the session token budget has been exceeded. Optional, so a policy must guard it with `context has budget_exceeded`. The guard is a validation requirement, not a fail-closed property: the detector emits false whenever a session loads, including when no budget rule is configured, so a grant conditioned on `!context.budget_exceeded` will fire on unbudgeted sessions."
|
|
1497
1549
|
}
|
|
1498
1550
|
]
|
|
1499
1551
|
},
|
|
@@ -1709,6 +1761,19 @@
|
|
|
1709
1761
|
"type": "number",
|
|
1710
1762
|
"required": false,
|
|
1711
1763
|
"description": "Sum of per-turn risk scores across the session. Catches death-by-a-thousand-cuts where no single turn is high but cumulative risk is significant"
|
|
1764
|
+
},
|
|
1765
|
+
{
|
|
1766
|
+
"key": "budget_remaining_pct",
|
|
1767
|
+
"type": "number",
|
|
1768
|
+
"required": false,
|
|
1769
|
+
"description": "Remaining session token budget as a percentage (0-100). Optional, so a policy must guard it: `context has budget_remaining_pct && context.budget_remaining_pct < 10` — a bare comparison fails Cedar validation. Runtime default-fills it to 100 whenever the detector runs, so a high value is not evidence that a budget rule exists.",
|
|
1770
|
+
"range": "0-100"
|
|
1771
|
+
},
|
|
1772
|
+
{
|
|
1773
|
+
"key": "budget_exceeded",
|
|
1774
|
+
"type": "boolean",
|
|
1775
|
+
"required": false,
|
|
1776
|
+
"description": "Whether the session token budget has been exceeded. Optional, so a policy must guard it with `context has budget_exceeded`. The guard is a validation requirement, not a fail-closed property: the detector emits false whenever a session loads, including when no budget rule is configured, so a grant conditioned on `!context.budget_exceeded` will fire on unbudgeted sessions."
|
|
1712
1777
|
}
|
|
1713
1778
|
]
|
|
1714
1779
|
}
|
|
@@ -299,6 +299,47 @@
|
|
|
299
299
|
}
|
|
300
300
|
]
|
|
301
301
|
},
|
|
302
|
+
{
|
|
303
|
+
"id": "budget_checker",
|
|
304
|
+
"stability": "stable",
|
|
305
|
+
"tier": "fast",
|
|
306
|
+
"description": "Tracks cumulative session token consumption against a configured budget. On the IDE surface the total is reported by the Overwatch daemon on the prompt and before-tool events (cerberus#147); only the token-budget attributes apply here, not the gateway-metered RPM/TPM ones.",
|
|
307
|
+
"producesAttrs": [
|
|
308
|
+
{
|
|
309
|
+
"name": "budget_remaining_pct",
|
|
310
|
+
"type": "Long",
|
|
311
|
+
"semantic": "severity_0_100",
|
|
312
|
+
"description": "Remaining session token budget (0-100). Default-filled to 100 when no metering ran."
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"name": "budget_exceeded",
|
|
316
|
+
"type": "Bool",
|
|
317
|
+
"semantic": "boolean_flag",
|
|
318
|
+
"description": "True iff the session token budget has been exceeded. Absent when no metering ran, so policies must guard with `context has`."
|
|
319
|
+
}
|
|
320
|
+
],
|
|
321
|
+
"supportedModes": [
|
|
322
|
+
"enforce",
|
|
323
|
+
"monitor",
|
|
324
|
+
"alert"
|
|
325
|
+
],
|
|
326
|
+
"modifyContract": null,
|
|
327
|
+
"displayName": "Budget Checker",
|
|
328
|
+
"category": "agent_behavior",
|
|
329
|
+
"defendsAgainst": [
|
|
330
|
+
"unbounded_consumption"
|
|
331
|
+
],
|
|
332
|
+
"exampleAttacks": [
|
|
333
|
+
{
|
|
334
|
+
"title": "Runaway autonomous session",
|
|
335
|
+
"vulnerabilityId": "unbounded_consumption",
|
|
336
|
+
"snippet": "(agentic) one prompt drives hundreds of tool calls, burning the session's token budget unattended",
|
|
337
|
+
"expectedSignal": {
|
|
338
|
+
"budget_exceeded": true
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
]
|
|
342
|
+
},
|
|
302
343
|
{
|
|
303
344
|
"id": "tool_risk",
|
|
304
345
|
"stability": "stable",
|
|
@@ -870,6 +911,12 @@
|
|
|
870
911
|
"workspace_root"
|
|
871
912
|
],
|
|
872
913
|
"fieldToDetectorIds": {
|
|
914
|
+
"budget_exceeded": [
|
|
915
|
+
"budget_checker"
|
|
916
|
+
],
|
|
917
|
+
"budget_remaining_pct": [
|
|
918
|
+
"budget_checker"
|
|
919
|
+
],
|
|
873
920
|
"crime_score": [
|
|
874
921
|
"toxicity"
|
|
875
922
|
],
|
|
@@ -151,6 +151,24 @@ action process_prompt appliesTo {
|
|
|
151
151
|
// --- Legacy ---
|
|
152
152
|
prompt_text?: String, // Same as content (backward compatibility)
|
|
153
153
|
response_content?: String, // Response content (if available)
|
|
154
|
+
// --- Session Token Budget --- (OWASP LLM10; CAP-ENF-009, cerberus#147)
|
|
155
|
+
// Fed by the cumulative session total the Overwatch daemon reports on the
|
|
156
|
+
// prompt and before-tool events.
|
|
157
|
+
//
|
|
158
|
+
// BOTH are optional, so BOTH need a `has` guard — a bare
|
|
159
|
+
// `context.budget_remaining_pct < 10` fails cedar validation with
|
|
160
|
+
// "unable to guarantee safety of access to optional attribute".
|
|
161
|
+
// Write: `context has budget_remaining_pct && context.budget_remaining_pct < 10`.
|
|
162
|
+
//
|
|
163
|
+
// The guard is a validation requirement, NOT a safety property. Whenever a
|
|
164
|
+
// session loads, budget_checker emits both keys — including
|
|
165
|
+
// budget_exceeded: false and budget_remaining_pct: 100 when no budget rule
|
|
166
|
+
// is configured for the scope. So a grant conditioned on
|
|
167
|
+
// `!context.budget_exceeded` fires on unbudgeted sessions, and a low
|
|
168
|
+
// percentage is evidence of spend only when a budget rule exists. Absence
|
|
169
|
+
// means the detector did not run at all (no session).
|
|
170
|
+
budget_remaining_pct?: Long, // Remaining session token budget (0-100)
|
|
171
|
+
budget_exceeded?: Bool, // Session token budget exceeded
|
|
154
172
|
},
|
|
155
173
|
};
|
|
156
174
|
|
|
@@ -270,6 +288,24 @@ action call_tool appliesTo {
|
|
|
270
288
|
|
|
271
289
|
// --- Legacy ---
|
|
272
290
|
response_content?: String,
|
|
291
|
+
// --- Session Token Budget --- (OWASP LLM10; CAP-ENF-009, cerberus#147)
|
|
292
|
+
// Fed by the cumulative session total the Overwatch daemon reports on the
|
|
293
|
+
// prompt and before-tool events.
|
|
294
|
+
//
|
|
295
|
+
// BOTH are optional, so BOTH need a `has` guard — a bare
|
|
296
|
+
// `context.budget_remaining_pct < 10` fails cedar validation with
|
|
297
|
+
// "unable to guarantee safety of access to optional attribute".
|
|
298
|
+
// Write: `context has budget_remaining_pct && context.budget_remaining_pct < 10`.
|
|
299
|
+
//
|
|
300
|
+
// The guard is a validation requirement, NOT a safety property. Whenever a
|
|
301
|
+
// session loads, budget_checker emits both keys — including
|
|
302
|
+
// budget_exceeded: false and budget_remaining_pct: 100 when no budget rule
|
|
303
|
+
// is configured for the scope. So a grant conditioned on
|
|
304
|
+
// `!context.budget_exceeded` fires on unbudgeted sessions, and a low
|
|
305
|
+
// percentage is evidence of spend only when a budget rule exists. Absence
|
|
306
|
+
// means the detector did not run at all (no session).
|
|
307
|
+
budget_remaining_pct?: Long, // Remaining session token budget (0-100)
|
|
308
|
+
budget_exceeded?: Bool, // Session token budget exceeded
|
|
273
309
|
},
|
|
274
310
|
};
|
|
275
311
|
|
|
@@ -392,6 +428,24 @@ action connect_server appliesTo {
|
|
|
392
428
|
session_max_pii_score?: Long,
|
|
393
429
|
session_max_secret_score?: Long,
|
|
394
430
|
session_cumulative_risk_score?: Long,
|
|
431
|
+
// --- Session Token Budget --- (OWASP LLM10; CAP-ENF-009, cerberus#147)
|
|
432
|
+
// Fed by the cumulative session total the Overwatch daemon reports on the
|
|
433
|
+
// prompt and before-tool events.
|
|
434
|
+
//
|
|
435
|
+
// BOTH are optional, so BOTH need a `has` guard — a bare
|
|
436
|
+
// `context.budget_remaining_pct < 10` fails cedar validation with
|
|
437
|
+
// "unable to guarantee safety of access to optional attribute".
|
|
438
|
+
// Write: `context has budget_remaining_pct && context.budget_remaining_pct < 10`.
|
|
439
|
+
//
|
|
440
|
+
// The guard is a validation requirement, NOT a safety property. Whenever a
|
|
441
|
+
// session loads, budget_checker emits both keys — including
|
|
442
|
+
// budget_exceeded: false and budget_remaining_pct: 100 when no budget rule
|
|
443
|
+
// is configured for the scope. So a grant conditioned on
|
|
444
|
+
// `!context.budget_exceeded` fires on unbudgeted sessions, and a low
|
|
445
|
+
// percentage is evidence of spend only when a budget rule exists. Absence
|
|
446
|
+
// means the detector did not run at all (no session).
|
|
447
|
+
budget_remaining_pct?: Long, // Remaining session token budget (0-100)
|
|
448
|
+
budget_exceeded?: Bool, // Session token budget exceeded
|
|
395
449
|
},
|
|
396
450
|
};
|
|
397
451
|
|
|
@@ -446,6 +500,24 @@ action read_file appliesTo {
|
|
|
446
500
|
|
|
447
501
|
// --- Agent Security ---
|
|
448
502
|
indirect_injection_score?: Long, // Indirect injection via file content
|
|
503
|
+
// --- Session Token Budget --- (OWASP LLM10; CAP-ENF-009, cerberus#147)
|
|
504
|
+
// Fed by the cumulative session total the Overwatch daemon reports on the
|
|
505
|
+
// prompt and before-tool events.
|
|
506
|
+
//
|
|
507
|
+
// BOTH are optional, so BOTH need a `has` guard — a bare
|
|
508
|
+
// `context.budget_remaining_pct < 10` fails cedar validation with
|
|
509
|
+
// "unable to guarantee safety of access to optional attribute".
|
|
510
|
+
// Write: `context has budget_remaining_pct && context.budget_remaining_pct < 10`.
|
|
511
|
+
//
|
|
512
|
+
// The guard is a validation requirement, NOT a safety property. Whenever a
|
|
513
|
+
// session loads, budget_checker emits both keys — including
|
|
514
|
+
// budget_exceeded: false and budget_remaining_pct: 100 when no budget rule
|
|
515
|
+
// is configured for the scope. So a grant conditioned on
|
|
516
|
+
// `!context.budget_exceeded` fires on unbudgeted sessions, and a low
|
|
517
|
+
// percentage is evidence of spend only when a budget rule exists. Absence
|
|
518
|
+
// means the detector did not run at all (no session).
|
|
519
|
+
budget_remaining_pct?: Long, // Remaining session token budget (0-100)
|
|
520
|
+
budget_exceeded?: Bool, // Session token budget exceeded
|
|
449
521
|
},
|
|
450
522
|
};
|
|
451
523
|
|
|
@@ -500,6 +572,24 @@ action write_file appliesTo {
|
|
|
500
572
|
|
|
501
573
|
// --- Agent Security ---
|
|
502
574
|
indirect_injection_score?: Long, // Indirect injection via file content
|
|
575
|
+
// --- Session Token Budget --- (OWASP LLM10; CAP-ENF-009, cerberus#147)
|
|
576
|
+
// Fed by the cumulative session total the Overwatch daemon reports on the
|
|
577
|
+
// prompt and before-tool events.
|
|
578
|
+
//
|
|
579
|
+
// BOTH are optional, so BOTH need a `has` guard — a bare
|
|
580
|
+
// `context.budget_remaining_pct < 10` fails cedar validation with
|
|
581
|
+
// "unable to guarantee safety of access to optional attribute".
|
|
582
|
+
// Write: `context has budget_remaining_pct && context.budget_remaining_pct < 10`.
|
|
583
|
+
//
|
|
584
|
+
// The guard is a validation requirement, NOT a safety property. Whenever a
|
|
585
|
+
// session loads, budget_checker emits both keys — including
|
|
586
|
+
// budget_exceeded: false and budget_remaining_pct: 100 when no budget rule
|
|
587
|
+
// is configured for the scope. So a grant conditioned on
|
|
588
|
+
// `!context.budget_exceeded` fires on unbudgeted sessions, and a low
|
|
589
|
+
// percentage is evidence of spend only when a budget rule exists. Absence
|
|
590
|
+
// means the detector did not run at all (no session).
|
|
591
|
+
budget_remaining_pct?: Long, // Remaining session token budget (0-100)
|
|
592
|
+
budget_exceeded?: Bool, // Session token budget exceeded
|
|
503
593
|
},
|
|
504
594
|
};
|
|
505
595
|
|