aws-secretsmanager-cmk-gated-read
Gates the existing explicit CanReadSecret edge on kms:Decrypt (CONDITIONAL(key_permission)) when the secret is CMK-encrypted.
derived
aws
emits
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_class(?secret) ==
Secret
?principal has EFFECTIVE secretsmanager:GetSecretValue on ?secret
?secret.KmsKeyId is a customer-managed CMK (NOT alias/aws/secretsmanager)
emit
Raw rule rules/derived/aws/secretsmanager.yaml
id: aws-secretsmanager-cmk-gated-read
kind: state_annotation
annotates: CanReadSecret
description: Gates the existing explicit CanReadSecret edge on kms:Decrypt (CONDITIONAL(key_permission))
when the secret is CMK-encrypted.
match:
- - principal: null
- CanReadSecret
- secret: null
where:
- node_class(?secret) == Secret
- ?principal has EFFECTIVE secretsmanager:GetSecretValue on ?secret
- ?secret.KmsKeyId is a customer-managed CMK (NOT alias/aws/secretsmanager)
annotation:
target_edge:
- principal: null
- CanReadSecret
- secret: null
set_state: CONDITIONAL
set_conditions:
- iam_permission
- resource_policy
- key_permission
state_logic: "Set CONDITIONAL(key_permission) \u2014 the caller holds GetSecretValue but decrypt on\
\ the wrapping CMK is unproven. The generic decrypt-gates-secret-read rule upgrades this edge to ACTIVE\
\ iff the caller also holds CanDecrypt on the exact CMK that wraps this secret. BLOCKED if a key-policy/SCP\
\ Deny hits kms:Decrypt or a resource-policy Deny hits GetSecretValue."
derived_from:
- <explicit CanReadSecret(GetSecretValue) edge_id>
- <wraps(?cmk,?secret) link>
false_positive_note: Secrets Manager calls kms:Decrypt on the caller's behalf against the secret's CMK;
GetSecretValue alone WITHOUT decrypt on that CMK yields AccessDeniedException. Do NOT leave the explicit
edge ACTIVE from GetSecretValue alone on a CMK secret. The decrypt must be on the EXACT CMK that wraps
THIS secret (DescribeSecret.KmsKeyId), honoring kms:ViaService/EncryptionContext conditions. This
rule SETS the gate; the generic decrypt-gates-secret-read rule performs the upgrade. Produces no edge.
narrative: "{principal.name} can call secretsmanager:GetSecretValue on {secret.name}, but the value\
\ is CMK-encrypted \u2014 reading the plaintext also requires kms:Decrypt on the wrapping CMK; without\
\ it this stays CONDITIONAL(key_permission)."