azure-appconfig-connstring-reads-all-kv

A listKeys holder uses the access-key connection string to read every secret key-value, bypassing data-plane RBAC.

derived azure emits CanReadSecret

match

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

{'principal': None} HasPermission {'store': None} {'store': None} Contains {'dataplane': None} {'dataplane': None} ExposesCredential {'connstring': None} {'connstring': None} CredentialsFor {'dataplane': None} {'dataplane': None} ContainsSecret {'keyvalue': None}

where

node_class(?principal) == Identity node_class(?store) == ManagementService provider_type(?store) == 'Microsoft.AppConfiguration/configurationStores' node_class(?dataplane) == Data node_class(?connstring) == Secret # ?connstring is Secret-class (subtype Credential), so it is a valid CredentialsFor source (source set [Secret, Credential, AccessKey, Token, Certificate]) and a valid ExposesCredential target (Credential) node_type(?connstring) == Credential ?principal has EFFECTIVE Microsoft.AppConfiguration/configurationStores/listKeys/action at the store scope node_type(?keyvalue) in [Secret, ConnectionString, APIKey, Password] ?keyvalue is a plaintext key-value (not a keyvaultref)

emit

source?principal
target?keyvalue
permissionsMicrosoft.AppConfiguration/configurationStores/listKeys/action
conditionsiam_permission service_state
state logicACTIVE when listKeys/action is effective AND disableLocalAuth == false (a valid connection string is obtainable). A read-only OR read-write key both read key-values. BLOCKED if disableLocalAuth == true (no key) or the read action is denied. This is the RBAC-BYPASS: it does NOT require a data-plane role.

Narrative

{principal.name} can read secret key-value {keyvalue.name} in {store.name} via its access-key connection string (listKeys), bypassing data-plane RBAC.

Raw rule rules/derived/azure/appconfig.yaml

id: azure-appconfig-connstring-reads-all-kv
emits: CanReadSecret
description: A listKeys holder uses the access-key connection string to read every secret key-value, bypassing
  data-plane RBAC.
match:
- - principal: null
  - HasPermission
  - store: null
- - store: null
  - Contains
  - dataplane: null
- - dataplane: null
  - ExposesCredential
  - connstring: null
- - connstring: null
  - CredentialsFor
  - dataplane: null
- - dataplane: null
  - ContainsSecret
  - keyvalue: null
where:
- node_class(?principal) == Identity
- node_class(?store) == ManagementService
- provider_type(?store) == 'Microsoft.AppConfiguration/configurationStores'
- node_class(?dataplane) == Data
- 'node_class(?connstring) == Secret  # ?connstring is Secret-class (subtype Credential), so it is a valid
  CredentialsFor source (source set [Secret, Credential, AccessKey, Token, Certificate]) and a valid ExposesCredential
  target (Credential)'
- node_type(?connstring) == Credential
- ?principal has EFFECTIVE Microsoft.AppConfiguration/configurationStores/listKeys/action at the store
  scope
- node_type(?keyvalue) in [Secret, ConnectionString, APIKey, Password]
- ?keyvalue is a plaintext key-value (not a keyvaultref)
emit:
  source: ?principal
  target: ?keyvalue
  permissions:
  - Microsoft.AppConfiguration/configurationStores/listKeys/action
  conditions:
  - iam_permission
  - service_state
  state_logic: 'ACTIVE when listKeys/action is effective AND disableLocalAuth == false (a valid connection
    string is obtainable). A read-only OR read-write key both read key-values. BLOCKED if disableLocalAuth
    == true (no key) or the read action is denied. This is the RBAC-BYPASS: it does NOT require a data-plane
    role.'
  confidence: '0.95'
  derived_from:
  - ?principal HasPermission ?store (listKeys/action)
  - ?dataplane ExposesCredential ?connstring
  - ?connstring CredentialsFor ?dataplane
  - ?dataplane ContainsSecret ?keyvalue
  false_positive_note: 'The connection string reads the WHOLE data plane, so emit once per secret-bearing
    key-value. This is what makes a listKeys holder (Contributor/Owner) equivalent to a data reader for
    secrets WITHOUT any App Configuration data role. BLOCKED when disableLocalAuth=true. KV-reference
    key-values are still excluded (their value is a URI). Source is the Identity principal (schema: CanReadSecret
    source is [Identity, Compute]), not the connection string. Depends on the ExposesCredential edge from
    azure-appconfig-listkeys-exposes-connstring (fixpoint ordering); ?connstring is a Secret-class Credential
    node, conformant as the CredentialsFor source.'
  narrative: '{principal.name} can read secret key-value {keyvalue.name} in {store.name} via its access-key
    connection string (listKeys), bypassing data-plane RBAC.'
move · open · esc close