gcp-dataflow-update-contents-modify-code

dataflow.jobs.updateContents on a streaming Dataflow job lets the principal inject attacker-controlled transforms into the live pipeline; updated code runs immediately as the worker SA.

derived gcp emits CanModifyCode

match (effective permission)

{ "action": "dataflow.jobs.updateContents", "resource_type": "google.dataflow.Job" }

where

?job is a STREAMING job (job type == JOB_TYPE_STREAMING or has no end time) ?job state is RUNNING (active streaming pipeline)

emit

source typeIdentity
target typeCompute
source<principal>
target<Dataflow streaming job (BatchJob)>
permissionsdataflow.jobs.updateContents
conditionsservice_state
state logicACTIVE if dataflow.jobs.updateContents is confirmed in the effective grant at project scope AND the target job is a RUNNING streaming job. The code update takes effect without restart — there is no trigger_exists gate; the streaming pipeline is already running. CONDITIONAL(service_state) if job state is not confirmed as RUNNING (the streaming pipeline must already be running for this to be an active code-injection path). Do not emit for batch jobs (they do not support in-flight code updates via this permission). BLOCKED if a deny policy applies to dataflow.jobs.updateContents.

Narrative

{principal.name} can update the user code of running streaming pipeline {job.name} (dataflow.jobs.updateContents), injecting attacker-controlled transforms that execute immediately as worker service account {sa.name} without restarting the job.

Raw rule rules/derived/gcp/dataflow.yaml

id: gcp-dataflow-update-contents-modify-code
emits: CanModifyCode
description: dataflow.jobs.updateContents on a streaming Dataflow job lets the principal inject attacker-controlled
  transforms into the live pipeline; updated code runs immediately as the worker SA.
match_effective_permission:
  action: dataflow.jobs.updateContents
  resource_type: google.dataflow.Job
where:
- ?job is a STREAMING job (job type == JOB_TYPE_STREAMING or has no end time)
- ?job state is RUNNING (active streaming pipeline)
emit:
  source_type: Identity
  target_type: Compute
  source: <principal>
  target: <Dataflow streaming job (BatchJob)>
  permissions:
  - dataflow.jobs.updateContents
  conditions:
  - service_state
  state_logic: "ACTIVE if dataflow.jobs.updateContents is confirmed in the effective grant at project\
    \ scope AND the target job is a RUNNING streaming job. The code update takes effect without restart\
    \ \u2014 there is no trigger_exists gate; the streaming pipeline is already running. CONDITIONAL(service_state)\
    \ if job state is not confirmed as RUNNING (the streaming pipeline must already be running for this\
    \ to be an active code-injection path). Do not emit for batch jobs (they do not support in-flight\
    \ code updates via this permission). BLOCKED if a deny policy applies to dataflow.jobs.updateContents."
  confidence: 0.93
  derived_from:
  - <dataflow.jobs.updateContents effective permission>
  false_positive_note: "STREAMING ONLY: dataflow.jobs.updateContents is meaningful for code injection\
    \ only on streaming jobs that are currently RUNNING. For batch jobs, this path does not apply \u2014\
    \ emit as POTENTIAL or omit. Confirm job type (JOB_TYPE_STREAMING) and state (RUNNING) from the collected\
    \ job resource before emitting ACTIVE. The downstream CanExecuteAs is derived by can-execute-as execute-as-via-code-modify\
    \ off the CanModifyCode + ExecutesAs combination \u2014 cite, do not re-derive."
  narrative: '{principal.name} can update the user code of running streaming pipeline {job.name} (dataflow.jobs.updateContents),
    injecting attacker-controlled transforms that execute immediately as worker service account {sa.name}
    without restarting the job.'
move · open · esc close