gcp-tasks-passidentity-queue-sa

cloudtasks.queues.update + iam.serviceAccounts.actAs on a SA allows an attacker to configure that SA as the queue-level OIDC/OAuth token identity - the GCP actAs guard rail for Cloud Tasks queue-SA attachment.

derived gcp emits CanPassIdentity

match

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

?principal CanModifyConfiguration ?queue

where

node_type(?queue) == Queue AND provider_type(?queue) == google.cloud.tasks.v2.Queue ?principal has effective iam.serviceAccounts.actAs on ?sa (confirmed via SA IAM policy) ?sa is the SA intended for httpTarget.oidcToken.serviceAccountEmail or oauthToken.serviceAccountEmail on ?queue

emit

source typeIdentity
target typeServiceAccount
source<principal>
target<service account>
permissionscloudtasks.queues.update iam.serviceAccounts.actAs
conditionsiam_permission role_compatibility
state logicACTIVE when both cloudtasks.queues.update on the queue AND iam.serviceAccounts.actAs on the chosen SA are confirmed. POTENTIAL(role_compatibility) when actAs on the SA is unknown or unresolved. BLOCKED when actAs is explicitly denied (IAM deny policy on iam.serviceAccounts.actAs). Never emit when only cloudtasks.queues.update is confirmed without actAs.

Narrative

{principal.name} holds cloudtasks.queues.update on {queue.name} and iam.serviceAccounts.actAs on {sa.name}, and can configure {sa.name} as the queue-level OIDC/OAuth token identity. Future dispatches carry a token for {sa.name} - an attacker controlling the consumer endpoint captures that token.

Raw rule rules/derived/gcp/tasks.yaml

id: gcp-tasks-passidentity-queue-sa
emits: CanPassIdentity
description: "cloudtasks.queues.update + iam.serviceAccounts.actAs on a SA allows an attacker to configure\
  \ that SA as the queue-level OIDC/OAuth token identity \u2014 the GCP actAs guard rail for Cloud Tasks\
  \ queue-SA attachment."
match:
- - ?principal
  - CanModifyConfiguration
  - ?queue
where:
- node_type(?queue) == Queue AND provider_type(?queue) == google.cloud.tasks.v2.Queue
- ?principal has effective iam.serviceAccounts.actAs on ?sa (confirmed via SA IAM policy)
- ?sa is the SA intended for httpTarget.oidcToken.serviceAccountEmail or oauthToken.serviceAccountEmail
  on ?queue
emit:
  source_type: Identity
  target_type: ServiceAccount
  source: <principal>
  target: <service account>
  permissions:
  - cloudtasks.queues.update
  - iam.serviceAccounts.actAs
  conditions:
  - iam_permission
  - role_compatibility
  state_logic: ACTIVE when both cloudtasks.queues.update on the queue AND iam.serviceAccounts.actAs on
    the chosen SA are confirmed. POTENTIAL(role_compatibility) when actAs on the SA is unknown or unresolved.
    BLOCKED when actAs is explicitly denied (IAM deny policy on iam.serviceAccounts.actAs). Never emit
    when only cloudtasks.queues.update is confirmed without actAs.
  derived_from:
  - ?principal CanModifyConfiguration ?queue (gcp-tasks-update-queue-config)
  - 'effective permission: iam.serviceAccounts.actAs on ?sa'
  false_positive_note: "actAs is the load-bearing gate: the Cloud Tasks API rejects queue updates that\
    \ change oidcToken.serviceAccountEmail / oauthToken.serviceAccountEmail when the caller lacks iam.serviceAccounts.actAs\
    \ on the chosen SA. Do not emit for SAs the principal cannot actAs. actAs on sa-A does NOT grant actAs\
    \ on sa-B. Only emit for the specific SA confirmed in the actAs check. This CanPassIdentity is consumed\
    \ by can-execute-as execute-as-via-config-identity-swap to derive CanExecuteAs(principal \u2192 SA):\
    \ the queue's OIDC/OAuth token SA becomes the identity the consumer receives and, if the consumer\
    \ is also attacker-controlled or leaks the token, the attacker exercises it."
  narrative: "{principal.name} holds cloudtasks.queues.update on {queue.name} and iam.serviceAccounts.actAs\
    \ on {sa.name}, and can configure {sa.name} as the queue-level OIDC/OAuth token identity. Future dispatches\
    \ carry a token for {sa.name} \u2014 an attacker controlling the consumer endpoint captures that token."
move · open · esc close