gcp-cloudscheduler-create-pubsub-job-write

A principal with cloudscheduler.jobs.create can establish a Pub/Sub job that publishes attacker-controlled data to any topic in the project on schedule. No actAs required. Feeds messaging-chains rules 1+2 for CanTrigger -> CanExecuteAs chain.

derived gcp emits CanWriteData

match

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

{'principal': None} CanSchedule {'job': None}

where

node_type(?job) == Scheduler ?principal intends to / has created a Pub/Sub target job (pubsubTarget.topicName is set)

emit

source typeIdentity
source<principal>
target<target Pub/Sub topic (Topic node in Messaging class)>
permissionscloudscheduler.jobs.create
conditionsiam_permission api_enabled
state logicACTIVE when the principal holds cloudscheduler.jobs.create AND the Cloud Scheduler API is enabled. The Cloud Scheduler service agent automatically holds roles/pubsub.publisher on project-local topics — no additional pubsub grant needed for the creator. Cross-project topic targets require the service agent to hold pubsub.topics.publish on the external topic (separate grant; downgrade to CONDITIONAL(resource_policy) for cross-project targets).

Narrative

{principal.name} can create a Cloud Scheduler Pub/Sub job (cloudscheduler.jobs.create) that publishes attacker-controlled messages to {topic.name} on schedule. If {topic.name} has a bound consumer, messaging-chains rolls this up to CanTrigger -> CanExecuteAs(consumer runtime SA).

Raw rule rules/derived/gcp/cloudscheduler.yaml

id: gcp-cloudscheduler-create-pubsub-job-write
emits: CanWriteData
description: A principal with cloudscheduler.jobs.create can establish a Pub/Sub job that publishes attacker-controlled
  data to any topic in the project on schedule. No actAs required. Feeds messaging-chains rules 1+2 for
  CanTrigger -> CanExecuteAs chain.
match:
- - principal: null
  - CanSchedule
  - job: null
where:
- node_type(?job) == Scheduler
- ?principal intends to / has created a Pub/Sub target job (pubsubTarget.topicName is set)
emit:
  source_type: Identity
  source: <principal>
  target: <target Pub/Sub topic (Topic node in Messaging class)>
  permissions:
  - cloudscheduler.jobs.create
  conditions:
  - iam_permission
  - api_enabled
  state_logic: "ACTIVE when the principal holds cloudscheduler.jobs.create AND the Cloud Scheduler API\
    \ is enabled. The Cloud Scheduler service agent automatically holds roles/pubsub.publisher on project-local\
    \ topics \u2014 no additional pubsub grant needed for the creator. Cross-project topic targets require\
    \ the service agent to hold pubsub.topics.publish on the external topic (separate grant; downgrade\
    \ to CONDITIONAL(resource_policy) for cross-project targets)."
  confidence: min(contributing_confidences) * 0.85
  derived_from:
  - ?principal CanSchedule ?job (gcp-cloudscheduler-can-schedule)
  false_positive_note: This CanWriteData is from the PRINCIPAL (via job creation) to the TOPIC, not from
    the job itself. The intermediate step is the job creation (gcp-cloudscheduler-can-schedule). The actual
    publish is performed by the Cloud Scheduler service agent; the creator controls the message payload
    (pubsubTarget.data and pubsubTarget.attributes). If the topic has no consumer bound (no CanTrigger
    edge from topic to a Compute node), this is CanWriteData with no CanExecuteAs consequence. Messaging-chains
    rule 1 (messaging-publish-triggers-consumer) handles the CanWriteData + CanTrigger => CanTrigger roll-up
    for the principal.
  narrative: '{principal.name} can create a Cloud Scheduler Pub/Sub job (cloudscheduler.jobs.create) that
    publishes attacker-controlled messages to {topic.name} on schedule. If {topic.name} has a bound consumer,
    messaging-chains rolls this up to CanTrigger -> CanExecuteAs(consumer runtime SA).'
move · open · esc close