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.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
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. |
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."