gcp-pubsub-push-triggers-consumer
An active push subscription binding its topic's messages to an HTTP endpoint (Cloud Run service / Cloud Function URL) makes the subscription an event-source bridge: messages published to the topic are delivered to the consumer. Consumed by messaging-chains to derive CanTrigger(publisher → consumer).
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'match_record': {'resource_type': 'google.pubsub.Subscription', 'field': 'pushConfig.pushEndpoint'}, 'where': ['pushConfig.pushEndpoint is non-empty and resolves to a Cloud Run service or Cloud Functions URL', '?subscription.state is ACTIVE (not PAUSED, DELETED)', '?subscription is attached to ?topic (subscription.topic field)']}
where
node_type(?subscription) ==
Queue
node_class(?consumer) == Compute
emit
| source type | Queue |
|---|---|
| target type | Compute |
| source | <Pub/Sub subscription node> |
| target | <Cloud Run service / Cloud Function consumer at the push endpoint URL> |
| conditions | trigger_exists service_state |
| state logic | ACTIVE when the subscription state is ACTIVE and the push endpoint URL resolves to a modeled Cloud Run service or Cloud Function that is enabled/ running. CONDITIONAL(trigger_exists) when the subscription is PAUSED or the endpoint does not resolve to a modeled compute node (external URL or not yet inventoried). CONDITIONAL(condition_expression) when the subscription has a non-trivial message filter (filter field non-empty): the attacker's payload must match the filter expression to be delivered. BLOCKED when the subscription state is DELETED or the push SA is missing actAs (delivery fails silently). Inherit weakest with the contributing CanWriteData edge in messaging-chains. |
Narrative
Pub/Sub subscription {subscription.name} is a push subscription delivering to {consumer.name} ({pushEndpoint}); messages published to the topic are POSTed to {consumer.name}, feeding messaging-chains to derive CanTrigger(publisher → {consumer.name}).
Raw rule rules/derived/gcp/pubsub.yaml
id: gcp-pubsub-push-triggers-consumer
emits: CanTrigger
description: "An active push subscription binding its topic's messages to an HTTP endpoint (Cloud Run\
\ service / Cloud Function URL) makes the subscription an event-source bridge: messages published to\
\ the topic are delivered to the consumer. Consumed by messaging-chains to derive CanTrigger(publisher\
\ \u2192 consumer)."
match:
- match_record:
resource_type: google.pubsub.Subscription
field: pushConfig.pushEndpoint
where:
- pushConfig.pushEndpoint is non-empty and resolves to a Cloud Run service or Cloud Functions URL
- ?subscription.state is ACTIVE (not PAUSED, DELETED)
- ?subscription is attached to ?topic (subscription.topic field)
where:
- node_type(?subscription) == Queue
- node_class(?consumer) == Compute
emit:
source_type: Queue
target_type: Compute
source: <Pub/Sub subscription node>
target: <Cloud Run service / Cloud Function consumer at the push endpoint URL>
permissions: []
conditions:
- trigger_exists
- service_state
state_logic: 'ACTIVE when the subscription state is ACTIVE and the push endpoint URL resolves to a modeled
Cloud Run service or Cloud Function that is enabled/ running. CONDITIONAL(trigger_exists) when the
subscription is PAUSED or the endpoint does not resolve to a modeled compute node (external URL or
not yet inventoried). CONDITIONAL(condition_expression) when the subscription has a non-trivial message
filter (filter field non-empty): the attacker''s payload must match the filter expression to be delivered.
BLOCKED when the subscription state is DELETED or the push SA is missing actAs (delivery fails silently).
Inherit weakest with the contributing CanWriteData edge in messaging-chains.'
confidence: 0.95
derived_from:
- pubsub.subscriptions.get[pushConfig.pushEndpoint, state, topic, filter]
false_positive_note: "Only emit for PUSH subscriptions (pushConfig.pushEndpoint non-empty). Pull subscriptions\
\ do NOT automatically trigger a consumer \u2014 they require the consumer to actively poll (model\
\ the consumer's pull action as CanReadData, not CanTrigger). A subscription filter (filter field)\
\ is a real delivery gate: if the attacker's payload cannot satisfy the filter expression, delivery\
\ is silently dropped \u2014 downgrade to CONDITIONAL(condition_expression). BigQuery and Cloud Storage\
\ subscriptions deliver to data sinks, not compute; do not emit CanTrigger \u2192 CanExecuteAs for\
\ those delivery types. Dead-letter topic forwarding is a separate hop; model it as a new CanTrigger\
\ chain off the dead-letter topic if that topic also has a consumer."
narrative: "Pub/Sub subscription {subscription.name} is a push subscription delivering to {consumer.name}\
\ ({pushEndpoint}); messages published to the topic are POSTed to {consumer.name}, feeding messaging-chains\
\ to derive CanTrigger(publisher \u2192 {consumer.name})."