@highflame/policy 2.2.45 → 2.4.0
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/schema.cedarschema +22 -0
- package/_schemas/ai_gateway/schema.cedarschema +49 -0
- package/_schemas/ai_gateway/templates/single_use_sensitive_tools.cedar +54 -0
- package/_schemas/ai_gateway/templates/templates.json +37 -0
- package/_schemas/ai_gateway/templates/unadopted_agents.cedar +64 -0
- package/_schemas/guardrails/schema.cedarschema +21 -0
- package/_schemas/guardrails/templates/dual_attribution.cedar +143 -48
- package/_schemas/guardrails/templates/single_use_sensitive_tools.cedar +54 -0
- package/_schemas/guardrails/templates/templates.json +34 -2
- package/_schemas/guardrails/templates/unadopted_agents.cedar +64 -0
- package/dist/aarm-annotation.d.ts +58 -4
- package/dist/aarm-annotation.js +177 -4
- package/dist/aarm-annotations.gen.js +85 -1
- package/dist/ai_gateway-defaults.gen.d.ts +1 -1
- package/dist/ai_gateway-defaults.gen.js +176 -0
- package/dist/annotations.d.ts +3 -2
- package/dist/annotations.js +6 -2
- package/dist/guardrails-defaults.gen.js +309 -44
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/service-schemas.gen.d.ts +3 -3
- package/dist/service-schemas.gen.js +92 -0
- package/dist/step-up-role-conflict.d.ts +49 -0
- package/dist/step-up-role-conflict.js +117 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.js +3 -0
- package/package.json +1 -1
|
@@ -3894,22 +3894,77 @@ when {
|
|
|
3894
3894
|
const GUARDRAILS_AGENT_IDENTITY_DUAL_ATTRIBUTION_CEDAR = `// =============================================================================
|
|
3895
3895
|
// Dual Attribution
|
|
3896
3896
|
// =============================================================================
|
|
3897
|
-
//
|
|
3898
|
-
// agent did this?" is half an answer; the other half
|
|
3899
|
-
//
|
|
3900
|
-
//
|
|
3901
|
-
//
|
|
3897
|
+
// Routes privileged agent actions that no human answers for to explicit
|
|
3898
|
+
// human approval. "Which agent did this?" is half an answer; the other half
|
|
3899
|
+
// is "on whose behalf?" When an agent acts for a person, that person is the
|
|
3900
|
+
// accountable party. When it acts for nobody (an autonomous agent, a
|
|
3901
|
+
// workload, an api_key-rooted token), its sensitive tool calls run only when
|
|
3902
|
+
// someone explicitly authorizes them, never silently and never by default.
|
|
3902
3903
|
//
|
|
3903
3904
|
// The agent comes from the authenticated identity, never from the request
|
|
3904
3905
|
// body: agent_id, agent_type, agent_trust_level, agent_framework. The human
|
|
3905
|
-
// comes from the identity claims on the credential, as the \`principal\`
|
|
3906
|
-
//
|
|
3907
|
-
//
|
|
3908
|
-
//
|
|
3909
|
-
//
|
|
3910
|
-
//
|
|
3911
|
-
//
|
|
3912
|
-
//
|
|
3906
|
+
// comes from the identity claims on the credential, as the \`principal\`
|
|
3907
|
+
// record. Its \`act_sub\` field names the human the agent acts for, and is
|
|
3908
|
+
// absent when there is none: legacy tokens carry the delegating parent (or
|
|
3909
|
+
// the api key's creator), while rfc8693 tokens carry it only when the
|
|
3910
|
+
// delegation chain is rooted in a user. Autonomous, workload and
|
|
3911
|
+
// api_key-rooted rfc8693 tokens have no act_sub.
|
|
3912
|
+
//
|
|
3913
|
+
// How an agent call with no acting human is authorized: it needs an
|
|
3914
|
+
// explicit approval OR an explicit grant.
|
|
3915
|
+
// 1. Approval. Section 1 permits the call but suspends it for step-up
|
|
3916
|
+
// approval (@step_up_required, AARM R4, CAP-ENF-004): Shield issues an
|
|
3917
|
+
// OpenID CIBA challenge and the call runs only after an approver with
|
|
3918
|
+
// the named AuthN role approves it; the timeout denies it (fail-closed).
|
|
3919
|
+
// 2. Grants. Allow/deny rows in Studio's agent access table compile to
|
|
3920
|
+
// permits (grants) and forbids (restrictions) that Shield evaluates per
|
|
3921
|
+
// agent. A restriction always denies. Section 1 carries
|
|
3922
|
+
// @step_up_unless_granted, so when an allow grant explicitly matches
|
|
3923
|
+
// the call under an ENFORCING access posture, Shield waives the
|
|
3924
|
+
// challenge and the call runs (recorded as step_up_waived_by_grant).
|
|
3925
|
+
// Under the default shadow posture a grant is only observed, so it
|
|
3926
|
+
// waives nothing and approval decides. With no matching grant the call
|
|
3927
|
+
// is challenged under either posture, not denied for lack of a grant.
|
|
3928
|
+
// The waiver needs every step-up policy matching the call to carry
|
|
3929
|
+
// @step_up_unless_granted: another step-up policy without it still
|
|
3930
|
+
// challenges. Shield releases older than the grant waiver ignore the
|
|
3931
|
+
// annotation: there a no-human call needs the approval even when granted,
|
|
3932
|
+
// and under an enforcing posture an ungranted one is denied outright.
|
|
3933
|
+
//
|
|
3934
|
+
// A call carrying an acting human (act_sub present) is untouched by
|
|
3935
|
+
// Section 1, unless the agent is autonomous and not first_party.
|
|
3936
|
+
//
|
|
3937
|
+
// Customize Section 1 before deploying:
|
|
3938
|
+
// - @step_up_unless_granted: remove it to require the approval even for
|
|
3939
|
+
// calls an allow grant covers (grant AND approval).
|
|
3940
|
+
// - role: must exist in authn.roles ("security_oncall" is a placeholder;
|
|
3941
|
+
// unknown roles reject the policy at deploy time). Another step-up
|
|
3942
|
+
// policy matching the same call must name the same role, or Shield
|
|
3943
|
+
// denies the call (one approval cannot satisfy two roles).
|
|
3944
|
+
// - timeout_seconds: this template sets 3600 (1h); when omitted the
|
|
3945
|
+
// default is 86400 (24h). Bounds [60, 604800].
|
|
3946
|
+
//
|
|
3947
|
+
// Deploy this policy in ENFORCE mode. Section 1's approval gate exists only
|
|
3948
|
+
// in enforce mode: in monitor or alert mode Shield issues no challenge and
|
|
3949
|
+
// passes Section 1's Allow through unchanged. Section 1 is a permit, so in
|
|
3950
|
+
// a policy set without the baseline permit (organization.permit-baseline)
|
|
3951
|
+
// it is what lifts the implicit deny for these calls. In enforce mode that
|
|
3952
|
+
// lift is gated on approval (or a grant); in monitor mode the same calls
|
|
3953
|
+
// would be allowed with no approval at all, a widening, not an observation.
|
|
3954
|
+
//
|
|
3955
|
+
// Only Section 2 is safe to observe first. It blocks unverified agents, and
|
|
3956
|
+
// a service key authenticates as unverified until the agent is registered
|
|
3957
|
+
// and adopted in Studio. Either register and adopt the agents before
|
|
3958
|
+
// deploying, or move Section 2 into its own policy, deploy that one in
|
|
3959
|
+
// MONITOR mode to see what it would block, and switch it to enforce once
|
|
3960
|
+
// the agents are adopted. Mode is per policy, so never put this whole
|
|
3961
|
+
// template in monitor to trial Section 2.
|
|
3962
|
+
//
|
|
3963
|
+
// Change note: Section 3 (autonomous agents must be first_party) used to be
|
|
3964
|
+
// a hard block. It is now folded into Section 1, so a non-first-party
|
|
3965
|
+
// autonomous agent's sensitive call is routed to approval (or a grant)
|
|
3966
|
+
// instead of being denied. To restore the hard block, add the forbid kept
|
|
3967
|
+
// in the Section 3 comment below; it overrides approvals and grants.
|
|
3913
3968
|
//
|
|
3914
3969
|
// Detection layers:
|
|
3915
3970
|
// - agent identity (authentication layer, always available)
|
|
@@ -3917,7 +3972,8 @@ const GUARDRAILS_AGENT_IDENTITY_DUAL_ATTRIBUTION_CEDAR = `// ===================
|
|
|
3917
3972
|
//
|
|
3918
3973
|
// Context keys consumed:
|
|
3919
3974
|
// - agent_id, agent_type, agent_trust_level: String
|
|
3920
|
-
// - principal: record
|
|
3975
|
+
// - principal: record; act_sub names the acting human, absent when the
|
|
3976
|
+
// agent acts for nobody
|
|
3921
3977
|
// - tool_is_sensitive: Bool
|
|
3922
3978
|
// - tool_category: String
|
|
3923
3979
|
//
|
|
@@ -3929,33 +3985,69 @@ const GUARDRAILS_AGENT_IDENTITY_DUAL_ATTRIBUTION_CEDAR = `// ===================
|
|
|
3929
3985
|
// =============================================================================
|
|
3930
3986
|
|
|
3931
3987
|
// ---------------------------------------------------------------------------
|
|
3932
|
-
// Section 1: No
|
|
3933
|
-
//
|
|
3934
|
-
//
|
|
3988
|
+
// Section 1: No-human privileged action requires approval
|
|
3989
|
+
// ENFORCE mode only: in monitor mode this permit allows the call with no
|
|
3990
|
+
// approval (see the header).
|
|
3991
|
+
// Matches when attribution is ABSENT (an absent field cannot be compared,
|
|
3992
|
+
// only tested for), and also for autonomous agents that are not first_party
|
|
3993
|
+
// even when a human is attached: nobody watches an autonomous agent in real
|
|
3994
|
+
// time, and a legacy api_key token names the key's creator as act_sub
|
|
3995
|
+
// whether or not that person is directing the call.
|
|
3996
|
+
//
|
|
3997
|
+
// Cedar is deny-overrides and Shield issues step-up only when the decision
|
|
3998
|
+
// is Allow, so this permit must not coexist with a forbid on the same calls:
|
|
3999
|
+
// the forbid would deny every one of them and no approval would be asked.
|
|
4000
|
+
// To hard-block instead of gating on approval, replace this rule with:
|
|
4001
|
+
//
|
|
4002
|
+
// @id("agent-identity.require-principal-for-sensitive-tools")
|
|
4003
|
+
// @name("Block sensitive agent tools without a principal")
|
|
4004
|
+
// @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.")
|
|
4005
|
+
// @severity("critical")
|
|
4006
|
+
// @tags("category:agent-identity,detection:rule,scope:per-agent,surface:call-tool,owasp:asi01,posture:deny-default")
|
|
4007
|
+
// @reject_message("Tool execution blocked: this agent invoked a sensitive tool with no accountable human attached.")
|
|
4008
|
+
// forbid (
|
|
4009
|
+
// principal,
|
|
4010
|
+
// action == Guardrails::Action::"call_tool",
|
|
4011
|
+
// resource
|
|
4012
|
+
// )
|
|
4013
|
+
// when {
|
|
4014
|
+
// context has agent_id && context.agent_id != "" &&
|
|
4015
|
+
// context has tool_is_sensitive && context.tool_is_sensitive == true
|
|
4016
|
+
// }
|
|
4017
|
+
// unless {
|
|
4018
|
+
// context has principal && context.principal has act_sub
|
|
4019
|
+
// };
|
|
3935
4020
|
// ---------------------------------------------------------------------------
|
|
3936
4021
|
|
|
3937
|
-
@id("agent-identity.
|
|
3938
|
-
@name("
|
|
3939
|
-
@description("
|
|
4022
|
+
@id("agent-identity.step-up-sensitive-tools-without-principal")
|
|
4023
|
+
@name("Permit no-human sensitive agent tools with step-up approval")
|
|
4024
|
+
@description("Permits call_tool on a sensitive tool by an agent with no acting human (no act_sub), or an autonomous agent that is not first_party, but suspends it until an approver with the configured AuthN role approves, unless an enforcing allow grant explicitly covers the call.")
|
|
3940
4025
|
@severity("critical")
|
|
3941
|
-
@tags("category:agent-identity,detection:rule,surface:call-tool,
|
|
3942
|
-
@
|
|
3943
|
-
|
|
4026
|
+
@tags("category:agent-identity,detection:rule,scope:per-agent,surface:call-tool,posture:deny-default,owasp:asi01,step-up")
|
|
4027
|
+
@step_up_required("role=security_oncall,timeout_seconds=3600")
|
|
4028
|
+
@step_up_unless_granted
|
|
4029
|
+
permit (
|
|
3944
4030
|
principal,
|
|
3945
4031
|
action == Guardrails::Action::"call_tool",
|
|
3946
4032
|
resource
|
|
3947
4033
|
)
|
|
3948
4034
|
when {
|
|
3949
4035
|
context has agent_id && context.agent_id != "" &&
|
|
3950
|
-
context has tool_is_sensitive && context.tool_is_sensitive == true
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
4036
|
+
context has tool_is_sensitive && context.tool_is_sensitive == true &&
|
|
4037
|
+
(
|
|
4038
|
+
!(context has principal && context.principal has act_sub) ||
|
|
4039
|
+
(
|
|
4040
|
+
context has agent_type && context.agent_type == "autonomous" &&
|
|
4041
|
+
!(context has agent_trust_level && context.agent_trust_level == "first_party")
|
|
4042
|
+
)
|
|
4043
|
+
)
|
|
3954
4044
|
};
|
|
3955
4045
|
|
|
3956
4046
|
// ---------------------------------------------------------------------------
|
|
3957
4047
|
// Section 2: Trust level gates the blast radius
|
|
3958
4048
|
// Start in monitor: a service key is unverified until the agent is adopted.
|
|
4049
|
+
// Unverified agents are blocked outright, approval or not: registration and
|
|
4050
|
+
// adoption come before any per-call authorization.
|
|
3959
4051
|
// ---------------------------------------------------------------------------
|
|
3960
4052
|
|
|
3961
4053
|
@id("agent-identity.block-unverified-agent-sensitive-tools")
|
|
@@ -3978,28 +4070,151 @@ when {
|
|
|
3978
4070
|
};
|
|
3979
4071
|
|
|
3980
4072
|
// ---------------------------------------------------------------------------
|
|
3981
|
-
// Section 3:
|
|
3982
|
-
//
|
|
3983
|
-
//
|
|
4073
|
+
// Section 3 (folded into Section 1): autonomous agents need first-party trust
|
|
4074
|
+
// This template used to forbid sensitive tool calls from autonomous agents
|
|
4075
|
+
// that are not first-party. A forbid would deny the very calls Section 1
|
|
4076
|
+
// routes to approval, so the condition now lives in Section 1 and those
|
|
4077
|
+
// calls require approval instead of being blocked. To hard-block them anyway
|
|
4078
|
+
// (overriding approvals and grants), add:
|
|
4079
|
+
//
|
|
4080
|
+
// @id("agent-identity.restrict-autonomous-agent-sensitive-tools")
|
|
4081
|
+
// @name("Block non-first-party autonomous agent tool use")
|
|
4082
|
+
// @description("Blocks call_tool on a sensitive tool when agent_type is autonomous unless agent_trust_level is first_party.")
|
|
4083
|
+
// @severity("high")
|
|
4084
|
+
// @tags("category:agent-identity,threat:escalation,detection:rule,surface:call-tool,scope:per-agent,owasp:asi04")
|
|
4085
|
+
// @reject_message("Tool execution blocked: autonomous agents must be first-party to call sensitive tools.")
|
|
4086
|
+
// forbid (
|
|
4087
|
+
// principal,
|
|
4088
|
+
// action == Guardrails::Action::"call_tool",
|
|
4089
|
+
// resource
|
|
4090
|
+
// )
|
|
4091
|
+
// when {
|
|
4092
|
+
// context has agent_type && context.agent_type == "autonomous" &&
|
|
4093
|
+
// context has tool_is_sensitive && context.tool_is_sensitive == true
|
|
4094
|
+
// }
|
|
4095
|
+
// unless {
|
|
4096
|
+
// context has agent_trust_level && context.agent_trust_level == "first_party"
|
|
4097
|
+
// };
|
|
3984
4098
|
// ---------------------------------------------------------------------------
|
|
4099
|
+
`;
|
|
4100
|
+
const GUARDRAILS_AGENT_IDENTITY_BLOCK_UNADOPTED_AGENT_SENSITIVE_TOOLS_CEDAR = `// =============================================================================
|
|
4101
|
+
// Unadopted Agents: No Sensitive Tool Calls
|
|
4102
|
+
// =============================================================================
|
|
4103
|
+
// Agents that an enterprise registers in its own IdP (Entra Agent ID, Okta AI
|
|
4104
|
+
// agents) are discovered by Highflame before anyone adopts them. A discovered
|
|
4105
|
+
// or pending agent has an identity but no owner-reviewed policy, so it should
|
|
4106
|
+
// not reach sensitive tools until it is adopted (status "active").
|
|
4107
|
+
//
|
|
4108
|
+
// \`context.actor.status\` carries the agent lifecycle status. When no actor
|
|
4109
|
+
// record is present the rule falls back to the legacy \`context.principal.status\`
|
|
4110
|
+
// profile. Absence means "unknown", not "active": the has-guards below make a
|
|
4111
|
+
// request with no status fall through to the other rules instead of being
|
|
4112
|
+
// matched here. Any status other than "active" is matched, so suspended and
|
|
4113
|
+
// deactivated agents are blocked too. The comparison is case-sensitive, like
|
|
4114
|
+
// ZeroID's lowercase status values.
|
|
4115
|
+
//
|
|
4116
|
+
// Today this template is forward-looking. ZeroID mints tokens only for active
|
|
4117
|
+
// identities and stamps "active" at mint, and every caller of Shield forwards
|
|
4118
|
+
// that value, so no live path reports another status yet. It takes effect when
|
|
4119
|
+
// a non-active status reaches Shield, as ADR 0042 plans for agents registered
|
|
4120
|
+
// in a customer IdP. The status is a mint-time snapshot: an identity suspended
|
|
4121
|
+
// after its token was minted reads "active" until the token expires.
|
|
4122
|
+
//
|
|
4123
|
+
// \`tool_is_sensitive\` is set by Shield's tool_validator from a fixed list of
|
|
4124
|
+
// tool names (shell, execute_command, write_file, http_post, send_email,
|
|
4125
|
+
// delete_file, run_sql, plus Shield's configured list); other tools are not
|
|
4126
|
+
// sensitive and are not matched.
|
|
4127
|
+
//
|
|
4128
|
+
// This copy covers Guardrails traffic. Gateway traffic (Firehog) is evaluated
|
|
4129
|
+
// in the AIGateway namespace; use the AIGateway copy for it.
|
|
4130
|
+
//
|
|
4131
|
+
// Deploy in MONITOR mode first to see which discovered agents already call
|
|
4132
|
+
// sensitive tools, adopt them, then switch to enforce.
|
|
4133
|
+
//
|
|
4134
|
+
// Context keys consumed:
|
|
4135
|
+
// - actor, principal: records - status is the agent lifecycle status
|
|
4136
|
+
// - tool_is_sensitive: Bool
|
|
4137
|
+
//
|
|
4138
|
+
// Compliance:
|
|
4139
|
+
// - OWASP ASI04
|
|
4140
|
+
//
|
|
4141
|
+
// Category: agent-identity
|
|
4142
|
+
// Namespace: Guardrails
|
|
4143
|
+
// =============================================================================
|
|
3985
4144
|
|
|
3986
|
-
@id("agent-identity.
|
|
3987
|
-
@name("Block
|
|
3988
|
-
@description("Blocks call_tool on a sensitive tool when
|
|
4145
|
+
@id("agent-identity.block-unadopted-agent-sensitive-tools")
|
|
4146
|
+
@name("Block unadopted agents from sensitive tools")
|
|
4147
|
+
@description("Blocks call_tool on a sensitive tool when actor.status (or, without an actor record, principal.status) is present and is not active.")
|
|
3989
4148
|
@severity("high")
|
|
3990
|
-
@tags("category:agent-identity,
|
|
3991
|
-
@reject_message("Tool execution blocked:
|
|
4149
|
+
@tags("category:agent-identity,detection:rule,surface:call-tool,scope:per-agent,posture:deny-default,owasp:asi04")
|
|
4150
|
+
@reject_message("Tool execution blocked: this agent has not been adopted, so it may not call sensitive tools. Adopt the agent to activate it.")
|
|
3992
4151
|
forbid (
|
|
3993
4152
|
principal,
|
|
3994
4153
|
action == Guardrails::Action::"call_tool",
|
|
3995
4154
|
resource
|
|
3996
4155
|
)
|
|
3997
4156
|
when {
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4157
|
+
(
|
|
4158
|
+
(context has actor && context.actor has status && context.actor.status != "active") ||
|
|
4159
|
+
(!(context has actor) && context has principal && context.principal has status &&
|
|
4160
|
+
context.principal.status != "active")
|
|
4161
|
+
) &&
|
|
4162
|
+
context has tool_is_sensitive && context.tool_is_sensitive
|
|
4163
|
+
};
|
|
4164
|
+
`;
|
|
4165
|
+
const GUARDRAILS_AGENT_IDENTITY_PERMIT_SINGLE_USE_SENSITIVE_TOOLS_CEDAR = `// =============================================================================
|
|
4166
|
+
// Single-Use for Sensitive Tools
|
|
4167
|
+
// =============================================================================
|
|
4168
|
+
// Permits sensitive tool calls, but only once per grant. @single_use makes
|
|
4169
|
+
// Shield record the first allow in an atomic consume store and deny any replay
|
|
4170
|
+
// of the same grant ("grant_consumed"). Bare @single_use means per tool: one
|
|
4171
|
+
// allow covers any call to the same action and tool. Use
|
|
4172
|
+
// @single_use("request") instead to bind the allow to one exact set of
|
|
4173
|
+
// arguments.
|
|
4174
|
+
//
|
|
4175
|
+
// @single_use is only valid on a permit. Deny, DEFER and step-up outcomes
|
|
4176
|
+
// never consume. If the consume store is unavailable the request is denied
|
|
4177
|
+
// ("single_use_store_unavailable") unless the operator opted into fail-open.
|
|
4178
|
+
//
|
|
4179
|
+
// Requests that carry no bearer token, such as gateway and internal-service
|
|
4180
|
+
// traffic, have no token id to record the use against. Shield denies every
|
|
4181
|
+
// sensitive call_tool from them ("single_use_unbound"), whatever the
|
|
4182
|
+
// fail-open setting. Until the gateway forwards the caller's token to Shield
|
|
4183
|
+
// (ADR 0041 D16, ADR 0042 D10), that means every sensitive gateway call.
|
|
4184
|
+
//
|
|
4185
|
+
// Try it with request-level monitor mode (guard requests sent with
|
|
4186
|
+
// mode=monitor): single_use.would_deny on each response shows what enforcing
|
|
4187
|
+
// would deny. Per-policy monitor mode does NOT exercise @single_use, because
|
|
4188
|
+
// Shield reads it only from enforcing policies; a monitor-mode copy of this
|
|
4189
|
+
// template reports nothing, and switching it to enforce takes effect at once.
|
|
4190
|
+
//
|
|
4191
|
+
// Adopted alone, this permit also allows every sensitive call_tool (once per
|
|
4192
|
+
// grant). Pair it with the policies that decide which sensitive calls are
|
|
4193
|
+
// allowed at all.
|
|
4194
|
+
//
|
|
4195
|
+
// Context keys consumed:
|
|
4196
|
+
// - tool_is_sensitive: Bool
|
|
4197
|
+
//
|
|
4198
|
+
// Compliance:
|
|
4199
|
+
// - OWASP ASI02
|
|
4200
|
+
//
|
|
4201
|
+
// Category: agent-identity
|
|
4202
|
+
// Namespace: Guardrails
|
|
4203
|
+
// =============================================================================
|
|
4204
|
+
|
|
4205
|
+
@id("agent-identity.permit-single-use-sensitive-tools")
|
|
4206
|
+
@name("Permit sensitive tools once per grant")
|
|
4207
|
+
@description("Permits call_tool on a sensitive tool once per grant.")
|
|
4208
|
+
@severity("medium")
|
|
4209
|
+
@tags("category:agent-identity,surface:call-tool,scope:per-tool,owasp:asi02,single-use")
|
|
4210
|
+
@single_use
|
|
4211
|
+
permit (
|
|
4212
|
+
principal,
|
|
4213
|
+
action == Guardrails::Action::"call_tool",
|
|
4214
|
+
resource
|
|
4215
|
+
)
|
|
4216
|
+
when {
|
|
4217
|
+
context has tool_is_sensitive && context.tool_is_sensitive
|
|
4003
4218
|
};
|
|
4004
4219
|
`;
|
|
4005
4220
|
// =============================================================================
|
|
@@ -4398,11 +4613,29 @@ export const GUARDRAILS_TEMPLATES = [
|
|
|
4398
4613
|
{
|
|
4399
4614
|
id: 'agent-identity.dual-attribution',
|
|
4400
4615
|
name: 'Dual Attribution',
|
|
4401
|
-
description: '
|
|
4616
|
+
description: 'Require human approval or an explicit allow grant for sensitive agent tool calls that no human answers for (no acting human, e.g. autonomous or workload agents); calls with an acting human pass. Customize the approver role. Deploy in enforce mode: the approval gate does not exist in monitor mode. Adopt agents first (a service key is unverified until then), or trial the unverified-agent block as its own monitor-mode policy.',
|
|
4402
4617
|
category: 'agent-identity',
|
|
4403
4618
|
cedarText: GUARDRAILS_AGENT_IDENTITY_DUAL_ATTRIBUTION_CEDAR,
|
|
4404
4619
|
severity: 'critical',
|
|
4405
|
-
tags: ['category:agent-identity', 'detection:rule', 'surface:call-tool', 'scope:per-agent', 'posture:deny-default', 'owasp:asi01'],
|
|
4620
|
+
tags: ['category:agent-identity', 'detection:rule', 'surface:call-tool', 'scope:per-agent', 'posture:deny-default', 'owasp:asi01', 'step-up'],
|
|
4621
|
+
},
|
|
4622
|
+
{
|
|
4623
|
+
id: 'agent-identity.block-unadopted-agent-sensitive-tools',
|
|
4624
|
+
name: 'Unadopted Agents: No Sensitive Tool Calls',
|
|
4625
|
+
description: 'Block sensitive tool calls from agents whose lifecycle status is not active (discovered or pending). Deploy in monitor mode first to see which discovered agents already call sensitive tools.',
|
|
4626
|
+
category: 'agent-identity',
|
|
4627
|
+
cedarText: GUARDRAILS_AGENT_IDENTITY_BLOCK_UNADOPTED_AGENT_SENSITIVE_TOOLS_CEDAR,
|
|
4628
|
+
severity: 'high',
|
|
4629
|
+
tags: ['category:agent-identity', 'detection:rule', 'surface:call-tool', 'scope:per-agent', 'posture:deny-default', 'owasp:asi04'],
|
|
4630
|
+
},
|
|
4631
|
+
{
|
|
4632
|
+
id: 'agent-identity.permit-single-use-sensitive-tools',
|
|
4633
|
+
name: 'Single-Use for Sensitive Tools',
|
|
4634
|
+
description: 'Permit sensitive tool calls once per grant: the first allow is recorded and a replay of the same grant is denied. Bare @single_use means per tool; use @single_use(\"request\") to bind to one set of arguments. Requests without a bearer token (gateway and internal-service traffic, until the gateway forwards the caller token) are denied (single_use_unbound). Try it with request-level mode=monitor: per-policy monitor mode does not exercise @single_use. Adopted alone, it also permits every sensitive call.',
|
|
4635
|
+
category: 'agent-identity',
|
|
4636
|
+
cedarText: GUARDRAILS_AGENT_IDENTITY_PERMIT_SINGLE_USE_SENSITIVE_TOOLS_CEDAR,
|
|
4637
|
+
severity: 'medium',
|
|
4638
|
+
tags: ['category:agent-identity', 'surface:call-tool', 'scope:per-tool', 'owasp:asi02', 'single-use'],
|
|
4406
4639
|
},
|
|
4407
4640
|
];
|
|
4408
4641
|
// =============================================================================
|
|
@@ -5048,7 +5281,7 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
|
|
|
5048
5281
|
{
|
|
5049
5282
|
"id": "agent-identity.dual-attribution",
|
|
5050
5283
|
"name": "Dual Attribution",
|
|
5051
|
-
"description": "
|
|
5284
|
+
"description": "Require human approval or an explicit allow grant for sensitive agent tool calls that no human answers for (no acting human, e.g. autonomous or workload agents); calls with an acting human pass. Customize the approver role. Deploy in enforce mode: the approval gate does not exist in monitor mode. Adopt agents first (a service key is unverified until then), or trial the unverified-agent block as its own monitor-mode policy.",
|
|
5052
5285
|
"category": "agent-identity",
|
|
5053
5286
|
"file": "dual_attribution.cedar",
|
|
5054
5287
|
"severity": "critical",
|
|
@@ -5058,7 +5291,39 @@ export const GUARDRAILS_TEMPLATES_JSON = `{
|
|
|
5058
5291
|
"surface:call-tool",
|
|
5059
5292
|
"scope:per-agent",
|
|
5060
5293
|
"posture:deny-default",
|
|
5061
|
-
"owasp:asi01"
|
|
5294
|
+
"owasp:asi01",
|
|
5295
|
+
"step-up"
|
|
5296
|
+
]
|
|
5297
|
+
},
|
|
5298
|
+
{
|
|
5299
|
+
"id": "agent-identity.block-unadopted-agent-sensitive-tools",
|
|
5300
|
+
"name": "Unadopted Agents: No Sensitive Tool Calls",
|
|
5301
|
+
"description": "Block sensitive tool calls from agents whose lifecycle status is not active (discovered or pending). Deploy in monitor mode first to see which discovered agents already call sensitive tools.",
|
|
5302
|
+
"category": "agent-identity",
|
|
5303
|
+
"file": "unadopted_agents.cedar",
|
|
5304
|
+
"severity": "high",
|
|
5305
|
+
"tags": [
|
|
5306
|
+
"category:agent-identity",
|
|
5307
|
+
"detection:rule",
|
|
5308
|
+
"surface:call-tool",
|
|
5309
|
+
"scope:per-agent",
|
|
5310
|
+
"posture:deny-default",
|
|
5311
|
+
"owasp:asi04"
|
|
5312
|
+
]
|
|
5313
|
+
},
|
|
5314
|
+
{
|
|
5315
|
+
"id": "agent-identity.permit-single-use-sensitive-tools",
|
|
5316
|
+
"name": "Single-Use for Sensitive Tools",
|
|
5317
|
+
"description": "Permit sensitive tool calls once per grant: the first allow is recorded and a replay of the same grant is denied. Bare @single_use means per tool; use @single_use(\\"request\\") to bind to one set of arguments. Requests without a bearer token (gateway and internal-service traffic, until the gateway forwards the caller token) are denied (single_use_unbound). Try it with request-level mode=monitor: per-policy monitor mode does not exercise @single_use. Adopted alone, it also permits every sensitive call.",
|
|
5318
|
+
"category": "agent-identity",
|
|
5319
|
+
"file": "single_use_sensitive_tools.cedar",
|
|
5320
|
+
"severity": "medium",
|
|
5321
|
+
"tags": [
|
|
5322
|
+
"category:agent-identity",
|
|
5323
|
+
"surface:call-tool",
|
|
5324
|
+
"scope:per-tool",
|
|
5325
|
+
"owasp:asi02",
|
|
5326
|
+
"single-use"
|
|
5062
5327
|
]
|
|
5063
5328
|
}
|
|
5064
5329
|
]
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from './category-layers.gen.js';
|
|
|
6
6
|
export * from './decision-effects.gen.js';
|
|
7
7
|
export * from './aarm-annotations.gen.js';
|
|
8
8
|
export * from './aarm-annotation.js';
|
|
9
|
+
export * from './step-up-role-conflict.js';
|
|
9
10
|
export * from './privilege-catalog.gen.js';
|
|
10
11
|
export * from './engine.js';
|
|
11
12
|
export * from './builder.js';
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,9 @@ export * from './aarm-annotations.gen.js';
|
|
|
17
17
|
// AARM annotation parser/validator (typed parse + fail-closed validation,
|
|
18
18
|
// parity with Go's aarm_annotation.go).
|
|
19
19
|
export * from './aarm-annotation.js';
|
|
20
|
+
// Policy-set lint: warns when step-up policies naming different approver
|
|
21
|
+
// roles can match the same call (Shield denies with step_up_internal_error).
|
|
22
|
+
export * from './step-up-role-conflict.js';
|
|
20
23
|
// Role -> capability privilege catalog (privilege_scope minted into
|
|
21
24
|
// the trusted-service JWT; checked by role-conditioned Cedar policies).
|
|
22
25
|
export * from './privilege-catalog.gen.js';
|