From 9119795793e7639d0a74d54f974ec6308dd0fca2 Mon Sep 17 00:00:00 2001 From: Lee Hannigan <29015211+LeeroyHannigan@users.noreply.github.com> Date: Sat, 31 Oct 2026 00:45:38 +0000 Subject: [PATCH 01/14] fix(server): refuse stream ARNs outside this account, partition and region DescribeStream and GetShardIterator served the caller's own stream for a StreamArn that named another account, another partition, another region, an upper-cased region or the service spelled DYNAMODB. Authorization rebuilt every stream ARN with the server's partition, service and region and the caller's account, and the stream engine resolved the stream by table name and label, so the fields a client might get wrong were ignored on both sides. The tag operations answered the same stream ARN with "Invalid TableArn: Invalid ResourceArn provided as input ". Measured on DynamoDB on 2026-10-23 for an admin, a no-permissions user, a user allowed the exact stream ARN and a user with a Deny on it. On StreamArn, every principal gets AccessDeniedException "Access is denied" for another account and ValidationException "Invalid StreamArn" for aws-cn, aws-us-gov, DynamoDB, DYNAMODB, us-west-2 or US-EAST-1, so both checks run before the IAM decision, account first. On a tag operation's ResourceArn, another account is "Access is denied" for every principal; the other spellings are authorized as sent (a no-permissions principal is denied naming the ARN it sent, a principal allowed only the canonical ARN is denied the respelled one) and then refused with "Invalid StreamArn". A GetRecords token the service did not issue is "Invalid ShardIterator". A stream ARN is now checked in the server before the IAM decision: on StreamArn for its account, partition, service and region (check_stream_arn_scope); on a tag operation's ResourceArn for its account (check_stream_arn_account), with the ARN then authorized as sent and the engine refusing another partition, service or region with "Invalid StreamArn" after authorization; inside a GetRecords iterator for all four fields, refusing the token as Invalid. Wire tests in tests/test_stream_arn_scope.py cover the four principals, five operations and seventeen spellings with the measured message text; test_authz_resource_arn.py and test_cross_account_isolation.py are updated to the measured answers. Fixes #450 Fixes #451 --- crates/engine/src/tagging.rs | 20 + crates/server/src/authz_resource.rs | 182 ++++--- .../server/src/authz_resource/stream_scope.rs | 249 +++++++++ crates/server/src/request_helpers.rs | 15 +- docs/manuals/07-upgrade-manual.md | 6 + tests/test_authz_resource_arn.py | 15 +- tests/test_cross_account_isolation.py | 10 +- tests/test_stream_arn_scope.py | 489 ++++++++++++++++++ 8 files changed, 902 insertions(+), 84 deletions(-) create mode 100644 crates/server/src/authz_resource/stream_scope.rs create mode 100644 tests/test_stream_arn_scope.py diff --git a/crates/engine/src/tagging.rs b/crates/engine/src/tagging.rs index 432d75dd4..669b386aa 100755 --- a/crates/engine/src/tagging.rs +++ b/crates/engine/src/tagging.rs @@ -34,6 +34,13 @@ fn tag_subresource_not_table_or_stream(resource: &str) -> bool { !matches!(segs.next(), None | Some("") | Some("stream")) } +/// True for a `table//stream/