azure-mhsm-decrypt-to-credential
A wrapped store whose wrapping key the principal can decrypt exposes the credential it holds.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
{'principal': None}
CanDecrypt {'key': None}
{'wrapped': None} ContainsCredential {'cred': None}
{'cred': None} CredentialsFor {'identity': None}
where
?key.provider_type == 'Microsoft.KeyVault/managedHSMs/keys'
wraps(?key, ?wrapped) — ?key is the wrapping/encryption key protecting ?wrapped (evidenced DEK/keyvault wrap reference)
node_class(?wrapped) in [Compute, Storage, Data, Messaging,
Secret] (ExposesCredential source set)
node_class(?identity) == Identity (ExposesCredential target set)
emit
| source | ?wrapped |
|---|---|
| target | ?identity |
| permissions | Microsoft.KeyVault/managedHsm/keys/decrypt/action Microsoft.KeyVault/managedHsm/keys/unwrap/action |
| conditions | key_permission |
| state logic | inherit_weakest(CanDecrypt): ACTIVE if the CanDecrypt input is ACTIVE and the wrap relationship is evidenced; if CanDecrypt is POTENTIAL/CONDITIONAL, so is this; BLOCKED if CanDecrypt is BLOCKED |
Narrative
{principal.name} can decrypt {key.name} that wraps {wrapped.name}, exposing {cred.name} - a credential that authenticates as {identity.name}.
Raw rule rules/derived/azure/managedhsm.yaml
id: azure-mhsm-decrypt-to-credential
emits: ExposesCredential
description: A wrapped store whose wrapping key the principal can decrypt exposes the credential it holds.
match:
- - principal: null
- CanDecrypt
- key: null
- - wrapped: null
- ContainsCredential
- cred: null
- - cred: null
- CredentialsFor
- identity: null
where:
- ?key.provider_type == 'Microsoft.KeyVault/managedHSMs/keys'
- "wraps(?key, ?wrapped) \u2014 ?key is the wrapping/encryption key protecting ?wrapped (evidenced DEK/keyvault\
\ wrap reference)"
- node_class(?wrapped) in [Compute, Storage, Data, Messaging, Secret] (ExposesCredential source set)
- node_class(?identity) == Identity (ExposesCredential target set)
emit:
source: ?wrapped
target: ?identity
permissions:
- Microsoft.KeyVault/managedHsm/keys/decrypt/action
- Microsoft.KeyVault/managedHsm/keys/unwrap/action
conditions:
- key_permission
state_logic: 'inherit_weakest(CanDecrypt): ACTIVE if the CanDecrypt input is ACTIVE and the wrap relationship
is evidenced; if CanDecrypt is POTENTIAL/CONDITIONAL, so is this; BLOCKED if CanDecrypt is BLOCKED'
confidence: min(contributing_confidences) * 0.9
derived_from:
- ?principal CanDecrypt ?key
- ?wrapped ContainsCredential ?cred
- ?cred CredentialsFor ?identity
- wraps(?key, ?wrapped)
false_positive_note: "This is a genuinely new edge (the wrapped store -> the identity its wrapped credential\
\ authenticates as); it does NOT re-emit the CredentialsFor input. Only fires when the material the\
\ key wraps/encrypts is ITSELF a credential (a wrapped DEK protecting a secret store, a wrapped SP\
\ key, an encrypted connection string) evidenced by ContainsCredential + CredentialsFor. A key used\
\ only for disk-block/CMK envelope encryption of non-credential data does NOT yield an identity credential\
\ \u2014 keep the CanDecrypt edge and do not emit this pivot. The decrypt-of-wrapped-credential value\
\ lives in the contributing CanDecrypt edge; this edge only surfaces the exposure so credential-centric\
\ queries and credential-chains.yaml can complete the impersonation roll-up."
narrative: "{principal.name} can decrypt {key.name} that wraps {wrapped.name}, exposing {cred.name}\
\ \u2014 a credential that authenticates as {identity.name}."