s3: close list-type / ownership-controls routing mismatch (#11280)

* s3: reject list-type paired with another operation subresource

?list-type=2&ownershipControls= routes to ListObjectsV2 (the list-type
route is registered first) while the IAM action resolver resolves the
ownershipControls selector to s3:GetBucketOwnershipControls. A principal
denied s3:ListBucket but allowed s3:GetBucketOwnershipControls would
therefore list the bucket. list-type selects an operation just like the
other keys in operationSubresources, so add it there and reject the
combination before routing, matching the fix for policy&tagging (#10987).

* s3: resolve list-type to s3:ListBucket ahead of bucket subresources

The router registers the ListObjectsV2 route ahead of the bucket
subresource routes, so the action resolver should resolve list-type the
same way. Without this, a request carrying list-type and another operation
selector resolves to the subresource action (e.g. s3:GetBucketOwnershipControls)
while being served by ListObjectsV2. The ambiguity guard rejects such
combinations before routing, but resolving list-type to s3:ListBucket keeps
the resolver aligned with the router, mirroring how versions is handled.

* s3: match list-type=2 exactly in action resolver

The router selects ListObjectsV2 only for list-type=2; other values fall
through to the subresource routes. Resolve the same way so the action
matches the handler for every list-type value, not just 2.
This commit is contained in:
Chris Lu
2026-09-11 22:21:58 -07:00
committed by GitHub
parent 9f6feef299
commit 210afacd12
4 changed files with 64 additions and 3 deletions
+3 -3
View File
@@ -49,9 +49,9 @@ func hasPathSegmentQuery(rawQuery string) bool {
var operationSubresources = map[string]bool{
"accelerate": true, "acl": true, "analytics": true, "attributes": true,
"cors": true, "delete": true, "encryption": true, "intelligent-tiering": true,
"inventory": true, "legal-hold": true, "lifecycle": true, "location": true,
"logging": true, "metrics": true, "notification": true, "object-lock": true,
"ownershipControls": true, "policy": true, "policyStatus": true,
"inventory": true, "legal-hold": true, "lifecycle": true, "list-type": true,
"location": true, "logging": true, "metrics": true, "notification": true,
"object-lock": true, "ownershipControls": true, "policy": true, "policyStatus": true,
"publicAccessBlock": true, "renameObject": true, "replication": true,
"requestPayment": true, "retention": true, "tagging": true, "uploadId": true,
"uploads": true, "versioning": true, "versions": true, "website": true,