azure-adls-public-container

ADLS Gen2 account with public blob access enabled and a public container exposes blob data to unauthenticated internet access.

derived azure emits ExposedToInternet

match

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

{'account': None} Contains {'container': None}

where

node_class(?account) == Data and node_type(?account) == DataLake ?account.properties.isHnsEnabled == true ?account.properties.publicNetworkAccess != Disabled # publicNetworkAccess=Disabled overrides allowBlobPublicAccess and closes the public endpoint entirely ?account.properties.allowBlobPublicAccess == true # account-level gate; if false, container publicAccess is ignored node_type(?container) == ObjectStorage # blobServices/containers node ?container.properties.publicAccess in ['Blob', 'Container'] # Blob=blob-level anon read; Container=list+read

emit

source typeStorage
target typeAnonymousIdentity
source?container
target<AnonymousIdentity>
conditionsnetwork_reachability
state logicif ?account.properties.publicNetworkAccess == Disabled: DO NOT EMIT (public endpoint is fully closed regardless of allowBlobPublicAccess or container publicAccess settings — this is a blocking gate); elif ?account.properties.allowBlobPublicAccess == false: DO NOT EMIT (container publicAccess is ignored when account-level flag is false); elif networkAcls.defaultAction == Deny AND public internet is not in allowlist: CONDITIONAL(network_reachability) — the public access setting is enabled but the network firewall blocks public internet traffic; elif ?container.properties.publicAccess == 'Container': ACTIVE (anonymous list+read); elif ?container.properties.publicAccess == 'Blob': ACTIVE (anonymous blob read, no listing).

Narrative

{container.name} in {account.name} is publicly readable from the internet without authentication: the account has allowBlobPublicAccess=true and the container publicAccess={container.properties.publicAccess}. Any internet user can read{if publicAccess=='Container': ' and list'} blobs in this container.

Raw rule rules/derived/azure/adls.yaml

id: azure-adls-public-container
emits: ExposedToInternet
description: ADLS Gen2 account with public blob access enabled and a public container exposes blob data
  to unauthenticated internet access.
match:
- - account: null
  - Contains
  - container: null
where:
- node_class(?account) == Data and node_type(?account) == DataLake
- ?account.properties.isHnsEnabled == true
- '?account.properties.publicNetworkAccess != Disabled  # publicNetworkAccess=Disabled overrides allowBlobPublicAccess
  and closes the public endpoint entirely'
- '?account.properties.allowBlobPublicAccess == true  # account-level gate; if false, container publicAccess
  is ignored'
- 'node_type(?container) == ObjectStorage  # blobServices/containers node'
- '?container.properties.publicAccess in [''Blob'', ''Container'']  # Blob=blob-level anon read; Container=list+read'
emit:
  source_type: Storage
  target_type: AnonymousIdentity
  source: ?container
  target: <AnonymousIdentity>
  permissions: []
  conditions:
  - network_reachability
  state_logic: "if ?account.properties.publicNetworkAccess == Disabled: DO NOT EMIT (public endpoint is\
    \ fully closed regardless of allowBlobPublicAccess or container publicAccess settings \u2014 this\
    \ is a blocking gate); elif ?account.properties.allowBlobPublicAccess == false: DO NOT EMIT (container\
    \ publicAccess is ignored when account-level flag is false); elif networkAcls.defaultAction == Deny\
    \ AND public internet is not in allowlist: CONDITIONAL(network_reachability) \u2014 the public access\
    \ setting is enabled but the network firewall blocks public internet traffic; elif ?container.properties.publicAccess\
    \ == 'Container': ACTIVE (anonymous list+read); elif ?container.properties.publicAccess == 'Blob':\
    \ ACTIVE (anonymous blob read, no listing)."
  confidence: '0.93'
  derived_from:
  - "<Contains edge_id (account \u2192 container)>"
  - <container publicAccess property record>
  - <account allowBlobPublicAccess property record>
  false_positive_note: "CRITICAL: the account-level `allowBlobPublicAccess=false` setting OVERRIDES any\
    \ container-level publicAccess \u2014 the storage service returns 409 if public access is attempted\
    \ on a container under a public-blocked account. Do NOT emit ExposedToInternet if the account flag\
    \ is false, even if the container property says Blob or Container. A networkAcls.defaultAction=Deny\
    \ firewall with no public internet IP allowlist blocks the public endpoint \u2014 downgrade to CONDITIONAL(network_reachability).\
    \ Note that publicAccess=Container allows directory listing (a superset of Blob-only access) \u2014\
    \ treat it as higher impact. This is purely anonymous data exposure (no Entra identity involved);\
    \ use target AnonymousIdentity, not a tenant/account boundary node."
  narrative: '{container.name} in {account.name} is publicly readable from the internet without authentication:
    the account has allowBlobPublicAccess=true and the container publicAccess={container.properties.publicAccess}.
    Any internet user can read{if publicAccess==''Container'': '' and list''} blobs in this container.'
move · open · esc close