Files
seaweedfs/weed/s3api/s3api_bucket_subresource_routing_test.go
T
Chris Lu 1354b58675 s3: stop unrouted bucket subresources from being answered with a listing (#10814)
* s3: answer GetBucketReplication, GetBucketWebsite and GetBucketNotificationConfiguration

None of the three had a route, so they reached the unconstrained ListObjectsV1
catch-all and a client asking for a bucket's replication config got 200 and a
<ListBucketResult> back. Replication and website report their configuration as
absent the way AWS does; notification returns the empty configuration AWS
returns for a bucket with no events wired up.

* s3: stop an unrouted bucket subresource from being answered with a listing

ListObjectsV1 is the catch-all GET on a bucket, so every subresource without a
route of its own - ?torrent today, whatever AWS adds next - came back 200 with a
<ListBucketResult>. A client that asked for a configuration and got a listing
either fails its XML decode in a way that reads like corruption, or worse,
tolerantly parses it. Refuse the request instead.

The allow-list is the ListObjects parameters rather than the subresources,
so a new one fails closed. Presigned URLs sign their credentials into the
query string, so X-Amz-* and the SigV2 trio have to stay listable.
2026-08-18 16:03:08 -07:00

35 lines
1.2 KiB
Go

package s3api
import (
"net/http"
"testing"
"github.com/stretchr/testify/assert"
)
// TestUnroutedBucketSubresource guards the rule that keeps a bucket GET carrying an
// unimplemented subresource from being answered with a bucket listing: everything
// SeaweedFS implements is matched by its own route before the ListObjects catch-all,
// so anything left over that is not a listing parameter is a subresource.
func TestUnroutedBucketSubresource(t *testing.T) {
for _, query := range []string{
"",
"prefix=a&max-keys=10",
"list-type=2&continuation-token=x",
"delimiter=/&encoding-type=url",
"x-id=ListObjectsV2",
"prefix=a&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Signature=deadbeef&X-Amz-Expires=900",
"AWSAccessKeyId=key&Signature=sig&Expires=1700000000",
} {
req, _ := http.NewRequest("GET", "http://localhost/bucket?"+query, nil)
name, found := unroutedBucketSubresource(req)
assert.False(t, found, "%q is a listing request, got %q", query, name)
}
for _, query := range []string{"torrent=", "replication=", "prefix=a&torrent="} {
req, _ := http.NewRequest("GET", "http://localhost/bucket?"+query, nil)
_, found := unroutedBucketSubresource(req)
assert.True(t, found, "%q should be reported as a subresource", query)
}
}