gcp-pubsub-topic-public-publish
allUsers or allAuthenticatedUsers granted pubsub.topics.publish on a topic makes it publicly writable; any internet caller can trigger bound push-subscription consumers via messaging-chains without project credentials.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'match_record': {'resource_type': 'google.pubsub.Topic', 'field': 'iamPolicy.bindings'}, 'where': ['a binding grants pubsub.topics.publish or roles/pubsub.publisher to allUsers OR allAuthenticatedUsers']}
where
node_type(?topic) ==
Topic
emit
| source type | Topic |
|---|---|
| target type | AnonymousIdentity |
| source | <Pub/Sub topic node> |
| target | <AnonymousIdentity> |
| conditions | resource_policy |
| state logic | ACTIVE when the topic IAM binding unconditionally grants pubsub.topics.publish to allUsers (fully unauthenticated) or allAuthenticatedUsers (any Google account — treat as effectively internet-accessible). No IAM condition can restrict allUsers at the topic level (it is the wildcard principal). CONDITIONAL (resource_policy) when a condition expression on the binding would further scope it (unusual for allUsers but theoretically possible). BLOCKED by an org policy (constraints/iam.allowedPolicyMemberDomains) that prevents allUsers/ allAuthenticatedUsers grants at this project/folder/org. |
Narrative
Pub/Sub topic {topic.name} grants pubsub.topics.publish to {member} (public); any internet caller can publish messages to {topic.name}, enabling the Phase-10 trigger chain for any push-subscription consumer without project credentials via messaging-chains.
Raw rule rules/derived/gcp/pubsub.yaml
id: gcp-pubsub-topic-public-publish
emits: ExposedToInternet
description: allUsers or allAuthenticatedUsers granted pubsub.topics.publish on a topic makes it publicly
writable; any internet caller can trigger bound push-subscription consumers via messaging-chains without
project credentials.
match:
- match_record:
resource_type: google.pubsub.Topic
field: iamPolicy.bindings
where:
- a binding grants pubsub.topics.publish or roles/pubsub.publisher to allUsers OR allAuthenticatedUsers
where:
- node_type(?topic) == Topic
emit:
source_type: Topic
target_type: AnonymousIdentity
source: <Pub/Sub topic node>
target: <AnonymousIdentity>
permissions: []
conditions:
- resource_policy
state_logic: "ACTIVE when the topic IAM binding unconditionally grants pubsub.topics.publish to allUsers\
\ (fully unauthenticated) or allAuthenticatedUsers (any Google account \u2014 treat as effectively\
\ internet-accessible). No IAM condition can restrict allUsers at the topic level (it is the wildcard\
\ principal). CONDITIONAL (resource_policy) when a condition expression on the binding would further\
\ scope it (unusual for allUsers but theoretically possible). BLOCKED by an org policy (constraints/iam.allowedPolicyMemberDomains)\
\ that prevents allUsers/ allAuthenticatedUsers grants at this project/folder/org."
confidence: 0.97
derived_from:
- "pubsub.topics.getIamPolicy[bindings \u2014 allUsers/allAuthenticatedUsers member]"
false_positive_note: "allUsers is genuinely anonymous (no Google sign-in required); allAuthenticatedUsers\
\ requires any valid Google account \u2014 an internet attacker with a free Google account qualifies.\
\ Both are modeled as ExposedToInternet for path scoring. An org policy (constraints/iam.allowedPolicyMemberDomains)\
\ that excludes these members makes the grant ineffective -> BLOCKED. Only pubsub.topics.publish (or\
\ a role that includes it) creates the ExposedToInternet fact; a subscribe-only wildcard grant is\
\ a data-exfil exposure but does not produce ExposedToInternet for the trigger chain (emit separately\
\ as CanReadData with anonymous source)."
narrative: Pub/Sub topic {topic.name} grants pubsub.topics.publish to {member} (public); any internet
caller can publish messages to {topic.name}, enabling the Phase-10 trigger chain for any push-subscription
consumer without project credentials via messaging-chains.