s3api: answer unrecognized bucket PUT sub-resources with 501

A PUT on a bucket carrying an unrecognized query (logging, metrics,
intelligent-tiering, ...) fell through to the bare CreateBucket route
and returned BucketAlreadyOwnedByYou or re-created the bucket. AWS
answers these with NotImplemented.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Chris Lu
2026-10-06 09:04:10 -07:00
co-authored by Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent 051011758f
commit 564fdf26e1
2 changed files with 21 additions and 3 deletions
+9
View File
@@ -224,6 +224,15 @@ func (s3a *S3ApiServer) PutBucketHandler(w http.ResponseWriter, r *http.Request)
// collect parameters
bucket, _ := s3_constants.GetBucketAndObject(r)
// Only bucket-level PUTs with a registered sub-resource route land in a
// dedicated handler; anything else that reaches here is an operation we do
// not implement, not a bare CreateBucket.
if subresource, found := unroutedSubresource(r, nil); found {
glog.V(1).Infof("unimplemented bucket PUT subresource ?%s", subresource)
s3err.WriteErrorResponse(w, r, s3err.ErrNotImplemented)
return
}
// validate the bucket name
err := s3bucket.VerifyS3BucketName(bucket)
if err != nil {
+12 -3
View File
@@ -185,11 +185,20 @@ var listObjectsQueryParams = map[string]bool{
// catch-all, so anything left is a subresource it does not implement - and answering
// it with a bucket listing is worse than saying so.
func unroutedBucketSubresource(r *http.Request) (string, bool) {
return unroutedSubresource(r, listObjectsQueryParams)
}
// unroutedSubresource names a query key that no route claimed, outside the
// request's own parameters, SigV2 presign keys, and SigV4 presign keys
// (all "X-Amz-" prefixed).
func unroutedSubresource(r *http.Request, allowed map[string]bool) (string, bool) {
for key := range r.URL.Query() {
if listObjectsQueryParams[key] || strings.HasPrefix(key, "X-Amz-") {
continue
switch {
case allowed[key], strings.HasPrefix(key, "X-Amz-"):
case key == "x-id", key == "AWSAccessKeyId", key == "Signature", key == "Expires":
default:
return key, true
}
return key, true
}
return "", false
}