gcp-cloudbuild-trigger-run
cloudbuild.triggers.run lets the principal fire an existing trigger at a chosen source revision; code execution as the trigger's SA is CONDITIONAL on the current definition being attacker-influenced.
match (effective permission)
{
"action": "cloudbuild.triggers.run",
"resource_type": "google.devtools.cloudbuild.v1.BuildTrigger"
}
emit
| source type | Identity |
|---|---|
| target type | Compute |
| source | <principal> |
| target | <Cloud Build trigger node / BuildWorker> |
| permissions | cloudbuild.triggers.run |
| conditions | attacker_influenced_definition |
| state logic | CONDITIONAL(attacker_influenced_definition): triggers.run executes the CURRENT trigger definition. This becomes ACTIVE code execution as the trigger's SA only if: (a) the trigger builds from a branch/commit the attacker controls (so the source revision arg lets the attacker choose an attacker-authored commit), OR (b) the trigger's build spec references unpinned/mutable config the attacker can poison (dependency, GCS bucket), OR (c) the trigger's inline build spec was already tampered (by gcp-cloudbuild-trigger-update-code). A trigger.run over a fully pinned, reviewed definition with no attacker-reachable inputs stays CONDITIONAL, not ACTIVE. Inherits BLOCKED if a deny policy applies to triggers.run. |
Narrative
{principal.name} can fire Cloud Build trigger {trigger.name} (cloudbuild.triggers.run) at a chosen source revision; if the trigger's build definition is attacker-influenced (attacker-controlled branch, unpinned dependency, or already-poisoned spec), this executes code as the trigger's SA.
Raw rule rules/derived/gcp/cloudbuild.yaml
id: gcp-cloudbuild-trigger-run
emits: CanTrigger
description: cloudbuild.triggers.run lets the principal fire an existing trigger at a chosen source revision;
code execution as the trigger's SA is CONDITIONAL on the current definition being attacker-influenced.
match_effective_permission:
action: cloudbuild.triggers.run
resource_type: google.devtools.cloudbuild.v1.BuildTrigger
emit:
source_type: Identity
target_type: Compute
source: <principal>
target: <Cloud Build trigger node / BuildWorker>
permissions:
- cloudbuild.triggers.run
conditions:
- attacker_influenced_definition
state_logic: 'CONDITIONAL(attacker_influenced_definition): triggers.run executes the CURRENT trigger
definition. This becomes ACTIVE code execution as the trigger''s SA only if: (a) the trigger builds
from a branch/commit the attacker controls (so the source revision arg lets the attacker choose an
attacker-authored commit), OR (b) the trigger''s build spec references unpinned/mutable config the
attacker can poison (dependency, GCS bucket), OR (c) the trigger''s inline build spec was already
tampered (by gcp-cloudbuild-trigger-update-code). A trigger.run over a fully pinned, reviewed definition
with no attacker-reachable inputs stays CONDITIONAL, not ACTIVE. Inherits BLOCKED if a deny policy
applies to triggers.run.'
confidence: '0.70'
derived_from:
- <cloudbuild.triggers.run effective permission>
false_positive_note: "Do NOT emit ACTIVE merely because the principal can run the trigger \u2014 the\
\ CURRENT definition must be demonstrably attacker-influenced for CanExecuteAs to be ACTIVE. The classic\
\ ACTIVE case is a trigger that builds from a public fork / untrusted PR (pwn-request pattern) or\
\ an unpinned Dockerfile FROM / base image the attacker can push to. cicd-chains cicd-trigger-runner-executes-as\
\ handles the CONDITIONAL -> CanExecuteAs collapse; do not re-derive here."
narrative: '{principal.name} can fire Cloud Build trigger {trigger.name} (cloudbuild.triggers.run) at
a chosen source revision; if the trigger''s build definition is attacker-influenced (attacker-controlled
branch, unpinned dependency, or already-poisoned spec), this executes code as the trigger''s SA.'