@highflame/policy 2.2.42 → 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/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/session_risk_accumulation.cedar +103 -0
- package/_schemas/guardrails/templates/templates.json +50 -0
- package/dist/agent_ops-context.gen.d.ts +1 -2
- package/dist/agent_ops-context.gen.js +0 -2
- package/dist/guardrails-context.gen.d.ts +1 -2
- package/dist/guardrails-context.gen.js +0 -2
- package/dist/guardrails-defaults.gen.js +409 -0
- package/dist/parser.d.ts +2 -1
- package/dist/parser.js +124 -37
- package/dist/service-schemas.gen.d.ts +2 -2
- package/dist/service-schemas.gen.js +87 -24
- package/package.json +1 -1
|
@@ -25,12 +25,6 @@
|
|
|
25
25
|
"required": false,
|
|
26
26
|
"description": "Principal identity class projected from the token: 'human', 'agent', 'service', or 'mcp_server'"
|
|
27
27
|
},
|
|
28
|
-
{
|
|
29
|
-
"key": "principal",
|
|
30
|
-
"type": "string",
|
|
31
|
-
"required": false,
|
|
32
|
-
"description": "Stable principal identifier projected from the token (e.g. a ZeroID / WIMSE URI or user id)"
|
|
33
|
-
},
|
|
34
28
|
{
|
|
35
29
|
"key": "request_id",
|
|
36
30
|
"type": "string",
|
|
@@ -817,12 +811,6 @@
|
|
|
817
811
|
"required": false,
|
|
818
812
|
"description": "Principal identity class projected from the token: 'human', 'agent', 'service', or 'mcp_server'"
|
|
819
813
|
},
|
|
820
|
-
{
|
|
821
|
-
"key": "principal",
|
|
822
|
-
"type": "string",
|
|
823
|
-
"required": false,
|
|
824
|
-
"description": "Stable principal identifier projected from the token (e.g. a ZeroID / WIMSE URI or user id)"
|
|
825
|
-
},
|
|
826
814
|
{
|
|
827
815
|
"key": "request_id",
|
|
828
816
|
"type": "string",
|
|
@@ -1609,12 +1597,6 @@
|
|
|
1609
1597
|
"required": false,
|
|
1610
1598
|
"description": "Principal identity class: 'human', 'agent', 'service', or 'mcp_server'"
|
|
1611
1599
|
},
|
|
1612
|
-
{
|
|
1613
|
-
"key": "principal",
|
|
1614
|
-
"type": "string",
|
|
1615
|
-
"required": false,
|
|
1616
|
-
"description": "Stable principal identifier"
|
|
1617
|
-
},
|
|
1618
1600
|
{
|
|
1619
1601
|
"key": "request_id",
|
|
1620
1602
|
"type": "string",
|
|
@@ -2372,12 +2354,6 @@
|
|
|
2372
2354
|
"required": false,
|
|
2373
2355
|
"description": "Principal identity class: 'human', 'agent', 'service', or 'mcp_server'"
|
|
2374
2356
|
},
|
|
2375
|
-
{
|
|
2376
|
-
"key": "principal",
|
|
2377
|
-
"type": "string",
|
|
2378
|
-
"required": false,
|
|
2379
|
-
"description": "Stable principal identifier"
|
|
2380
|
-
},
|
|
2381
2357
|
{
|
|
2382
2358
|
"key": "request_id",
|
|
2383
2359
|
"type": "string",
|
|
@@ -2752,12 +2728,6 @@
|
|
|
2752
2728
|
"required": false,
|
|
2753
2729
|
"description": "Principal identity class: 'human', 'agent', 'service', or 'mcp_server'"
|
|
2754
2730
|
},
|
|
2755
|
-
{
|
|
2756
|
-
"key": "principal",
|
|
2757
|
-
"type": "string",
|
|
2758
|
-
"required": false,
|
|
2759
|
-
"description": "Stable principal identifier"
|
|
2760
|
-
},
|
|
2761
2731
|
{
|
|
2762
2732
|
"key": "request_id",
|
|
2763
2733
|
"type": "string",
|
|
@@ -3145,12 +3115,6 @@
|
|
|
3145
3115
|
"required": false,
|
|
3146
3116
|
"description": "Principal identity class: 'human', 'agent', 'service', or 'mcp_server'"
|
|
3147
3117
|
},
|
|
3148
|
-
{
|
|
3149
|
-
"key": "principal",
|
|
3150
|
-
"type": "string",
|
|
3151
|
-
"required": false,
|
|
3152
|
-
"description": "Stable principal identifier"
|
|
3153
|
-
},
|
|
3154
3118
|
{
|
|
3155
3119
|
"key": "request_id",
|
|
3156
3120
|
"type": "string",
|
|
@@ -3565,12 +3529,6 @@
|
|
|
3565
3529
|
"required": false,
|
|
3566
3530
|
"description": "Principal identity class: 'human', 'agent', 'service', or 'mcp_server'"
|
|
3567
3531
|
},
|
|
3568
|
-
{
|
|
3569
|
-
"key": "principal",
|
|
3570
|
-
"type": "string",
|
|
3571
|
-
"required": false,
|
|
3572
|
-
"description": "Stable principal identifier (ZeroID / WIMSE URI or user id)"
|
|
3573
|
-
},
|
|
3574
3532
|
{
|
|
3575
3533
|
"key": "agent_trust_level",
|
|
3576
3534
|
"type": "string",
|
|
@@ -122,6 +122,44 @@ namespace AgentOps {
|
|
|
122
122
|
// Context Types (Action-Specific)
|
|
123
123
|
// =========================================================================
|
|
124
124
|
|
|
125
|
+
/// The ZeroID NHI principal block (ADR 0009 / CAP-IDN-011).
|
|
126
|
+
///
|
|
127
|
+
/// A record, not a string: Shield projects the whole identity block under
|
|
128
|
+
/// `context.principal`, so policies read members off it —
|
|
129
|
+
///
|
|
130
|
+
/// permit ... when { context has principal &&
|
|
131
|
+
/// context.principal.trust_level == "first_party" };
|
|
132
|
+
/// forbid ... unless { context has principal &&
|
|
133
|
+
/// context.principal has act_sub };
|
|
134
|
+
///
|
|
135
|
+
/// Declared once and shared by every action context so a new member cannot
|
|
136
|
+
/// land on one action and go missing on another.
|
|
137
|
+
///
|
|
138
|
+
/// `act` (RFC 8693 delegation) is flattened to `act_sub` / `act_iss`
|
|
139
|
+
/// because Cedar records are flat key/value at each level; the full
|
|
140
|
+
/// delegation tree is reconstructable audit-side by joining on `mission_id`.
|
|
141
|
+
///
|
|
142
|
+
/// Every member is optional except `delegation_depth`: the projector omits
|
|
143
|
+
/// a field whose value is empty, but always emits the depth because zero is
|
|
144
|
+
/// the meaningful base case for a direct (non-delegated) token. The record
|
|
145
|
+
/// itself is optional — it is absent, not empty, when the request carries no
|
|
146
|
+
/// NHI claims (RS256 human tokens, internal-service callers), so
|
|
147
|
+
/// `context has principal` is false rather than true-but-empty.
|
|
148
|
+
type PrincipalContext = {
|
|
149
|
+
"identity_type"?: String, // "human" | "agent" | "service" | "mcp_server"
|
|
150
|
+
"sub_type"?: String, // Identity subtype (e.g. "coding_agent")
|
|
151
|
+
"trust_level"?: String, // "first_party" | "verified" | "unverified"
|
|
152
|
+
"framework"?: String, // Agent framework (e.g. "langchain")
|
|
153
|
+
"publisher"?: String, // Agent publisher
|
|
154
|
+
"capabilities"?: Set<String>, // Capabilities granted to the identity
|
|
155
|
+
"scopes"?: Set<String>, // OAuth scopes carried by the token
|
|
156
|
+
"delegation_depth": Long, // Delegation hops; 0 for a direct token
|
|
157
|
+
"act_sub"?: String, // RFC 8693 actor — the accountable human
|
|
158
|
+
"act_iss"?: String, // Issuer of the actor claim
|
|
159
|
+
"mission_id"?: String, // Correlates one delegation tree
|
|
160
|
+
"grant_type"?: String, // How the token was obtained (e.g. "api_key")
|
|
161
|
+
};
|
|
162
|
+
|
|
125
163
|
/// Context for the process_response action.
|
|
126
164
|
///
|
|
127
165
|
/// An alias, not a copy: prompts and responses are inspected by the same
|
|
@@ -136,7 +174,7 @@ namespace AgentOps {
|
|
|
136
174
|
"role"?: String,
|
|
137
175
|
"privilege_scope"?: Set<String>,
|
|
138
176
|
"identity_type"?: String, // Principal identity class: "human" | "agent" | "service" | "mcp_server"
|
|
139
|
-
"principal"?:
|
|
177
|
+
"principal"?: PrincipalContext, // ZeroID NHI identity block; absent when the token carries no NHI claims
|
|
140
178
|
// Core metadata (required)
|
|
141
179
|
"request_id": String,
|
|
142
180
|
"timestamp": Long,
|
|
@@ -331,7 +369,7 @@ namespace AgentOps {
|
|
|
331
369
|
"role"?: String,
|
|
332
370
|
"privilege_scope"?: Set<String>,
|
|
333
371
|
"identity_type"?: String,
|
|
334
|
-
"principal"?:
|
|
372
|
+
"principal"?: PrincipalContext, // ZeroID NHI identity block; absent when the token carries no NHI claims
|
|
335
373
|
// Core metadata (required)
|
|
336
374
|
"request_id": String,
|
|
337
375
|
"timestamp": Long,
|
|
@@ -542,7 +580,7 @@ namespace AgentOps {
|
|
|
542
580
|
"role"?: String,
|
|
543
581
|
"privilege_scope"?: Set<String>,
|
|
544
582
|
"identity_type"?: String,
|
|
545
|
-
"principal"?:
|
|
583
|
+
"principal"?: PrincipalContext, // ZeroID NHI identity block; absent when the token carries no NHI claims
|
|
546
584
|
// Core metadata (required)
|
|
547
585
|
"request_id": String,
|
|
548
586
|
"timestamp": Long,
|
|
@@ -650,7 +688,7 @@ namespace AgentOps {
|
|
|
650
688
|
"role"?: String,
|
|
651
689
|
"privilege_scope"?: Set<String>,
|
|
652
690
|
"identity_type"?: String,
|
|
653
|
-
"principal"?:
|
|
691
|
+
"principal"?: PrincipalContext, // ZeroID NHI identity block; absent when the token carries no NHI claims
|
|
654
692
|
// Core metadata (required)
|
|
655
693
|
"request_id": String,
|
|
656
694
|
"timestamp": Long,
|
|
@@ -762,7 +800,7 @@ namespace AgentOps {
|
|
|
762
800
|
"role"?: String,
|
|
763
801
|
"privilege_scope"?: Set<String>,
|
|
764
802
|
"identity_type"?: String,
|
|
765
|
-
"principal"?:
|
|
803
|
+
"principal"?: PrincipalContext, // ZeroID NHI identity block; absent when the token carries no NHI claims
|
|
766
804
|
// Core metadata (required)
|
|
767
805
|
"request_id": String,
|
|
768
806
|
"timestamp": Long,
|
|
@@ -895,7 +933,7 @@ namespace AgentOps {
|
|
|
895
933
|
"role"?: String,
|
|
896
934
|
"privilege_scope"?: Set<String>,
|
|
897
935
|
"identity_type"?: String, // Principal identity class: "human" | "agent" | "service" | "mcp_server"
|
|
898
|
-
"principal"?:
|
|
936
|
+
"principal"?: PrincipalContext, // ZeroID NHI identity block; absent when the token carries no NHI claims
|
|
899
937
|
"agent_trust_level"?: String, // "untrusted" | "low" | "verified" | "trusted"
|
|
900
938
|
|
|
901
939
|
// Core metadata (required)
|
|
@@ -25,12 +25,6 @@
|
|
|
25
25
|
"required": false,
|
|
26
26
|
"description": "Principal identity class projected from the token: 'human', 'agent', or 'service'. Use to apply identity-class-specific policies"
|
|
27
27
|
},
|
|
28
|
-
{
|
|
29
|
-
"key": "principal",
|
|
30
|
-
"type": "string",
|
|
31
|
-
"required": false,
|
|
32
|
-
"description": "Stable principal identifier projected from the token (e.g. a ZeroID / WIMSE URI or user id). Absent when the token carries no principal claim"
|
|
33
|
-
},
|
|
34
28
|
{
|
|
35
29
|
"key": "request_id",
|
|
36
30
|
"type": "string",
|
|
@@ -696,12 +690,6 @@
|
|
|
696
690
|
"required": false,
|
|
697
691
|
"description": "Principal identity class projected from the token: 'human', 'agent', or 'service'. Use to apply identity-class-specific policies"
|
|
698
692
|
},
|
|
699
|
-
{
|
|
700
|
-
"key": "principal",
|
|
701
|
-
"type": "string",
|
|
702
|
-
"required": false,
|
|
703
|
-
"description": "Stable principal identifier projected from the token (e.g. a ZeroID / WIMSE URI or user id). Absent when the token carries no principal claim"
|
|
704
|
-
},
|
|
705
693
|
{
|
|
706
694
|
"key": "request_id",
|
|
707
695
|
"type": "string",
|
|
@@ -1367,12 +1355,6 @@
|
|
|
1367
1355
|
"required": false,
|
|
1368
1356
|
"description": "Principal identity class projected from the token: 'human', 'agent', or 'service'. Use to apply identity-class-specific policies"
|
|
1369
1357
|
},
|
|
1370
|
-
{
|
|
1371
|
-
"key": "principal",
|
|
1372
|
-
"type": "string",
|
|
1373
|
-
"required": false,
|
|
1374
|
-
"description": "Stable principal identifier projected from the token (e.g. a ZeroID / WIMSE URI or user id). Absent when the token carries no principal claim"
|
|
1375
|
-
},
|
|
1376
1358
|
{
|
|
1377
1359
|
"key": "request_id",
|
|
1378
1360
|
"type": "string",
|
|
@@ -2098,12 +2080,6 @@
|
|
|
2098
2080
|
"required": false,
|
|
2099
2081
|
"description": "Principal identity class projected from the token: 'human', 'agent', or 'service'. Use to apply identity-class-specific policies"
|
|
2100
2082
|
},
|
|
2101
|
-
{
|
|
2102
|
-
"key": "principal",
|
|
2103
|
-
"type": "string",
|
|
2104
|
-
"required": false,
|
|
2105
|
-
"description": "Stable principal identifier projected from the token (e.g. a ZeroID / WIMSE URI or user id). Absent when the token carries no principal claim"
|
|
2106
|
-
},
|
|
2107
2083
|
{
|
|
2108
2084
|
"key": "request_id",
|
|
2109
2085
|
"type": "string",
|
|
@@ -2345,12 +2321,6 @@
|
|
|
2345
2321
|
"required": false,
|
|
2346
2322
|
"description": "Principal identity class projected from the token: 'human', 'agent', or 'service'. Use to apply identity-class-specific policies"
|
|
2347
2323
|
},
|
|
2348
|
-
{
|
|
2349
|
-
"key": "principal",
|
|
2350
|
-
"type": "string",
|
|
2351
|
-
"required": false,
|
|
2352
|
-
"description": "Stable principal identifier projected from the token (e.g. a ZeroID / WIMSE URI or user id). Absent when the token carries no principal claim"
|
|
2353
|
-
},
|
|
2354
2324
|
{
|
|
2355
2325
|
"key": "request_id",
|
|
2356
2326
|
"type": "string",
|
|
@@ -2605,12 +2575,6 @@
|
|
|
2605
2575
|
"required": false,
|
|
2606
2576
|
"description": "Principal identity class projected from the token: 'human', 'agent', or 'service'. Use to apply identity-class-specific policies"
|
|
2607
2577
|
},
|
|
2608
|
-
{
|
|
2609
|
-
"key": "principal",
|
|
2610
|
-
"type": "string",
|
|
2611
|
-
"required": false,
|
|
2612
|
-
"description": "Stable principal identifier projected from the token (e.g. a ZeroID / WIMSE URI or user id). Absent when the token carries no principal claim"
|
|
2613
|
-
},
|
|
2614
2578
|
{
|
|
2615
2579
|
"key": "request_id",
|
|
2616
2580
|
"type": "string",
|
|
@@ -105,6 +105,44 @@ namespace Guardrails {
|
|
|
105
105
|
// Context Types (Action-Specific)
|
|
106
106
|
// =========================================================================
|
|
107
107
|
|
|
108
|
+
/// The ZeroID NHI principal block (ADR 0009 / CAP-IDN-011).
|
|
109
|
+
///
|
|
110
|
+
/// A record, not a string: Shield projects the whole identity block under
|
|
111
|
+
/// `context.principal`, so policies read members off it —
|
|
112
|
+
///
|
|
113
|
+
/// permit ... when { context has principal &&
|
|
114
|
+
/// context.principal.trust_level == "first_party" };
|
|
115
|
+
/// forbid ... unless { context has principal &&
|
|
116
|
+
/// context.principal has act_sub };
|
|
117
|
+
///
|
|
118
|
+
/// Declared once and shared by every action context so a new member cannot
|
|
119
|
+
/// land on one action and go missing on another.
|
|
120
|
+
///
|
|
121
|
+
/// `act` (RFC 8693 delegation) is flattened to `act_sub` / `act_iss`
|
|
122
|
+
/// because Cedar records are flat key/value at each level; the full
|
|
123
|
+
/// delegation tree is reconstructable audit-side by joining on `mission_id`.
|
|
124
|
+
///
|
|
125
|
+
/// Every member is optional except `delegation_depth`: the projector omits
|
|
126
|
+
/// a field whose value is empty, but always emits the depth because zero is
|
|
127
|
+
/// the meaningful base case for a direct (non-delegated) token. The record
|
|
128
|
+
/// itself is optional — it is absent, not empty, when the request carries no
|
|
129
|
+
/// NHI claims (RS256 human tokens, internal-service callers), so
|
|
130
|
+
/// `context has principal` is false rather than true-but-empty.
|
|
131
|
+
type PrincipalContext = {
|
|
132
|
+
"identity_type"?: String, // "human" | "agent" | "service"
|
|
133
|
+
"sub_type"?: String, // Identity subtype (e.g. "coding_agent")
|
|
134
|
+
"trust_level"?: String, // "first_party" | "verified" | "unverified"
|
|
135
|
+
"framework"?: String, // Agent framework (e.g. "langchain")
|
|
136
|
+
"publisher"?: String, // Agent publisher
|
|
137
|
+
"capabilities"?: Set<String>, // Capabilities granted to the identity
|
|
138
|
+
"scopes"?: Set<String>, // OAuth scopes carried by the token
|
|
139
|
+
"delegation_depth": Long, // Delegation hops; 0 for a direct token
|
|
140
|
+
"act_sub"?: String, // RFC 8693 actor — the accountable human
|
|
141
|
+
"act_iss"?: String, // Issuer of the actor claim
|
|
142
|
+
"mission_id"?: String, // Correlates one delegation tree
|
|
143
|
+
"grant_type"?: String, // How the token was obtained (e.g. "api_key")
|
|
144
|
+
};
|
|
145
|
+
|
|
108
146
|
/// Context for the process_response action.
|
|
109
147
|
///
|
|
110
148
|
/// An alias, not a copy: prompts and responses are inspected by the same
|
|
@@ -121,7 +159,7 @@ namespace Guardrails {
|
|
|
121
159
|
"role"?: String,
|
|
122
160
|
"privilege_scope"?: Set<String>,
|
|
123
161
|
"identity_type"?: String, // Principal identity class: "human" | "agent" | "service"
|
|
124
|
-
"principal"?:
|
|
162
|
+
"principal"?: PrincipalContext, // ZeroID NHI identity block; absent when the token carries no NHI claims
|
|
125
163
|
// Core metadata (required)
|
|
126
164
|
"request_id": String,
|
|
127
165
|
"timestamp": Long,
|
|
@@ -273,7 +311,7 @@ namespace Guardrails {
|
|
|
273
311
|
"role"?: String,
|
|
274
312
|
"privilege_scope"?: Set<String>,
|
|
275
313
|
"identity_type"?: String, // Principal identity class: "human" | "agent" | "service"
|
|
276
|
-
"principal"?:
|
|
314
|
+
"principal"?: PrincipalContext, // ZeroID NHI identity block; absent when the token carries no NHI claims
|
|
277
315
|
// Core metadata (required)
|
|
278
316
|
"request_id": String,
|
|
279
317
|
"timestamp": Long,
|
|
@@ -469,7 +507,7 @@ namespace Guardrails {
|
|
|
469
507
|
"role"?: String,
|
|
470
508
|
"privilege_scope"?: Set<String>,
|
|
471
509
|
"identity_type"?: String, // Principal identity class: "human" | "agent" | "service"
|
|
472
|
-
"principal"?:
|
|
510
|
+
"principal"?: PrincipalContext, // ZeroID NHI identity block; absent when the token carries no NHI claims
|
|
473
511
|
// Core metadata (required)
|
|
474
512
|
"request_id": String,
|
|
475
513
|
"timestamp": Long,
|
|
@@ -542,7 +580,7 @@ namespace Guardrails {
|
|
|
542
580
|
"role"?: String,
|
|
543
581
|
"privilege_scope"?: Set<String>,
|
|
544
582
|
"identity_type"?: String, // Principal identity class: "human" | "agent" | "service"
|
|
545
|
-
"principal"?:
|
|
583
|
+
"principal"?: PrincipalContext, // ZeroID NHI identity block; absent when the token carries no NHI claims
|
|
546
584
|
// Core metadata (required)
|
|
547
585
|
"request_id": String,
|
|
548
586
|
"timestamp": Long,
|
|
@@ -619,7 +657,7 @@ namespace Guardrails {
|
|
|
619
657
|
"role"?: String,
|
|
620
658
|
"privilege_scope"?: Set<String>,
|
|
621
659
|
"identity_type"?: String, // Principal identity class: "human" | "agent" | "service"
|
|
622
|
-
"principal"?:
|
|
660
|
+
"principal"?: PrincipalContext, // ZeroID NHI identity block; absent when the token carries no NHI claims
|
|
623
661
|
// Core metadata (required)
|
|
624
662
|
"request_id": String,
|
|
625
663
|
"timestamp": Long,
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Dual Attribution
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Blocks privileged agent actions that cannot be attributed to a human. "Which
|
|
5
|
+
// agent did this?" is half an answer; the other half is "on whose behalf?" An
|
|
6
|
+
// agent is not an accountable party — the person who pointed it at the work
|
|
7
|
+
// is. Shield projects both sides of that pair, so the requirement can be a
|
|
8
|
+
// policy rather than a reporting convention nobody enforces.
|
|
9
|
+
//
|
|
10
|
+
// The agent comes from the authenticated identity, never from the request
|
|
11
|
+
// body: agent_id, agent_type, agent_trust_level, agent_framework. The human
|
|
12
|
+
// comes from the identity claims on the credential, as the `principal` record
|
|
13
|
+
// — its `act_sub` field names the party the agent is acting for.
|
|
14
|
+
//
|
|
15
|
+
// Deploy in MONITOR mode first. Section 2 blocks unverified agents, and a
|
|
16
|
+
// service key authenticates as unverified until the agent is registered and
|
|
17
|
+
// adopted in Studio. Monitor records what each rule would have blocked on
|
|
18
|
+
// every event without blocking anything; register the agent, then switch to
|
|
19
|
+
// enforce.
|
|
20
|
+
//
|
|
21
|
+
// Detection layers:
|
|
22
|
+
// - agent identity (authentication layer, always available)
|
|
23
|
+
// - tool_validator (tool_is_sensitive, tool_category, always available)
|
|
24
|
+
//
|
|
25
|
+
// Context keys consumed:
|
|
26
|
+
// - agent_id, agent_type, agent_trust_level: String
|
|
27
|
+
// - principal: record — act_sub names the accountable human
|
|
28
|
+
// - tool_is_sensitive: Bool
|
|
29
|
+
// - tool_category: String
|
|
30
|
+
//
|
|
31
|
+
// Compliance:
|
|
32
|
+
// - OWASP ASI01, OWASP ASI04
|
|
33
|
+
//
|
|
34
|
+
// Category: agent-identity
|
|
35
|
+
// Namespace: Guardrails
|
|
36
|
+
// =============================================================================
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Section 1: No unattributed privileged action
|
|
40
|
+
// `unless` because the rule must fire when attribution is ABSENT, and an
|
|
41
|
+
// absent field cannot be compared — only tested for.
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
|
|
44
|
+
@id("agent-identity.require-principal-for-sensitive-tools")
|
|
45
|
+
@name("Block sensitive agent tools without a principal")
|
|
46
|
+
@description("Blocks call_tool on a sensitive tool by an agent unless the credential carries a principal whose act_sub names the human the agent acts for.")
|
|
47
|
+
@severity("critical")
|
|
48
|
+
@tags("category:agent-identity,detection:rule,surface:call-tool,scope:per-agent,posture:deny-default,owasp:asi01")
|
|
49
|
+
@reject_message("Tool execution blocked: this agent invoked a sensitive tool with no accountable human attached. Every privileged agent action must name the person it acts for.")
|
|
50
|
+
forbid (
|
|
51
|
+
principal,
|
|
52
|
+
action == Guardrails::Action::"call_tool",
|
|
53
|
+
resource
|
|
54
|
+
)
|
|
55
|
+
when {
|
|
56
|
+
context has agent_id && context.agent_id != "" &&
|
|
57
|
+
context has tool_is_sensitive && context.tool_is_sensitive == true
|
|
58
|
+
}
|
|
59
|
+
unless {
|
|
60
|
+
context has principal && context.principal has act_sub
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Section 2: Trust level gates the blast radius
|
|
65
|
+
// Start in monitor: a service key is unverified until the agent is adopted.
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
@id("agent-identity.block-unverified-agent-sensitive-tools")
|
|
69
|
+
@name("Block unverified agents from sensitive tools")
|
|
70
|
+
@description("Blocks call_tool when agent_trust_level is unverified and the tool is sensitive or dangerous.")
|
|
71
|
+
@severity("critical")
|
|
72
|
+
@tags("category:agent-identity,detection:rule,surface:call-tool,scope:per-agent,posture:deny-default,owasp:asi01")
|
|
73
|
+
@reject_message("Tool execution blocked: unverified agents may not call sensitive or dangerous tools. Register and adopt the agent to raise its trust level.")
|
|
74
|
+
forbid (
|
|
75
|
+
principal,
|
|
76
|
+
action == Guardrails::Action::"call_tool",
|
|
77
|
+
resource
|
|
78
|
+
)
|
|
79
|
+
when {
|
|
80
|
+
context has agent_trust_level && context.agent_trust_level == "unverified" &&
|
|
81
|
+
(
|
|
82
|
+
(context has tool_is_sensitive && context.tool_is_sensitive == true) ||
|
|
83
|
+
(context has tool_category && context.tool_category == "dangerous")
|
|
84
|
+
)
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Section 3: Autonomous agents get a higher bar
|
|
89
|
+
// Nobody watches an autonomous agent in real time, so a sensitive tool call
|
|
90
|
+
// from one requires first-party trust rather than merely "not unverified".
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
@id("agent-identity.restrict-autonomous-agent-sensitive-tools")
|
|
94
|
+
@name("Block non-first-party autonomous agent tool use")
|
|
95
|
+
@description("Blocks call_tool on a sensitive tool when agent_type is autonomous unless agent_trust_level is first_party.")
|
|
96
|
+
@severity("high")
|
|
97
|
+
@tags("category:agent-identity,threat:escalation,detection:rule,surface:call-tool,scope:per-agent,owasp:asi04")
|
|
98
|
+
@reject_message("Tool execution blocked: autonomous agents must be first-party to call sensitive tools. No human is in the loop to catch a mistake.")
|
|
99
|
+
forbid (
|
|
100
|
+
principal,
|
|
101
|
+
action == Guardrails::Action::"call_tool",
|
|
102
|
+
resource
|
|
103
|
+
)
|
|
104
|
+
when {
|
|
105
|
+
context has agent_type && context.agent_type == "autonomous" &&
|
|
106
|
+
context has tool_is_sensitive && context.tool_is_sensitive == true
|
|
107
|
+
}
|
|
108
|
+
unless {
|
|
109
|
+
context has agent_trust_level && context.agent_trust_level == "first_party"
|
|
110
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// =============================================================================
|
|
2
|
+
// Multi-Turn Trajectory Escalation
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// Blocks a conversation whose TRAJECTORY is an attack, even when no single
|
|
5
|
+
// message is. A crescendo attack never sends a message alarming enough to
|
|
6
|
+
// block on its own; it walks the model there in small, plausible steps and
|
|
7
|
+
// asks for the payoff in a sentence that would pass any single-message filter.
|
|
8
|
+
//
|
|
9
|
+
// Shield scores every turn twice and exposes both scores separately:
|
|
10
|
+
// - injection_pulse_score / jailbreak_pulse_score — the single-turn
|
|
11
|
+
// classifier. Sees only the current message.
|
|
12
|
+
// - injection_deep_context_score / jailbreak_deep_context_score — the
|
|
13
|
+
// multi-turn model, which carries hidden state across turns keyed on
|
|
14
|
+
// session_id. Sees the whole conversation.
|
|
15
|
+
//
|
|
16
|
+
// Because they are separate keys, a rule can fire on the GAP between them:
|
|
17
|
+
// high trajectory score, low message score. In plain language, "the history
|
|
18
|
+
// is an attack and this message is not" — a condition a filter that only ever
|
|
19
|
+
// has one number cannot express. Section 1 is that rule. Section 2 is a safety
|
|
20
|
+
// net for a trajectory blatant enough to stand on its own.
|
|
21
|
+
//
|
|
22
|
+
// Requires a stable session_id on every request in the conversation. With no
|
|
23
|
+
// session_id there is no threaded state, multi_turn_detection is false, and
|
|
24
|
+
// none of these rules can fire.
|
|
25
|
+
//
|
|
26
|
+
// Detection layers:
|
|
27
|
+
// - injection (single-turn classifier, always available)
|
|
28
|
+
// - deepcontext (multi-turn model, always available)
|
|
29
|
+
//
|
|
30
|
+
// Context keys consumed:
|
|
31
|
+
// - multi_turn_detection: Bool — true only when threaded state was used
|
|
32
|
+
// - injection_deep_context_score, injection_pulse_score: Long (0-100)
|
|
33
|
+
// - jailbreak_deep_context_score, jailbreak_pulse_score: Long (0-100)
|
|
34
|
+
//
|
|
35
|
+
// Compliance:
|
|
36
|
+
// - OWASP LLM01, OWASP LLM02, MITRE ATLAS AML.T0051, AML.T0054
|
|
37
|
+
//
|
|
38
|
+
// Category: security
|
|
39
|
+
// Namespace: Guardrails
|
|
40
|
+
// =============================================================================
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Section 1: Trajectory/turn divergence
|
|
44
|
+
// The conversation scores as an attack; this message does not.
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
@id("security.block-trajectory-injection-divergence")
|
|
48
|
+
@name("Block multi-turn injection the current turn hides")
|
|
49
|
+
@description("Blocks process_prompt and process_response when threaded state is in use, injection_deep_context_score >= 60, and injection_pulse_score <= 39.")
|
|
50
|
+
@severity("critical")
|
|
51
|
+
@tags("category:security,threat:injection,detection:ml,surface:process-prompt,surface:process-response,scope:multi-turn,owasp:llm01,mitre:atlas-t0051")
|
|
52
|
+
@reject_message("Prompt blocked: this conversation is a progressive prompt-injection attempt. No single message crossed the line; the sequence did.")
|
|
53
|
+
forbid (
|
|
54
|
+
principal,
|
|
55
|
+
action in [Guardrails::Action::"process_prompt", Guardrails::Action::"process_response"],
|
|
56
|
+
resource
|
|
57
|
+
)
|
|
58
|
+
when {
|
|
59
|
+
context has multi_turn_detection && context.multi_turn_detection == true &&
|
|
60
|
+
context has injection_deep_context_score && context.injection_deep_context_score >= 60 &&
|
|
61
|
+
context has injection_pulse_score && context.injection_pulse_score <= 39
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
@id("security.block-trajectory-jailbreak-divergence")
|
|
65
|
+
@name("Block multi-turn jailbreak the current turn hides")
|
|
66
|
+
@description("Blocks process_prompt and process_response when threaded state is in use, jailbreak_deep_context_score >= 60, and jailbreak_pulse_score <= 39.")
|
|
67
|
+
@severity("critical")
|
|
68
|
+
@tags("category:security,threat:jailbreak,detection:ml,surface:process-prompt,surface:process-response,scope:multi-turn,owasp:llm02,mitre:atlas-t0054")
|
|
69
|
+
@reject_message("Prompt blocked: this conversation is a progressive jailbreak. No single message crossed the line; the sequence did.")
|
|
70
|
+
forbid (
|
|
71
|
+
principal,
|
|
72
|
+
action in [Guardrails::Action::"process_prompt", Guardrails::Action::"process_response"],
|
|
73
|
+
resource
|
|
74
|
+
)
|
|
75
|
+
when {
|
|
76
|
+
context has multi_turn_detection && context.multi_turn_detection == true &&
|
|
77
|
+
context has jailbreak_deep_context_score && context.jailbreak_deep_context_score >= 60 &&
|
|
78
|
+
context has jailbreak_pulse_score && context.jailbreak_pulse_score <= 39
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// Section 2: Blatant trajectory, regardless of the message score
|
|
83
|
+
// Covers the band between Section 1 and the default single-turn thresholds.
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
@id("security.block-trajectory-injection-high")
|
|
87
|
+
@name("Block high-confidence multi-turn injection")
|
|
88
|
+
@description("Blocks process_prompt and process_response when threaded state is in use and injection_deep_context_score >= 75.")
|
|
89
|
+
@severity("high")
|
|
90
|
+
@tags("category:security,threat:injection,detection:ml,surface:process-prompt,surface:process-response,scope:multi-turn,owasp:llm01")
|
|
91
|
+
@reject_message("Prompt blocked: the multi-turn model scored this conversation as a prompt-injection attempt with high confidence.")
|
|
92
|
+
forbid (
|
|
93
|
+
principal,
|
|
94
|
+
action in [Guardrails::Action::"process_prompt", Guardrails::Action::"process_response"],
|
|
95
|
+
resource
|
|
96
|
+
)
|
|
97
|
+
when {
|
|
98
|
+
context has multi_turn_detection && context.multi_turn_detection == true &&
|
|
99
|
+
context has injection_deep_context_score && context.injection_deep_context_score >= 75
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
@id("security.block-trajectory-jailbreak-high")
|
|
103
|
+
@name("Block high-confidence multi-turn jailbreak")
|
|
104
|
+
@description("Blocks process_prompt and process_response when threaded state is in use and jailbreak_deep_context_score >= 75.")
|
|
105
|
+
@severity("high")
|
|
106
|
+
@tags("category:security,threat:jailbreak,detection:ml,surface:process-prompt,surface:process-response,scope:multi-turn,owasp:llm02")
|
|
107
|
+
@reject_message("Prompt blocked: the multi-turn model scored this conversation as a jailbreak attempt with high confidence.")
|
|
108
|
+
forbid (
|
|
109
|
+
principal,
|
|
110
|
+
action in [Guardrails::Action::"process_prompt", Guardrails::Action::"process_response"],
|
|
111
|
+
resource
|
|
112
|
+
)
|
|
113
|
+
when {
|
|
114
|
+
context has multi_turn_detection && context.multi_turn_detection == true &&
|
|
115
|
+
context has jailbreak_deep_context_score && context.jailbreak_deep_context_score >= 75
|
|
116
|
+
};
|