Skip to content

feat(policy): add the arn:minio:memory ARN for Memory resources - #249

Merged
harshavardhana merged 2 commits into
minio:mainfrom
harshavardhana:feat/memory-arn
Aug 1, 2026
Merged

feat(policy): add the arn:minio:memory ARN for Memory resources#249
harshavardhana merged 2 commits into
minio:mainfrom
harshavardhana:feat/memory-arn

Conversation

@harshavardhana

@harshavardhana harshavardhana commented Aug 1, 2026

Copy link
Copy Markdown
Member

What

Memory resources get their own ARN namespace:

arn:minio:memory:::<cortex>[/<collection>[/<name>]]

They were authorized against the S3 ARN of the bucket backing the cortex, which
forced the storage path into every policy and made a Memory grant
indistinguishable from raw S3 access to the same bytes. The pattern is now a
logical path, so the Memory API's on-disk layout can change without
invalidating a policy.

ValidateMemory requires the Memory type, so an S3 ARN cannot carry a Memory
action and a Memory ARN cannot carry an S3 one.

Bare ARN prefix fix (affects every ARN type)

arn:aws:s3::: parsed as ResourceARNAll keeping its own prefix as the
pattern. It validated cleanly and then matched nothing — an Allow that
granted zero and, the direction that fails open, a Deny that never fired:

ParseResource("arn:aws:s3:::") -> Type=* Pattern="arn:aws:s3:::"
    matches "mybucket/key"? false
policy validates: <nil>
GetObject allowed despite the Deny? true

AWS rejects the form outright. It now parses as its own type with an empty
pattern. S3, S3 Tables and KMS keep loading one so a policy already on disk
still works, with ValidateStrict refusing it on create/update — the split
ParseConfigStrict already exists to provide. Memory carries no such
tolerance and fails at parse.

Tests

Memory resources had no coverage; the suite passed because nothing exercised
them. Adds parsing, validation, matching, cross-type isolation, whole-document
validation and end-to-end evaluation — including that an agent-scoped grant
denies a neighbour whose id shares a prefix, and that a bare-prefix policy
loads but cannot be written.

Both defences mutation-checked: dropping traversal/charset rejection fails 17
assertions; letting Memory inherit the bare-ARN tolerance fails 3.

Summary by CodeRabbit

  • New Features

    • Added support for AIStor Memory resources in policy ARNs.
    • Added Memory resource validation and wildcard matching.
    • Added strict policy validation that rejects incomplete resource prefixes and accepts explicit wildcard forms.
    • Added safeguards for resource type and scope isolation.
  • Bug Fixes

    • Prevented malformed or traversal-based Memory resources from being accepted.
    • Ensured resource representations do not expose underlying storage details.
    • Preserved compatibility when loading existing bare ARN policies while preventing unsafe updates.

Memory resources were authorized against the S3 ARN of the bucket
backing the cortex, which forced the storage path into every policy and
made a Memory grant indistinguishable from raw S3 access to the same
bytes. They get their own namespace instead:

  arn:minio:memory:::<cortex>[/<collection>[/<name>]]

The pattern is a logical path, never a storage key, so the Memory API's
on-disk layout can change without invalidating a policy. ValidateMemory
now requires the Memory type, so an S3 ARN can no longer carry a Memory
action and a Memory ARN can no longer carry an S3 one.

Malformed Memory ARNs are rejected rather than accepted as patterns
that silently match nothing: a wrong colon count, a foreign partition
or service, case variation, embedded whitespace, a backslash, any "."
or ".." segment, and a prefix naming no resource at all.

Also fixes a bug affecting every ARN type. A bare prefix such as
"arn:aws:s3:::" parsed as ResourceARNAll keeping its own prefix as the
pattern, so it validated cleanly and then matched nothing -- an Allow
that granted zero and, the direction that fails open, a Deny that never
fired. AWS rejects the form outright. It now parses as its own type
with an empty pattern. S3, S3 Tables and KMS keep loading one so a
policy already on disk still works, with ValidateStrict refusing it on
the create and update paths; Memory carries no such tolerance and fails
at parse.

Memory resources had no test coverage at all; the suite passed because
nothing exercised them. Adds parsing, validation, matching, cross-type
isolation, whole-document validation and end-to-end evaluation,
including that an agent-scoped grant denies a neighbour whose id shares
a prefix.
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@harshavardhana, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 05dd776e-7d80-45b0-ab85-5d7f004b8c79

📥 Commits

