gcp-pubsub-cross-project-publish-trust

A Pub/Sub topic IAM binding granting pubsub.topics.publish to a principal from another GCP project is cross-project publish trust; feeds can-control's CanEnterProject derivation via messaging-chains.

derived gcp emits CrossProjectTrust

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 or roles/pubsub.admin', "the member's project (extracted from SA email or user domain) != the topic's project", 'the member is NOT allUsers or allAuthenticatedUsers (those are ExposedToInternet, see rule 5)']}

where

node_type(?policy) == ResourcePolicy

emit

source typeResourcePolicy
target typeProject
source<topic ResourcePolicy node>
target<GCP project of the foreign member>
conditionsresource_policy condition_expression
state logicACTIVE when the topic IAM binding unconditionally grants pubsub.topics.publish to a named external SA or user (no IAM condition on the binding). CONDITIONAL (condition_expression) when the binding has an IAM condition expression that the external principal must satisfy (e.g. request.time, resource.name prefix) — the cross-project trust is gated. BLOCKED by an org policy (constraints/iam.allowedPolicyMemberDomains or constraints/gcp.restrictCrossProjectCommunication) that prevents the cross-project grant from being effective.

Narrative

The IAM policy on Pub/Sub topic {topic.name} grants pubsub.topics.publish to a principal in project {foreign_project}; this cross-project trust lets that external principal publish to {topic.name} and thereby (via messaging-chains) trigger any push-subscription consumer in {topic.project}.

Raw rule rules/derived/gcp/pubsub.yaml

id: gcp-pubsub-cross-project-publish-trust
emits: CrossProjectTrust
description: A Pub/Sub topic IAM binding granting pubsub.topics.publish to a principal from another GCP
  project is cross-project publish trust; feeds can-control's CanEnterProject derivation via messaging-chains.
match:
- match_record:
    resource_type: google.pubsub.Topic
    field: iamPolicy.bindings
  where:
  - a binding grants pubsub.topics.publish or roles/pubsub.publisher or roles/pubsub.admin
  - the member's project (extracted from SA email or user domain) != the topic's project
  - the member is NOT allUsers or allAuthenticatedUsers (those are ExposedToInternet, see rule 5)
where:
- node_type(?policy) == ResourcePolicy
emit:
  source_type: ResourcePolicy
  target_type: Project
  source: <topic ResourcePolicy node>
  target: <GCP project of the foreign member>
  permissions: []
  conditions:
  - resource_policy
  - condition_expression
  state_logic: "ACTIVE when the topic IAM binding unconditionally grants pubsub.topics.publish to a named\
    \ external SA or user (no IAM condition on the binding). CONDITIONAL (condition_expression) when the\
    \ binding has an IAM condition expression that the external principal must satisfy (e.g. request.time,\
    \ resource.name prefix) \u2014 the cross-project trust is gated. BLOCKED by an org policy (constraints/iam.allowedPolicyMemberDomains\
    \ or constraints/gcp.restrictCrossProjectCommunication) that prevents the cross-project grant from\
    \ being effective."
  confidence: 0.95
  derived_from:
  - pubsub.topics.getIamPolicy[bindings, member, condition]
  false_positive_note: "CITE, DO NOT DUPLICATE messaging-chains rule 4 and can-control: this rule asserts\
    \ only the CrossProjectTrust FACT; the CanEnterProject consequence is produced by can-control consuming\
    \ CrossProjectTrust together with CanTrigger/CanExecuteAs from messaging-chains rules 1-2. Honor IAM\
    \ condition expressions on the binding \u2014 a condition-scoped binding is CONDITIONAL, not an unconditional\
    \ cross-project trust. A foreign SA binding that is also denied by an IAM deny policy at project/folder/org\
    \ level is BLOCKED. A user SA whose project is the SAME as the topic's project is not cross-project\
    \ (compare the project number in the SA email, not just the project ID string). pubsub.subscriptions.consume\
    \ granted cross-project is a separate CrossProjectTrust edge on the subscription ResourcePolicy \u2014\
    \ not covered by this rule (emit separately from pubsub.subscriptions.getIamPolicy)."
  narrative: The IAM policy on Pub/Sub topic {topic.name} grants pubsub.topics.publish to a principal
    in project {foreign_project}; this cross-project trust lets that external principal publish to {topic.name}
    and thereby (via messaging-chains) trigger any push-subscription consumer in {topic.project}.
move · open · esc close