cicd-trigger-runner-executes-as
Trigger/start a runner whose current definition already runs attacker-influenced code, executing as the runner's identity.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?runner) in [
BuildWorker, ContainerTask, ContainerService, ServerlessFunction, VirtualMachine, KubernetesWorkload]
optional
Present → upgrade the emitted state; absent → downgrade (not a hard requirement).
{'principal': None}
CanStart {'runner': None}
emit
| source type | Identity |
|---|---|
| target type | Identity |
| source | ?principal |
| target | ?identity |
| conditions | attacker_influenced_definition |
| state logic | CONDITIONAL(attacker_influenced_definition) by default — triggering runs the CURRENT definition, which yields the identity to the attacker only if that definition already executes attacker-controllable code (a mutable/unpinned dependency the attacker can poison, a build of an untrusted PR/fork with pwn-request semantics, an attacker-authored branch the trigger targets, or an already-tampered spec). ACTIVE only when such attacker influence over the current definition is established (then it is effectively the modify path). A trigger over a fully pinned, trusted, reviewed definition => stays CONDITIONAL and is NOT an ACTIVE execute-as. Inherit weakest with CanTrigger / ExecutesAs. |
Narrative
{principal.name} can trigger {runner.name}, which executes as {identity.name}; if the runner's current definition already runs attacker-influenceable code (poisoned dependency, untrusted PR build, tampered spec), the run executes as {identity.name}.
Raw rule rules/derived/cicd-chains.yaml
id: cicd-trigger-runner-executes-as
emits: CanExecuteAs
description: Trigger/start a runner whose current definition already runs attacker-influenced code, executing
as the runner's identity.
applies_to:
- '*'
match:
- - principal: null
- CanTrigger
- runner: null
- - runner: null
- ExecutesAs
- identity: null
where:
- node_type(?runner) in [BuildWorker, ContainerTask, ContainerService, ServerlessFunction, VirtualMachine,
KubernetesWorkload]
optional:
- - principal: null
- CanStart
- runner: null
emit:
source_type: Identity
target_type: Identity
source: ?principal
target: ?identity
permissions: []
conditions:
- attacker_influenced_definition
state_logic: "CONDITIONAL(attacker_influenced_definition) by default \u2014 triggering runs the CURRENT\
\ definition, which yields the identity to the attacker only if that definition already executes attacker-controllable\
\ code (a mutable/unpinned dependency the attacker can poison, a build of an untrusted PR/fork with\
\ pwn-request semantics, an attacker-authored branch the trigger targets, or an already-tampered spec).\
\ ACTIVE only when such attacker influence over the current definition is established (then it is\
\ effectively the modify path). A trigger over a fully pinned, trusted, reviewed definition => stays\
\ CONDITIONAL and is NOT an ACTIVE execute-as. Inherit weakest with CanTrigger / ExecutesAs."
confidence: min(contributing_confidences) * 0.7
derived_from:
- ?principal CanTrigger ?runner
- ?runner ExecutesAs ?identity
false_positive_note: "Strictly weaker than rule 1 (0.7 prior): a trigger is not code control. Do NOT\
\ emit ACTIVE merely because the principal can start a build \u2014 the current definition must already\
\ run attacker-influenceable code. The classic ACTIVE cases are pwn-request-style PR builds (untrusted\
\ fork code runs with the trusted identity) and unpinned-dependency supply-chain (the attacker poisons\
\ a dependency the build resolves). If the runner builds only trusted, pinned, reviewed source with\
\ no untrusted input, keep CONDITIONAL. Dedupes with rule 1 on (CanExecuteAs, ?principal, ?identity):\
\ when the principal can BOTH modify and trigger, rule 1's ACTIVE dominates \u2014 do not double-count."
narrative: '{principal.name} can trigger {runner.name}, which executes as {identity.name}; if the runner''s
current definition already runs attacker-influenceable code (poisoned dependency, untrusted PR build,
tampered spec), the run executes as {identity.name}.'