Reviewing files that changed from the base of the PR and between fecf9ee and 98d63e9.

📒 Files selected for processing (3)
  • policy/memory-resource_test.go
  • policy/resource.go
  • policy/resourceset.go
📝 Walkthrough

Walkthrough

Adds AIStor Memory ARN support with dedicated parsing, path validation, wildcard matching, strict policy validation, authorization isolation, JSON round trips, and storage-independent serialization tests.

Changes

Memory ARN policy support

Layer / File(s) Summary
Memory resource model and parsing
policy/resource.go, policy/memory-resource_test.go
Adds the Memory ARN type, constructor, logical path validation, bare ARN tracking, parsing rules, and round-trip tests.
Strict policy validation
policy/resourceset.go, policy/statement.go, policy/memory-resource_test.go
Adds strict validation for bare prefixes and requires Memory resources in Memory resource sets. Policy tests cover invalid resources, actions, and resource combinations.
Matching and authorization behavior
policy/memory-resource_test.go
Tests exact and wildcard matching, normalized paths, resource-type isolation, cross-agent and cross-cortex isolation, authorization, and storage-independent serialization.

Estimated code review effort: 4 (Complex) | ~45 minutes

Poem

I’m a rabbit with an ARN,
Memory paths now parse and span.
Wildcards hop, strict rules stay,
Bare prefixes lose their way.
No storage secrets in the string—
Policy carrots! Let them spring.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the addition of the arn:minio:memory ARN namespace for Memory resources, which is the primary change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
policy/resource.go (1)

336-366: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

A bare prefix now carries a concrete type, which can break loading of existing non-S3 statements.

Before this change a bare prefix parsed as ResourceARNAll. ResourceARNAll satisfies isS3(), isKMS(), isTable() and now isMemory(), so a bare prefix loaded inside any statement type. After this change "arn:aws:s3:::" parses as ResourceARNS3. A stored KMS or Table statement that carries a bare S3 prefix now fails ValidateKMS/ValidateTable with "type is not KMS" on the load path, not only on the strict path.

TestBareARNPolicyLoadsButCannotBeWritten only covers a bare S3 prefix inside an S3 statement, so this case is untested. If the mixed-type combination is judged impossible in practice, add a test that pins that decision. Otherwise keep bare resources loadable across type validators.

