mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-31 01:10:41 +01:00
s3: pin object-lock actions so request shape cannot re-derive them
ResolveS3Action derives the authorized action from the request's method+query, overriding the caller's base action. A governance-bypass check on DELETE ?versionId=... therefore evaluated s3:DeleteObjectVersion instead of s3:BypassGovernanceRetention, letting an identity that holds version-delete but not bypass delete a GOVERNANCE-locked version (GHSA-j4hf-8r83-7fjj). The same substitution let s3:GetObjectVersion satisfy GetObjectRetention/GetObjectLegalHold on ?versionId requests, and silently dropped bucket-policy grants of the bypass action on versioned deletes. Object Lock actions name a concrete permission checked on top of another operation's shape; resolve them to themselves, like iam:/sts: actions. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
2f641a63d6
commit
5fdb061c99
3 files changed
+108
No files matched your search
@@ -2,6 +2,7 @@ package s3api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -55,6 +56,38 @@ func TestGovernanceBypassUsesDedicatedAuthorization(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGovernanceBypassChecksBypassActionOnVersionedDelete(t *testing.T) {
|
||||
arn := "arn:aws:s3:::test-bucket/test-object"
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
policyAction string
|
||||
allowed bool
|
||||
}{
|
||||
{"bypass permission", "s3:BypassGovernanceRetention", true},
|
||||
{"delete version only", "s3:DeleteObjectVersion", false},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
iam := newTestIAM()
|
||||
iam.identities[0].Actions = nil
|
||||
iam.identities[0].Account = &Account{Id: "test-account"}
|
||||
iam.policyEngine = NewBucketPolicyEngine()
|
||||
policy := fmt.Sprintf(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":"*","Action":"%s","Resource":"%s"}]}`,
|
||||
tc.policyAction, arn)
|
||||
require.NoError(t, iam.policyEngine.engine.SetBucketPolicy("test-bucket", policy))
|
||||
s3a := &S3ApiServer{iam: iam}
|
||||
|
||||
req := httptest.NewRequest(http.MethodDelete, "http://localhost:8333/test-bucket/test-object?versionId=v1", nil)
|
||||
req = mux.SetURLVars(req, map[string]string{"bucket": "test-bucket", "object": "test-object"})
|
||||
require.NoError(t, signRawHTTPRequest(context.Background(), req,
|
||||
"AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "us-east-1"))
|
||||
|
||||
if got := s3a.checkGovernanceBypassPermission(req, "test-bucket", "/test-object"); got != tc.allowed {
|
||||
t.Errorf("checkGovernanceBypassPermission() = %v, want %v", got, tc.allowed)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGovernanceBypassUsesBodyObjectKey(t *testing.T) {
|
||||
iam := newTestIAM()
|
||||
iam.identities[0].Actions = []Action{
|
||||
|
||||
Reference in new issue
Block a user