cicd-pipeline-orchestrates-stages
Triggering a pipeline reaches each downstream stage runner it orchestrates; propagate CanTrigger to those runners.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'principal': None}
CanTrigger {'pipeline': None}
{'pipeline': None} CanTrigger {'stage_runner': None}
where
node_type(?pipeline) in [
BuildWorker, ContainerService, ServerlessFunction, VirtualMachine]
node_type(?stage_runner) in [BuildWorker, ContainerTask, ContainerService, ServerlessFunction, VirtualMachine, KubernetesWorkload]
?stage_runner != ?pipeline
emit
| source type | Identity |
|---|---|
| target type | Compute |
| source | ?principal |
| target | ?stage_runner |
| conditions | stage_reachable |
| state logic | inherit from the two CanTrigger contributors: ACTIVE when the principal's trigger of the pipeline is ACTIVE and the pipeline genuinely invokes ?stage_runner unconditionally (not gated by a manual approval / stage condition the attacker cannot satisfy); CONDITIONAL(stage_reachable) when the stage is gated by an approval or a condition that must first be met; BLOCKED if either contributing trigger is BLOCKED. Never stronger than the weaker of the two CanTrigger edges. |
Narrative
{principal.name} can trigger the pipeline {pipeline.name}, which orchestrates {stage_runner.name}; the pipeline start therefore reaches {stage_runner.name}, propagating the ability to run it (and thus its execution identity).
Raw rule rules/derived/cicd-chains.yaml
id: cicd-pipeline-orchestrates-stages
emits: CanTrigger
description: Triggering a pipeline reaches each downstream stage runner it orchestrates; propagate CanTrigger
to those runners.
applies_to:
- '*'
match:
- - principal: null
- CanTrigger
- pipeline: null
- - pipeline: null
- CanTrigger
- stage_runner: null
where:
- node_type(?pipeline) in [BuildWorker, ContainerService, ServerlessFunction, VirtualMachine]
- node_type(?stage_runner) in [BuildWorker, ContainerTask, ContainerService, ServerlessFunction, VirtualMachine,
KubernetesWorkload]
- ?stage_runner != ?pipeline
emit:
source_type: Identity
target_type: Compute
source: ?principal
target: ?stage_runner
permissions: []
conditions:
- stage_reachable
state_logic: 'inherit from the two CanTrigger contributors: ACTIVE when the principal''s trigger of
the pipeline is ACTIVE and the pipeline genuinely invokes ?stage_runner unconditionally (not gated
by a manual approval / stage condition the attacker cannot satisfy); CONDITIONAL(stage_reachable)
when the stage is gated by an approval or a condition that must first be met; BLOCKED if either contributing
trigger is BLOCKED. Never stronger than the weaker of the two CanTrigger edges.'
confidence: min(contributing_confidences)
derived_from:
- ?principal CanTrigger ?pipeline
- ?pipeline CanTrigger ?stage_runner
false_positive_note: "Only propagate for stages the pipeline actually reaches from the triggered entry\
\ point. A stage behind a MANUAL APPROVAL gate, a protected-environment wait, or a stage condition\
\ (branch filter, `when: manual`) the attacker cannot satisfy => CONDITIONAL(stage_reachable), not\
\ ACTIVE. This is orchestration propagation only; it does NOT itself grant an identity \u2014 it feeds\
\ rules 1/2 on each ?stage_runner's ExecutesAs. Do not emit CanExecuteAs here."
narrative: '{principal.name} can trigger the pipeline {pipeline.name}, which orchestrates {stage_runner.name};
the pipeline start therefore reaches {stage_runner.name}, propagating the ability to run it (and thus
its execution identity).'