aws-keyspaces-select-read-data
IAM-effective cassandra:Select on a Keyspaces table allows full data-plane read (CQL SELECT), yielding all row data including any secret material.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?table) ==
NoSQLDatabase
?table.provider_type == 'AWS::Cassandra::Table'
effective_action(?principal, ?table) includes 'cassandra:Select'
emit
| source type | Identity |
|---|---|
| target type | NoSQLDatabase |
| source | ?principal |
| target | ?table |
| permissions | cassandra:Select |
| state logic | if ?table.encryptionSpecification.kmsKeyIdentifier is a customer-managed key (not alias/aws/cassandra or AWS-managed): CONDITIONAL(key_permission) — the caller needs kms:Decrypt on the CMK for AWS Keyspaces to return decrypted data. When the principal is confirmed to hold CanDecrypt on that exact CMK, a provider-agnostic linchpin rule (TBD) should upgrade this to ACTIVE. Otherwise (SSE uses the AWS-managed key alias/aws/cassandra): ACTIVE. |
Narrative
{principal.name} can run CQL SELECT statements against {table.name} (cassandra:Select), reading all application data stored in the table. If {table.name} contains credentials or secrets, this yields further pivots via credential-chains:read-secret-yields-identity.
Raw rule rules/derived/aws/keyspaces.yaml
id: aws-keyspaces-select-read-data
emits: CanReadData
description: IAM-effective cassandra:Select on a Keyspaces table allows full data-plane read (CQL SELECT),
yielding all row data including any secret material.
match:
- - principal: null
- HasPermission
- table: null
where:
- node_type(?table) == NoSQLDatabase
- ?table.provider_type == 'AWS::Cassandra::Table'
- effective_action(?principal, ?table) includes 'cassandra:Select'
emit:
source_type: Identity
target_type: NoSQLDatabase
source: ?principal
target: ?table
permissions:
- cassandra:Select
conditions_dynamic: 'if ?table.encryptionSpecification.kmsKeyIdentifier is a customer-managed key (not
alias/aws/cassandra or AWS-managed key): [iam_permission, key_permission]; else: [iam_permission]'
state_logic: "if ?table.encryptionSpecification.kmsKeyIdentifier is a customer-managed key (not alias/aws/cassandra\
\ or AWS-managed): CONDITIONAL(key_permission) \u2014 the caller needs kms:Decrypt on the CMK for\
\ AWS Keyspaces to return decrypted data. When the principal is confirmed to hold CanDecrypt on that\
\ exact CMK, a provider-agnostic linchpin rule (TBD) should upgrade this to ACTIVE. Otherwise (SSE\
\ uses the AWS-managed key alias/aws/cassandra): ACTIVE."
confidence: contributing_confidence * 0.95
derived_from:
- <HasPermission edge for cassandra:Select on ?table>
false_positive_note: "Honor the exact resource scope of the cassandra:Select permission \u2014 a grant\
\ scoped to system keyspace ARNs (system_schema, system_schema_mcs) does not confer access to application-data\
\ tables. Verify the effective resource ARN includes the application table ARN. cassandra:Select at\
\ the keyspace ARN (arn:aws:cassandra:<region>:<account>:/keyspace/<keyspace-name>) grants read on\
\ all tables in that keyspace; apply the same CMK check per table's encryptionSpecification.kmsKeyIdentifier.\
\ Do not emit ACTIVE when the table uses a customer-managed KMS key and the principal's kms:Decrypt\
\ on that key has not been confirmed; emit CONDITIONAL(key_permission) instead."
narrative: '{principal.name} can run CQL SELECT statements against {table.name} (cassandra:Select),
reading all application data stored in the table. If {table.name} contains credentials or secrets,
this yields further pivots via credential-chains:read-secret-yields-identity.'