azure-evg-read-dlq

A principal with read access to an Event Grid subscription's dead-letter destination (Storage blob container) can read all failed/expired event payloads, including any credential material they contain.

derived azure emits CanReadData

match

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

{'principal': None} CanReadData {'storage_container': None} {'subscription': None} HasPolicy {'dlq_policy': None}

where

node_type(?principal) in [Identity, Compute] node_type(?storage_container) in [ObjectStorage] node_type(?subscription) == EventRule ?subscription.properties.deadLetterDestination.properties.resourceId == ?storage_container.id ?dlq_policy is the dead-letter destination configuration on the subscription

emit

source typeIdentity
target typeGenericStorage
source?principal
target?storage_container
permissionsMicrosoft.Storage/storageAccounts/blobServices/containers/blobs/read (or Storage Blob Data Reader role)
state logicACTIVE when the principal has effective read permission on the storage container AND the subscription is configured with a dead-letter destination pointing to that container. The principal can read all event blobs in the dead-letter container, including events that failed delivery or expired. BLOCKED if a deny assignment covers blob read on the container or if no dead-letter destination is configured.

Narrative

{principal.name} has read access to the Storage container {target.name} that serves as the dead-letter destination for Event Grid subscription on topic {subscription.topic}; it can read all failed/expired event payloads in the container, including any credential material embedded in those events.

Raw rule rules/derived/azure/eventgrid.yaml

id: azure-evg-read-dlq
emits: CanReadData
description: A principal with read access to an Event Grid subscription's dead-letter destination (Storage
  blob container) can read all failed/expired event payloads, including any credential material they contain.
match:
- - principal: null
  - CanReadData
  - storage_container: null
- - subscription: null
  - HasPolicy
  - dlq_policy: null
where:
- node_type(?principal) in [Identity, Compute]
- node_type(?storage_container) in [ObjectStorage]
- node_type(?subscription) == EventRule
- ?subscription.properties.deadLetterDestination.properties.resourceId == ?storage_container.id
- ?dlq_policy is the dead-letter destination configuration on the subscription
emit:
  source_type: Identity
  target_type: GenericStorage
  source: ?principal
  target: ?storage_container
  permissions:
  - Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read (or Storage Blob Data Reader
    role)
  conditions: []
  state_logic: ACTIVE when the principal has effective read permission on the storage container AND the
    subscription is configured with a dead-letter destination pointing to that container. The principal
    can read all event blobs in the dead-letter container, including events that failed delivery or expired.
    BLOCKED if a deny assignment covers blob read on the container or if no dead-letter destination is
    configured.
  confidence: 0.92
  derived_from:
  - '?principal CanReadData ?storage_container: effective Storage blob read permission'
  - 'subscription.properties.deadLetterDestination.properties.resourceId: configuration link to DLQ container'
  - 'messaging-chains.yaml: messaging-read-data-exfiltration (if DLQ contains credentials, credential-chains
    fires)'
  false_positive_note: The dead-letter queue is populated with events that failed delivery after max retries
    or that expired before delivery. These failures are often transient (endpoint down, slow network),
    so the DLQ may be infrequently populated. However, if a custom topic receives events with sensitive
    data and event delivery fails systematically (e.g., webhook endpoint unreachable), the DLQ accumulates
    those events. Confirm that the subscription actually uses a dead-letter destination (not all subscriptions
    do); check deadLetterDestination.properties.resourceId on the subscription object. If the container
    is in a different subscription and the principal crosses that boundary to read it, check the cross-subscription
    path separately.
  narrative: '{principal.name} has read access to the Storage container {target.name} that serves as the
    dead-letter destination for Event Grid subscription on topic {subscription.topic}; it can read all
    failed/expired event payloads in the container, including any credential material embedded in those
    events.'
move · open · esc close