Skip to content

feat(s3,sqs,sns): wire Phase 1 IAM enforcement#399

Merged
vieiralucas merged 2 commits intomainfrom
worktree-sigv4-iam-batch8-sqs-sns-s3
Apr 15, 2026
Merged

feat(s3,sqs,sns): wire Phase 1 IAM enforcement#399
vieiralucas merged 2 commits intomainfrom
worktree-sigv4-iam-batch8-sqs-sns-s3

Conversation

@vieiralucas
Copy link
Copy Markdown
Member

@vieiralucas vieiralucas commented Apr 14, 2026

Summary

Extends the IAM enforcement surface (started in PR #395 with IAM+STS) to cover S3 + SQS + SNS — the three most-used queue/storage services. Combined with the IAM+STS coverage from #395, this matches the same enforcement surface LocalStack Pro ships for its paid IAM feature.

SQS

SNS

  • All 34 SNS actions map to sns:<Action>.
  • Topic-targeted actions read TopicArn; subscription-targeted actions read SubscriptionArn; platform-app/endpoint/tagging actions read their respective *Arn params.
  • CreateTopic builds the to-be-created topic ARN from the principal's account + Name.
  • Account-scoped actions (ListTopics, SetSMSAttributes, ...) → *.

S3

  • All 74 S3 actions classified by s3_resource_for into bucket or object ARNs (arn:aws:s3:::bucket[/key]); S3 ARNs intentionally omit account+region because bucket names are globally unique in AWS.
  • New s3_detect_action() helper re-derives the IAM action name from method + bucket + key + query params, since S3 dispatches internally on method+path and doesn't set request.action. Handles:
    • object-level: GetObject, PutObject, DeleteObject, HeadObject, CopyObject
    • bucket-level: ListBuckets, CreateBucket, DeleteBucket, HeadBucket, GetBucketLocation, ListObjectsV2, ListObjects, ListObjectVersions, DeleteObjects
    • sub-resources: ?acl / ?tagging / ?versioning / ?policy / ?cors / ?website / ?lifecycle / ?encryption / ?logging / ?notification / ?replication / ?ownershipControls / ?publicAccessBlock / ?accelerate / ?inventory / ?object-lock
    • multipart: CreateMultipartUpload, UploadPart, CompleteMultipartUpload, AbortMultipartUpload, ListParts, ListMultipartUploads

Test plan

5 new E2E tests in iam_enforcement.rs (on top of the 8 from PR #395), all signed with real aws-sdk-rust clients:

  • SQS SendMessage denied without policy → AccessDeniedException
  • SQS resource-scoped Allow on jobs queue lets through jobs messages but denies secrets messages (same action, different resource)
  • SNS Publish denied without policy
  • SNS Publish allowed on specific topic ARN via dynamically-composed inline policy
  • S3 ReadDocs policy: GetObject on private-docs/readme.md allowed, PutObject on same bucket denied (different action)

Plus:

  • cargo test -p fakecloud-e2e --test iam_enforcement13 tests all green (8 from feat(iam,sts): wire Phase 1 enforcement for IAM + STS services #395 + 5 new)
  • cargo test -p fakecloud-e2e --test iam --test s3 --test sqs --test sns --test sigv4_verification — 183 tests total, zero regressions
  • cargo clippy --workspace --all-targets -- -D warnings clean
  • cargo fmt --check clean

Summary by cubic

Adds Phase 1 IAM enforcement for S3, SQS, and SNS, with precise action-to-resource mapping and new E2E coverage. Also fixes S3 sub-resource method guards and SNS ARN/resource detection for consistency.

  • New Features

    • SQS: map all 20 actions to sqs:<Action>; queue ARN from QueueName (CreateQueue, GetQueueUrl) or last segment of QueueUrl; ListQueues*; account id from principal.account_id.
    • SNS: map all 34 actions to sns:<Action>; resources from TopicArn/SubscriptionArn/PlatformApplicationArn/EndpointArn; CreateTopic builds ARN from service state account + Name; account-scoped actions → *.
    • S3: s3_detect_action() derives the IAM action from method + bucket + key + query; 74 actions mapped to bucket/object ARNs arn:aws:s3:::bucket[/key] (ListBuckets*).
    • Tests: 5 new E2E tests cover SQS SendMessage deny and resource-scoped allow/deny, SNS Publish deny/allow, and S3 GetObject allowed vs PutObject denied.
  • Bug Fixes

    • S3: require GET for ?attributes and POST for ?restore to avoid misclassifying IAM actions.
    • SNS: CreateTopic IAM resource uses the service state account id to match the created ARN; ConfirmSubscription is keyed by TopicArn instead of SubscriptionArn.

Written for commit 28bcfc6. Summary will update on new commits.

Extends the IAM enforcement surface (started in PR #395 with IAM+STS)
to cover the three most-used queue/storage services. Along with
IAM+STS, this matches the same enforcement surface LocalStack Pro
ships for its paid IAM feature.

SQS:
- All 20 supported SQS actions map to sqs:<Action>.
- Queue ARN is built from QueueName (CreateQueue, GetQueueUrl) or
  from the trailing segment of QueueUrl (all other queue-targeted
  actions). Account-level actions (ListQueues) target '*'.
- Account id is sourced from principal.account_id (#381 note).

SNS:
- All 34 supported SNS actions map to sns:<Action>.
- Topic-targeted actions read TopicArn; subscription-targeted
  actions read SubscriptionArn; platform app / endpoint / tagging
  actions read their respective *Arn params. CreateTopic builds the
  to-be-created topic ARN from the principal's account + Name.
- Account-scoped actions (ListTopics, SetSMSAttributes, ...)
  resolve to '*'.

S3:
- All 74 supported S3 actions are classified by s3_resource_for
  into bucket or object ARNs (arn:aws:s3:::bucket[/key]); ARNs
  intentionally omit account+region because bucket names are
  globally unique in AWS.
- New s3_detect_action() helper derives the IAM action name from
  method + bucket + key + query params, since S3 dispatches
  internally on method+path and doesn't set request.action. Handles
  the common object/bucket operations plus ?acl / ?tagging /
  ?versioning / ?policy / ?cors / ?website / ?lifecycle /
  ?encryption / ?logging / ?notification / ?replication /
  ?ownershipControls / ?publicAccessBlock / ?accelerate /
  ?inventory / ?object-lock / ?uploads / ?uploadId sub-resources.

Tests: 5 new e2e tests in iam_enforcement.rs:

- SQS SendMessage denied without policy
- SQS resource-scoped Allow: jobs queue allowed, secrets queue
  denied under the same action/principal
- SNS Publish denied without policy
- SNS Publish allowed on a specific topic ARN
- S3 ReadDocs policy: GetObject allowed on private-docs/readme.md,
  PutObject on the same bucket denied (different action)

All 13 iam_enforcement tests pass end-to-end via real aws-sdk-rust
signed requests.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 issues found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/fakecloud-s3/src/service/mod.rs">

<violation number="1" location="crates/fakecloud-s3/src/service/mod.rs:800">
P2: Missing method guard on `?attributes` sub-resource — any HTTP method with `?attributes` maps to `GetObjectAttributes`. Should match GET only, consistent with all other sub-resource handlers.</violation>

<violation number="2" location="crates/fakecloud-s3/src/service/mod.rs:803">
P2: Missing method guard on `?restore` sub-resource — any HTTP method (including GET) with `?restore` in the query string maps to `RestoreObject` instead of falling through to the correct plain-method dispatch. Since RestoreObject is a POST-only operation, this should only match POST. A GET with `?restore` present would be IAM-checked against `s3:RestoreObject` instead of `s3:GetObject`.</violation>
</file>

<file name="crates/fakecloud-sns/src/service.rs">

<violation number="1" location="crates/fakecloud-sns/src/service.rs:155">
P2: `CreateTopic` IAM resource account derivation is inconsistent with actual topic ARN creation (`state.account_id`), which can cause policy evaluation against the wrong ARN.</violation>

<violation number="2" location="crates/fakecloud-sns/src/service.rs:180">
P2: `ConfirmSubscription` is mapped to `SubscriptionArn`, but the request uses `TopicArn`; this makes IAM evaluate the wrong resource (`*` fallback in most calls).</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread crates/fakecloud-s3/src/service/mod.rs Outdated
Comment thread crates/fakecloud-s3/src/service/mod.rs Outdated
Comment thread crates/fakecloud-sns/src/service.rs Outdated
Comment thread crates/fakecloud-sns/src/service.rs
Identified by cubic on PR #399:

S3:
- ?attributes sub-resource now requires GET — previously any method
  with ?attributes mapped to GetObjectAttributes, which was inconsistent
  with the other sub-resource handlers that all method-gate.
- ?restore sub-resource now requires POST — previously a GET with
  ?restore would be classified as RestoreObject (POST-only in AWS)
  and IAM-evaluated against s3:RestoreObject instead of s3:GetObject.

SNS:
- CreateTopic IAM resource now sources its account id from
  state.account_id via self.state.read(), matching how the handler
  actually builds the topic ARN (via Arn::new(..., &state.account_id,
  ...)). Previously it used principal.account_id, which is the same
  value today but would diverge under multi-account isolation (#381)
  and cause policy evaluation to target a different ARN than the one
  the handler creates. Keeping them consistent means #381 will update
  both sites in the same change.
- ConfirmSubscription is now keyed by TopicArn, not SubscriptionArn —
  the ConfirmSubscription API takes a Token against a topic, and the
  subscription ARN only exists after confirmation. Previously the
  resource fell back to '*' in most calls.
@vieiralucas vieiralucas merged commit 58aa495 into main Apr 15, 2026
37 checks passed
@vieiralucas vieiralucas deleted the worktree-sigv4-iam-batch8-sqs-sns-s3 branch April 15, 2026 00:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant