gcp-acm-iam-grant-to-perimeter-control

Identity that can grant policyAdmin at access policy scope gains full perimeter control.

derived gcp emits CanModifyPolicy

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 typeIdentity
target typeConditionalPolicy
source?principal
target?perimeter
conditionstrust_relationship iam_permission
state logicCONDITIONAL(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.'
move · open · esc close