s3: embedded IAM inline policy honors prefix-scoped resources (#10192)

* s3: embedded IAM inline policy honors prefix-scoped resources

getActions stripped the trailing wildcard from a resource like
arn:aws:s3:::bucket/prefix/*, producing a non-wildcard action
(Write:bucket/prefix) that CanDo only ever matched at bucket level, so
PutObject under the prefix was denied. Preserve the object path with its
wildcard (Write:bucket/prefix/*) to match objects under the prefix,
matching the standalone iamapi behavior.

* s3: prune bucket-confined wildcard actions on bucket delete

actionScopedToBucket treated any wildcard as multi-bucket, so a
prefix-scoped action like Write:bucket/prefix/* survived deletion of its
own bucket and could re-grant access if the bucket was recreated. Scope
the wildcard check to the bucket segment only: a wildcard in the object
path stays scoped to its bucket, while one in the bucket segment does
not.
This commit is contained in:
Chris Lu authored and GitHub committed 2026-07-02 09:14:54 -07:00
1 parent cc4043c9d2
commit c46526822b
4 files changed
+75 -13

No files matched your search

+5 -9
View File
@@ -989,15 +989,11 @@ func (e *EmbeddedIamApi) getActions(policy *policy_engine.PolicyDocument) ([]str
// Bucket-level or bucket/* - use just bucket name
actions = append(actions, fmt.Sprintf("%s:%s", statementAction, bucket))
} else {
// Path-specific: bucket/path/* -> Action:bucket/path
// Remove trailing /* if present for cleaner action format
objectPath = strings.TrimSuffix(objectPath, "/*")
objectPath = strings.TrimSuffix(objectPath, "*")
if objectPath == "" {
actions = append(actions, fmt.Sprintf("%s:%s", statementAction, bucket))
} else {
actions = append(actions, fmt.Sprintf("%s:%s/%s", statementAction, bucket, objectPath))
}
// Path-specific: preserve the object path, including any
// trailing wildcard, so CanDo can match objects under the
// prefix. Stripping the wildcard yielded a non-wildcard
// action that only matched at bucket level.
actions = append(actions, fmt.Sprintf("%s:%s/%s", statementAction, bucket, objectPath))
}
}
}