gcp-deploy-cross-project-exec-sa

A Cloud Deploy target's execution SA from a different project establishes a cross-project trust enabling CanEnterProject on that SA's home project.

derived gcp emits CrossProjectTrust

match

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

{'deploy_worker': None} ExecutesAs {'exec_sa': None}

where

node_type(?deploy_worker) == BuildWorker ?pipeline_project = project_of(?deploy_worker) ?exec_sa_project = project_of(?exec_sa) ?exec_sa_project != ?pipeline_project

emit

source typeProject
target typeProject
source?pipeline_project
target?exec_sa_project
permissionsiam.serviceAccounts.actAs (granted cross-project)
conditionsiam_permission resource_policy
state logicACTIVE when the execution SA is confirmed in a different project and the cross-project actAs grant is in place (required for Cloud Deploy to use it). hierarchy-chains enter-boundary-rollups and federation-chains federate-then-enter-project collapse this CrossProjectTrust + CanExecuteAs into CanEnterProject(?principal → exec_sa_project).

Narrative

Cloud Deploy pipeline in project {pipeline_project.name} uses execution SA {exec_sa.name} from project {exec_sa_project.name} - a cross-project trust between the two projects. An attacker who executes as {exec_sa.name} via a rollout gains a foothold in {exec_sa_project.name}. hierarchy-chains enter-boundary-rollups collapses this CrossProjectTrust into CanEnterProject({principal} → {exec_sa_project.name}).

Raw rule rules/derived/gcp/clouddeploy.yaml

id: gcp-deploy-cross-project-exec-sa
emits: CrossProjectTrust
description: A Cloud Deploy target's execution SA from a different project establishes a cross-project
  trust enabling CanEnterProject on that SA's home project.
applies_to:
- gcp
match:
- - deploy_worker: null
  - ExecutesAs
  - exec_sa: null
where:
- node_type(?deploy_worker) == BuildWorker
- ?pipeline_project = project_of(?deploy_worker)
- ?exec_sa_project = project_of(?exec_sa)
- ?exec_sa_project != ?pipeline_project
emit:
  source_type: Project
  target_type: Project
  source: ?pipeline_project
  target: ?exec_sa_project
  permissions:
  - iam.serviceAccounts.actAs (granted cross-project)
  conditions:
  - iam_permission
  - resource_policy
  state_logic: "ACTIVE when the execution SA is confirmed in a different project and the cross-project\
    \ actAs grant is in place (required for Cloud Deploy to use it). hierarchy-chains enter-boundary-rollups\
    \ and federation-chains federate-then-enter-project collapse this CrossProjectTrust + CanExecuteAs\
    \ into CanEnterProject(?principal \u2192 exec_sa_project)."
  confidence: 0.85
  derived_from:
  - ?deploy_worker ExecutesAs ?exec_sa (gcp-deploy-executes-as)
  - cross-project SA membership
  false_positive_note: "CrossProjectTrust is a structural fact, not an attack step by itself. The hierarchy-chains\
    \ roll-up turns it into CanEnterProject only when combined with a CanExecuteAs path that reaches ?exec_sa.\
    \ Confirm the SA is in a different project (?exec_sa_project != ?pipeline_project) before emitting.\
    \ Both source and target must be Project nodes bound by the where-clause. The cross-project actAs\
    \ org-policy constraint (iam.allowedPolicyMemberDomains) may restrict this \u2014 emit BLOCKED if\
    \ the org policy denies cross-project SA usage and no override exists."
  narrative: "Cloud Deploy pipeline in project {pipeline_project.name} uses execution SA {exec_sa.name}\
    \ from project {exec_sa_project.name} \u2014 a cross-project trust between the two projects. An attacker\
    \ who executes as {exec_sa.name} via a rollout gains a foothold in {exec_sa_project.name}. hierarchy-chains\
    \ enter-boundary-rollups collapses this CrossProjectTrust into CanEnterProject({principal} \u2192\
    \ {exec_sa_project.name})."
move · open · esc close