fix: return InvalidArgument for an empty website configuration

* fix: return InvalidArgument for an empty website configuration

`PutBucketWebsite` parsed an empty `<WebsiteConfiguration/>` into a zero-value config, and `WebsiteConfiguration.Validate` reported the nil `IndexDocument` as `MalformedXML`. S3 reports the missing index document as an `InvalidArgument` request error naming the argument instead:

    InvalidArgument: A value for IndexDocument Suffix must be provided if RedirectAllRequestsTo is empty

That branch now returns the error with `ArgumentName=IndexDocument` and `ArgumentValue=null`, matching the response in the report. Genuinely malformed XML, and a `RedirectAllRequestsTo` that conflicts with the other fields, still return `MalformedXML`.

Fixes #2260

* test: cover an empty website configuration in the PutBucketWebsite integration suite

Add a `PutBucketWebsite_empty_configuration` case that sends the empty `<WebsiteConfiguration/>` payload from the report and asserts the `InvalidArgument` response, including `ArgumentName=IndexDocument` and `ArgumentValue=null`.

The SDK collapses the error into a generic API error that drops the argument fields, so the request is signed by hand and the raw response checked with `checkHTTPResponseApiErr`, the same helper the other argument-field assertions use. The case fails against the previous behaviour with `expected error code to be InvalidArgument, instead got MalformedXML`.

---------

Co-authored-by: Tung Lam <lamphamabtung96@gmail.com>
This commit is contained in:
Tung Lam
2026-09-20 11:04:22 -07:00
committed by GitHub
co-authored by Tung Lam
parent f1fa2a2e48
commit 915fc7a20f
6 changed files with 148 additions and 2 deletions
+108
View File
@@ -1334,3 +1334,111 @@ func TestS3ApiController_PutBucketAcl(t *testing.T) {
})
}
}
func TestS3ApiController_PutBucketWebsite(t *testing.T) {
validBody, err := xml.Marshal(s3response.WebsiteConfiguration{
IndexDocument: &s3response.IndexDocument{Suffix: "index.html"},
})
assert.NoError(t, err)
// The payload from https://github.com/versity/versitygw/issues/2260
emptyBody := []byte(`<?xml version="1.0" encoding="UTF-8"?>
<WebsiteConfiguration xmlns="https://s3.amazonaws.com/doc/2006-03-01/"></WebsiteConfiguration>`)
tests := []struct {
name string
input testInput
output testOutput
}{
{
name: "verify access fails",
input: testInput{
locals: accessDeniedLocals,
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{
BucketOwner: "root",
},
},
err: s3err.GetAPIError(s3err.ErrAccessDenied),
},
},
{
name: "empty website configuration",
input: testInput{
locals: defaultLocals,
body: emptyBody,
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{BucketOwner: "root"},
},
err: s3err.GetInvalidArgumentErr(s3err.InvalidArgMissingIndexDocumentSuffix, "null"),
},
},
{
name: "malformed xml",
input: testInput{
locals: defaultLocals,
body: []byte("<WebsiteConfiguration><IndexDocument>"),
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{BucketOwner: "root"},
},
err: s3err.GetAPIError(s3err.ErrMalformedXML),
},
},
{
name: "backend error",
input: testInput{
locals: defaultLocals,
beErr: s3err.GetAPIError(s3err.ErrNoSuchBucket),
body: validBody,
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{BucketOwner: "root"},
},
err: s3err.GetAPIError(s3err.ErrNoSuchBucket),
},
},
{
name: "success",
input: testInput{
locals: defaultLocals,
body: validBody,
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{
BucketOwner: "root",
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
be := &BackendMock{
PutBucketWebsiteFunc: func(contextMoqParam context.Context, bucket string, website []byte) error {
return tt.input.beErr
},
GetBucketPolicyFunc: func(contextMoqParam context.Context, bucket string) ([]byte, error) {
return nil, s3err.GetAPIError(s3err.ErrAccessDenied)
},
}
ctrl := S3ApiController{
be: be,
}
testController(t, ctrl.PutBucketWebsite, tt.output.response, tt.output.err, ctxInputs{
locals: tt.input.locals,
body: tt.input.body,
})
})
}
}