cicd-modify-buildspec-executes-as

Modify the buildspec/pipeline/runbook/template a runner executes, then run it, to execute as the runner's service identity.

derived any cloud emits CanExecuteAs

match

A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.

{'principal': None} CanModifyCode {'runner': None} {'runner': None} ExecutesAs {'identity': None}

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} CanTrigger {'runner': None}

emit

source typeIdentity
target typeIdentity
source?principal
target?identity
conditionsrun_path
state logicACTIVE if a run path exists for the modified definition — the attacker can also trigger/start the runner (matched(optional[0])), OR the runner runs on a trigger the modification arms (push-to-branch webhook, PR build, schedule, upstream pipeline stage) so the injected code runs without further action. Otherwise CONDITIONAL(run_path): the code is planted but has no established way to run. Inherit weakest with the contributing CanModifyCode / ExecutesAs: a CONDITIONAL/BLOCKED CanModifyCode (branch protection, required review, protected pipeline, deny on the definition store) caps or blocks this edge.

Narrative

{principal.name} can modify the definition {runner.name} executes (its buildspec/pipeline/runbook/template or the source it builds) and run it, executing attacker code as the runner's identity {identity.name}.

Raw rule rules/derived/cicd-chains.yaml

id: cicd-modify-buildspec-executes-as
emits: CanExecuteAs
description: Modify the buildspec/pipeline/runbook/template a runner executes, then run it, to execute
  as the runner's service identity.
applies_to:
- '*'
match:
- - principal: null
  - CanModifyCode
  - runner: null
- - runner: null
  - ExecutesAs
  - identity: null
where:
- node_type(?runner) in [BuildWorker, ContainerTask, ContainerService, ServerlessFunction, VirtualMachine,
  KubernetesWorkload]
optional:
- - principal: null
  - CanTrigger
  - runner: null
emit:
  source_type: Identity
  target_type: Identity
  source: ?principal
  target: ?identity
  permissions: []
  conditions:
  - run_path
  state_logic: "ACTIVE if a run path exists for the modified definition \u2014 the attacker can also trigger/start\
    \ the runner (matched(optional[0])), OR the runner runs on a trigger the modification arms (push-to-branch\
    \ webhook, PR build, schedule, upstream pipeline stage) so the injected code runs without further\
    \ action. Otherwise CONDITIONAL(run_path): the code is planted but has no established way to run.\
    \ Inherit weakest with the contributing CanModifyCode / ExecutesAs: a CONDITIONAL/BLOCKED CanModifyCode\
    \ (branch protection, required review, protected pipeline, deny on the definition store) caps or blocks\
    \ this edge."
  confidence: min(contributing_confidences)
  derived_from:
  - ?principal CanModifyCode ?runner
  - ?runner ExecutesAs ?identity
  false_positive_note: "The judgment is (a) that CanModifyCode truly reaches the EXECUTED definition and\
    \ (b) that a run path exists. Do NOT emit ACTIVE when a branch-protection / required-reviewer / protected-pipeline\
    \ control gates the merge that would run the code (that CanModifyCode should itself be CONDITIONAL/BLOCKED,\
    \ and this edge inherits it). Editing a definition on a branch/stage the runner never executes is\
    \ not this edge \u2014 scope CanModifyCode to the ref/stage the runner actually runs. This dedupes\
    \ with can-execute-as execute-as-via-code-modify on (CanExecuteAs, ?principal, ?identity) \u2014 same\
    \ collapse, CI/CD framing; do not double-count weight where both fire. The escalation consequence\
    \ (identity more privileged than the principal) is produced by can-execute-as / can-control off this\
    \ CanExecuteAs \u2014 cite, do not duplicate. iam:PassRole / actAs that lets the runner assume a DIFFERENT\
    \ privileged role is the config-identity-swap case in can-execute-as, not this rule."
  narrative: '{principal.name} can modify the definition {runner.name} executes (its buildspec/pipeline/runbook/template
    or the source it builds) and run it, executing attacker code as the runner''s identity {identity.name}.'
move · open · esc close