guardrail-removal-upgrades-blocked
A principal who can modify or escape a guardrail (SCP / Org Policy / deny assignment) upgrades the edges that guardrail was suppressing from BLOCKED to their underlying state.
derived
any cloud
emits
?cap
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_class(?guardrail) == Policy
node_type(?guardrail) in [
ServiceControlPolicy, OrganizationPolicy, ConditionalPolicy] # AWS SCP / GCP org policy / Azure deny assignment
state_of(match[1]) == BLOCKED
blocked_by(match[1]) == ?guardrail # THIS guardrail is what suppresses THIS edge — not merely present in the account
optional
Present → upgrade the emitted state; absent → downgrade (not a hard requirement).
{'principal': None}
CanModifyConfiguration {'scope_binding': None}
emit
| source | ?x |
|---|---|
| target | ?y |
| conditions | guardrail_removed |
| state logic | UPGRADE (re-emit match[1] in place): the suppressed edge is re-emitted with its UNDERLYING state — the state it would have had absent ?guardrail (ACTIVE, or CONDITIONAL with whatever non-guardrail gate remains). The upgrade is itself CONDITIONAL(guardrail_removed): it is only realizable AFTER the principal actually removes/escapes the guardrail, so keep it CONDITIONAL(guardrail_removed) unless the underlying edge would be BLOCKED for an INDEPENDENT reason (a second guardrail, a hard deny not owned by ?guardrail) — then it stays BLOCKED on that other cause. If ?principal's CanModifyPolicy / move capability is itself CONDITIONAL or BLOCKED, the upgrade inherits that (no upgrade from a BLOCKED modify). Never upgrade an edge that was BLOCKED by something OTHER than this guardrail. Monotonic re-emit updates the existing edge's state in place (RULE-FORMAT: re-derivation updates state). |
Narrative
{principal.name} can remove the guardrail {guardrail.name} (by editing it or moving the affected resource out of its scope); doing so lifts the block on "{x.name} {cap} {y.name}", restoring that path to its underlying state.
Raw rule rules/derived/hierarchy-chains.yaml
id: guardrail-removal-upgrades-blocked
emits: ?cap
description: A principal who can modify or escape a guardrail (SCP / Org Policy / deny assignment) upgrades
the edges that guardrail was suppressing from BLOCKED to their underlying state.
applies_to:
- '*'
match:
- - principal: null
- CanModifyPolicy
- guardrail: null
- - x: null
- cap: null
- y: null
where:
- node_class(?guardrail) == Policy
- 'node_type(?guardrail) in [ServiceControlPolicy, OrganizationPolicy, ConditionalPolicy] # AWS SCP /
GCP org policy / Azure deny assignment'
- state_of(match[1]) == BLOCKED
- "blocked_by(match[1]) == ?guardrail # THIS guardrail is what suppresses THIS edge \u2014 not merely\
\ present in the account"
optional:
- - principal: null
- CanModifyConfiguration
- scope_binding: null
emit:
source: ?x
target: ?y
permissions: []
conditions:
- guardrail_removed
state_logic: "UPGRADE (re-emit match[1] in place): the suppressed edge is re-emitted with its UNDERLYING\
\ state \u2014 the state it would have had absent ?guardrail (ACTIVE, or CONDITIONAL with whatever\
\ non-guardrail gate remains). The upgrade is itself CONDITIONAL(guardrail_removed): it is only realizable\
\ AFTER the principal actually removes/escapes the guardrail, so keep it CONDITIONAL(guardrail_removed)\
\ unless the underlying edge would be BLOCKED for an INDEPENDENT reason (a second guardrail, a hard\
\ deny not owned by ?guardrail) \u2014 then it stays BLOCKED on that other cause. If ?principal's\
\ CanModifyPolicy / move capability is itself CONDITIONAL or BLOCKED, the upgrade inherits that (no\
\ upgrade from a BLOCKED modify). Never upgrade an edge that was BLOCKED by something OTHER than this\
\ guardrail. Monotonic re-emit updates the existing edge's state in place (RULE-FORMAT: re-derivation\
\ updates state)."
confidence: min(contributing_confidences)
derived_from:
- ?principal CanModifyPolicy ?guardrail (or reparent via CanModifyConfiguration ?scope_binding)
- ?x ?cap ?y (BLOCKED by ?guardrail)
- blocked_by == ?guardrail
false_positive_note: "Fire ONLY when THIS guardrail is the actual cause of the BLOCK \u2014 check blocked_by(edge)==?guardrail,\
\ not mere co-location in the account/org. Two forms of removal, EITHER suffices: (a) modify the policy\
\ (AWS organizations:UpdatePolicy/DetachPolicy/DisablePolicyType, GCP orgpolicy.policy.set, Azure\
\ delete the owning blueprint/managed-app that created the deny assignment); (b) move the resource\
\ out of scope (AWS MoveAccount/LeaveOrganization, Azure subscription move to another MG, GCP project.move).\
\ Keep the upgrade CONDITIONAL(guardrail_removed) \u2014 it is a two-step action, not an already-realized\
\ path \u2014 so path scoring reflects the extra removal step. Do NOT upgrade if a DIFFERENT guardrail\
\ or an explicit hard deny independently blocks the same edge. AWS SCP nuance: an SCP can only be\
\ edited from the management account or a delegated administrator \u2014 a member- account principal\
\ with iam-only permissions CANNOT weaken it; require the organizations:* action at the right scope.\
\ Azure deny-assignment nuance: most deny assignments are system-managed and not directly writable\
\ \u2014 prefer the move-out / delete-owning-assignment path and mark CONDITIONAL when only that indirect\
\ route exists."
narrative: '{principal.name} can remove the guardrail {guardrail.name} (by editing it or moving the
affected resource out of its scope); doing so lifts the block on "{x.name} {cap} {y.name}", restoring
that path to its underlying state.'