🛡️ One option: let the type validators tolerate an inert bare resource
 func (resourceSet ResourceSet) ValidateKMS() error {
 	for resource := range resourceSet {
-		if !resource.isKMS() {
+		if !resource.IsBareARN() && !resource.isKMS() {
 			return Errorf("resource '%v' type is not KMS", resource)
 		}
#!/bin/bash
# Check whether any stored policy fixture mixes a bare ARN prefix into a non-S3 statement,
# and how each type validator reacts to a typed bare resource.
rg -n -C 4 'arn:aws:s3:::"|arn:minio:kms:::"|arn:aws:s3tables:::"' --type=go
rg -n -C 8 'func \(resourceSet ResourceSet\) Validate(KMS|Table|S3|Memory)' --type=go
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@policy/resource.go` around lines 336 - 366, Preserve load compatibility for
bare ARN resources across statement validators: update the relevant ValidateKMS,
ValidateTable, ValidateS3, and ValidateMemory logic to tolerate an inert
resource with an empty Pattern regardless of its parsed concrete Type, while
retaining strict rejection for non-bare mismatched resource types and write-time
validation. Add coverage for a bare S3 prefix embedded in non-S3 statements, or
explicitly test and document the decision if mixed-type statements are
intentionally unsupported.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@policy/memory-resource_test.go`:
- Line 160: Replace the British spellings in all three sites: change “behaviour”
to “behavior” in the inert-resource comment at
policy/memory-resource_test.go:160-160 and the t.Error message at
policy/memory-resource_test.go:249-249, and change “neighbour” to “neighbor” in
the doc comment at policy/memory-resource_test.go:561-561.
- Around line 388-414: Harden the exported Memory matcher, MatchResource, by
normalizing the resource path before wildcard matching so traversal segments
cannot match outside the intended subtree; document the behavior in its GoDoc if
callers must still provide cleaned paths. Update
TestMemoryMatchRequiresCleanResource to remove the skip and assert the
traversal-containing resource does not match, while preserving the existing
invalid-pattern assertion.
- Around line 544-556: Update the policy test loop around Policy validation to
execute each table entry through t.Run using tc.name, keeping unmarshalling,
validation, and assertions inside the subtest. Extend the testCases table with a
valid Memory NotResource case alongside the existing rejecting case, preserving
the current expected-error assertions.

---

Outside diff comments:
In `@policy/resource.go`:
- Around line 336-366: Preserve load compatibility for bare ARN resources across
statement validators: update the relevant ValidateKMS, ValidateTable,
ValidateS3, and ValidateMemory logic to tolerate an inert resource with an empty
Pattern regardless of its parsed concrete Type, while retaining strict rejection
for non-bare mismatched resource types and write-time validation. Add coverage
for a bare S3 prefix embedded in non-S3 statements, or explicitly test and
document the decision if mixed-type statements are intentionally unsupported.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 935b1c71-1c76-499b-8bd1-f31065351020

📥 Commits

Reviewing files that changed from the base of the PR and between d4f5d70 and fecf9ee.

📒 Files selected for processing (4)
  • policy/memory-resource_test.go
  • policy/resource.go
  • policy/resourceset.go
  • policy/statement.go

Comment thread policy/memory-resource_test.go Outdated
Comment thread policy/memory-resource_test.go Outdated
Comment thread policy/memory-resource_test.go
Keep a bare ARN prefix loadable inside a statement of another type. It
used to parse as ResourceARNAll, which satisfies every type predicate,
so a KMS or Table statement carrying one loaded; giving it a concrete
type made those fail on the load path. The type validators now tolerate
an inert bare resource. ValidateMemory does not: a Memory statement
carrying one cannot already exist.

Clean the candidate before matching a Memory resource, so a raw request
path cannot escape the subtree its pattern names. Other ARN types match
verbatim, as before.

Fix three US-locale misspellings, and run the policy tables as
subtests.
@harshavardhana

Copy link
Copy Markdown
Member Author

Pushed 98d63e9. All four findings actioned.

Bare prefix breaks cross-type loading (outside-diff, Major) — correct, and it defeated the point of the tolerance. Reproduced before fixing:

KMS statement with bare S3 prefix, Validate() = resource 'arn:aws:s3:::' type is not KMS

ValidateS3/KMS/Table/Vectors now tolerate an inert bare resource. ValidateMemory deliberately does not — a Memory statement carrying one cannot already exist on disk, so there is nothing to keep loading. TestBareARNLoadsInsideForeignStatement covers all four combinations.

Traversal not enforced (Major) — agreed, and it is enforceable for Memory alone without touching S3 semantics. Match now cleans the candidate when the type is Memory. The test is a positive assertion instead of a self-skip, and also checks that cleaning does not break resources legitimately inside the subtree, and that a path resolving into the subtree still matches.

S3, Tables and KMS still match verbatim — that is pre-existing and changing it has a much wider blast radius, so it is documented on Match rather than altered here.

misspell — fixed all three. Also ran make lint locally this time; 0 issues.

Subtests — both policy tables now use t.Run, plus a valid Memory NotResource case.

@harshavardhana
harshavardhana merged commit 1ee10aa into minio:main Aug 1, 2026
11 checks passed
@harshavardhana
harshavardhana deleted the feat/memory-arn branch August 1, 2026 07:32
@harshavardhana

Copy link
Copy Markdown
Member Author

Correction. This PR's description and commit message state that AWS rejects arn:aws:s3::: outright. That is unsupported and I should not have asserted it. #250 corrects the code comment carrying the same claim.

Checked offline against three validators:

  • moto — reimplements AWS's MalformedPolicyDocument responses. It accepts arn:aws:s3:::. It rejects arn:aws:s3 ("can not contain region information") and notanarn ("must be in ARN format or *"), so it does have a resource-ARN rejection path; the empty form passes through it.
  • parliament — its INVALID_ARN check tests part count, prefix, partition, region and account shape. It never checks the resource segment is non-empty.
  • policy_sentry — parses it cleanly as {service:s3, region:'', account:'', resource:''}.

AWS's policy grammar defines resource_string as only "In most cases, consists of an ARN", so spec-malformedness cannot be argued from it either.

What does hold, and what the corrected comment says: AWS documents no S3 resource type with an empty bucket name, and its wildcard-completion rule is scoped to ARNs with fewer than six fieldsarn:aws:s3::: has six, so it is not equivalent to arn:aws:s3:::*.

The fix is unaffected. It rests on MinIO's own behavior, reproduced in the description above: a bare prefix parsed as ResourceARNAll keeping its own prefix as the pattern, matched nothing, and a Deny written that way never fired. That repro stands on its own and never depended on AWS rejecting anything.

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