aws-dynamodb-streams-read
dynamodb:GetShardIterator + dynamodb:GetRecords on a DynamoDB Stream yields real-time access to all item-level changes, including full item images depending on StreamViewType.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?stream) == Messaging
node_type(?table) ==
NoSQLDatabase
?stream.provider_type == 'AWS::DynamoDB::Stream' or arn_matches(?stream, ':table/*/stream/*')
?principal has EFFECTIVE dynamodb:GetShardIterator on ?stream ARN
?principal has EFFECTIVE dynamodb:GetRecords on ?stream ARN
emit
| source type | Identity |
|---|---|
| target type | NoSQLDatabase |
| source | ?principal |
| target | ?table |
| permissions | dynamodb:GetShardIterator dynamodb:GetRecords |
| conditions | iam_permission resource_policy service_state |
| state logic | ACTIVE when BOTH dynamodb:GetShardIterator AND dynamodb:GetRecords are confirmed EFFECTIVE on the stream ARN and the parent table has DynamoDB Streams enabled (StreamSpecification.StreamEnabled == true). CONDITIONAL(service_state) when Streams is not enabled on the parent table — GetShardIterator and GetRecords succeed only for the 24-hour shard retention window; after 24 hours with no new writes the stream is effectively empty. CONDITIONAL(resource_policy) when a stream resource policy exists but its evaluation is unresolved. BLOCKED by an explicit SCP or deny on either GetShardIterator or GetRecords. The stream only captures CHANGES since stream enablement — it does NOT provide historical data for items written before Streams was enabled. |
Narrative
{principal.name} can read the DynamoDB stream of {table.name} (dynamodb:GetShardIterator + GetRecords), accessing a real-time feed of all item mutations{if streamViewType != KEYS_ONLY: " including full item images"}.
Raw rule rules/derived/aws/dynamodb.yaml
id: aws-dynamodb-streams-read
emits: CanReadData
description: dynamodb:GetShardIterator + dynamodb:GetRecords on a DynamoDB Stream yields real-time access
to all item-level changes, including full item images depending on StreamViewType.
match:
- - principal: null
- HasPermission
- stream: null
- - stream: null
- LocatedIn
- table: null
where:
- node_type(?stream) == Messaging
- node_type(?table) == NoSQLDatabase
- ?stream.provider_type == 'AWS::DynamoDB::Stream' or arn_matches(?stream, ':table/*/stream/*')
- ?principal has EFFECTIVE dynamodb:GetShardIterator on ?stream ARN
- ?principal has EFFECTIVE dynamodb:GetRecords on ?stream ARN
emit:
source_type: Identity
target_type: NoSQLDatabase
source: ?principal
target: ?table
permissions:
- dynamodb:GetShardIterator
- dynamodb:GetRecords
conditions:
- iam_permission
- resource_policy
- service_state
state_logic: "ACTIVE when BOTH dynamodb:GetShardIterator AND dynamodb:GetRecords are confirmed EFFECTIVE\
\ on the stream ARN and the parent table has DynamoDB Streams enabled (StreamSpecification.StreamEnabled\
\ == true). CONDITIONAL(service_state) when Streams is not enabled on the parent table \u2014 GetShardIterator\
\ and GetRecords succeed only for the 24-hour shard retention window; after 24 hours with no new writes\
\ the stream is effectively empty. CONDITIONAL(resource_policy) when a stream resource policy exists\
\ but its evaluation is unresolved. BLOCKED by an explicit SCP or deny on either GetShardIterator\
\ or GetRecords. The stream only captures CHANGES since stream enablement \u2014 it does NOT provide\
\ historical data for items written before Streams was enabled."
confidence: min(contributing_confidences) * 0.90
derived_from:
- ?principal HasPermission ?stream (dynamodb:GetShardIterator + GetRecords effective)
false_positive_note: "BOTH GetShardIterator AND GetRecords are required; GetShardIterator alone cannot\
\ retrieve item data. The StreamViewType determines data richness: KEYS_ONLY reveals only partition/sort\
\ keys (lower sensitivity); NEW_IMAGE, OLD_IMAGE, and NEW_AND_OLD_IMAGES reveal full item attribute\
\ values (higher sensitivity). Emit with the parent table as target (CanReadData on the NoSQLDatabase);\
\ the stream is the access mechanism, not the data target. Streams have a 24-hour shard retention;\
\ old shards expire. SSE-KMS on the parent table does NOT additionally encrypt the stream \u2014 stream\
\ records are always decryptable by a principal with GetRecords, regardless of the table CMK. Do NOT\
\ emit CONDITIONAL(key_permission) for stream access based on the parent table's SSE setting."
narrative: '{principal.name} can read the DynamoDB stream of {table.name} (dynamodb:GetShardIterator
+ GetRecords), accessing a real-time feed of all item mutations{if streamViewType != KEYS_ONLY: "
including full item images"}.'