@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.
@@ -3894,22 +3894,77 @@ when {
3894
3894
  const GUARDRAILS_AGENT_IDENTITY_DUAL_ATTRIBUTION_CEDAR = `// =============================================================================
3895
3895
  // Dual Attribution
3896
3896
  // =============================================================================
3897
- // Blocks privileged agent actions that cannot be attributed to a human. "Which
3898
- // agent did this?" is half an answer; the other half is "on whose behalf?" An
3899
- // agent is not an accountable party — the person who pointed it at the work
3900
- // is. Shield projects both sides of that pair, so the requirement can be a
3901
- // policy rather than a reporting convention nobody enforces.
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\` record
3906
- // — its \`act_sub\` field names the party the agent is acting for.
3907
- //
3908
- // Deploy in MONITOR mode first. Section 2 blocks unverified agents, and a
3909
- // service key authenticates as unverified until the agent is registered and
3910
- // adopted in Studio. Monitor records what each rule would have blocked on
3911
- // every event without blocking anything; register the agent, then switch to
3912
- // enforce.
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 — act_sub names the accountable human
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 unattributed privileged action
3933
- // \`unless\` because the rule must fire when attribution is ABSENT, and an
3934
- // absent field cannot be compared — only tested for.
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.require-principal-for-sensitive-tools")
3938
- @name("Block sensitive agent tools without a principal")
3939
- @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.")
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,scope:per-agent,posture:deny-default,owasp:asi01")
3942
- @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.")
3943
- forbid (
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
- unless {
3953
- context has principal && context.principal has act_sub
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: Autonomous agents get a higher bar
3982
- // Nobody watches an autonomous agent in real time, so a sensitive tool call
3983
- // from one requires first-party trust rather than merely "not unverified".
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.restrict-autonomous-agent-sensitive-tools")
3987
- @name("Block non-first-party autonomous agent tool use")
3988
- @description("Blocks call_tool on a sensitive tool when agent_type is autonomous unless agent_trust_level is first_party.")
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,threat:escalation,detection:rule,surface:call-tool,scope:per-agent,owasp:asi04")
3991
- @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.")
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
- context has agent_type && context.agent_type == "autonomous" &&
3999
- context has tool_is_sensitive && context.tool_is_sensitive == true
4000
- }
4001
- unless {
4002
- context has agent_trust_level && context.agent_trust_level == "first_party"
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: '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.',
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": "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.",
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';