gcp-acm-iam-grant-to-perimeter-control
Identity that can grant policyAdmin at access policy scope gains full perimeter control.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'principal': None}
CanGrantPermission {'target': None}
{'accesspolicy': None} Contains {'perimeter': None}
where
scope_of(?principal,
CanGrantPermission) == ?accesspolicy (organization or access policy)
node_class(?perimeter) == ConditionalPolicy
provider_type(?perimeter) == 'google.identity.accesscontextmanager.v1.ServicePerimeter'
?principal == ?target (self-grant path; principal can grant policyAdmin to itself)
emit
| source type | Identity |
|---|---|
| target type | ConditionalPolicy |
| source | ?principal |
| target | ?perimeter |
| conditions | trust_relationship iam_permission |
| state logic | CONDITIONAL(role_grant_exercised) — the principal must actually grant itself or another principal policyAdmin and then assume/federate as that identity. ACTIVE when the grant has been exercised and the principal holds policyAdmin. |
Narrative
{principal.name} can grant itself or others roles/accesscontextmanager.policyAdmin by setting the IAM policy on the access policy. policyAdmin includes servicePerimeters.update and accessLevels.update, enabling full modification of every perimeter {perimeter.name} under that policy. Cite can-control.yaml and hierarchy-chains.yaml guardrail-removal-upgrades-blocked for downstream BLOCKED -> CONDITIONAL(guardrail_removed) upgrades.
Raw rule rules/derived/gcp/accesscontextmanager.yaml
id: gcp-acm-iam-grant-to-perimeter-control
emits: CanModifyPolicy
description: Identity that can grant policyAdmin at access policy scope gains full perimeter control.
applies_to:
- gcp
match:
- - principal: null
- CanGrantPermission
- target: null
- - accesspolicy: null
- Contains
- perimeter: null
where:
- scope_of(?principal, CanGrantPermission) == ?accesspolicy (organization or access policy)
- node_class(?perimeter) == ConditionalPolicy
- provider_type(?perimeter) == 'google.identity.accesscontextmanager.v1.ServicePerimeter'
- ?principal == ?target (self-grant path; principal can grant policyAdmin to itself)
emit:
source_type: Identity
target_type: ConditionalPolicy
source: ?principal
target: ?perimeter
state_logic: "CONDITIONAL(role_grant_exercised) \u2014 the principal must actually grant itself or another\
\ principal policyAdmin and then assume/federate as that identity. ACTIVE when the grant has been\
\ exercised and the principal holds policyAdmin."
conditions:
- trust_relationship
- iam_permission
confidence: 0.85
derived_from:
- gcp-acm-can-set-iam-policy (CanGrantPermission at AccessPolicy)
- identity-escalation.yaml gcp-set-iam-policy-resource (policyAdmin role definition)
- hierarchy-chains.yaml admin-inherits-down-contains (perimeter under access policy)
false_positive_note: This rule is only traversable if the principal actually exercises the grant to
assign itself/others policyAdmin. If a deny policy prevents the grant or the role assignment, this
edge is BLOCKED.
narrative: '{principal.name} can grant itself or others roles/accesscontextmanager.policyAdmin by setting
the IAM policy on the access policy. policyAdmin includes servicePerimeters.update and accessLevels.update,
enabling full modification of every perimeter {perimeter.name} under that policy. Cite can-control.yaml
and hierarchy-chains.yaml guardrail-removal-upgrades-blocked for downstream BLOCKED -> CONDITIONAL(guardrail_removed)
upgrades.'