mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-10-26 15:00:48 +01:00
* s3api: stop echoing stored tags and internal attributes on GET/HEAD setResponseHeaders copied every entry.Extended key into the response, only filtering xattr- and x-seaweedfs-. Stored object tags (X-Amz-Tagging-*) and the gateway's own attributes (Seaweed-X-Amz-Acl, -Owner, -Version-Id, -Object-Lock-Mode, ...) therefore leaked to any caller holding s3:GetObject, and to anonymous readers on public-read objects, without s3:GetObjectTagging or READ_ACP. Tag count, user metadata, storage class and the real S3 headers (x-amz-version-id, x-amz-object-lock-*) are unaffected. * s3api: match stored tag and internal attribute keys case-insensitively
67 lines
2.7 KiB
Go
67 lines
2.7 KiB
Go
package s3api
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
|
|
"github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
|
|
)
|
|
|
|
// GetObject/HeadObject must expose only response headers AWS defines: user
|
|
// metadata, standard entity headers and x-amz-tagging-count. Tag values live
|
|
// behind GetObjectTagging and ACL/owner/lock state behind its own APIs; the
|
|
// generic Extended echo must not hand them to any caller that can read the
|
|
// object.
|
|
func TestSetResponseHeadersFiltersInternalExtendedKeys(t *testing.T) {
|
|
s3a := &S3ApiServer{}
|
|
entry := &filer_pb.Entry{
|
|
Name: "object",
|
|
Attributes: &filer_pb.FuseAttributes{Mtime: 100},
|
|
Extended: map[string][]byte{
|
|
s3_constants.AmzObjectTaggingPrefix + "project": []byte("alpha"),
|
|
s3_constants.AmzObjectTaggingPrefix + "owner": []byte("y"),
|
|
"x-amz-tagging-sneaky": []byte("z"),
|
|
"seaweed-x-amz-acl": []byte("z"),
|
|
s3_constants.ExtAmzAclKey: []byte(`[{"Permission":"FULL_CONTROL"}]`),
|
|
s3_constants.ExtAmzOwnerKey: []byte("admin"),
|
|
s3_constants.ExtVersionIdKey: []byte("version-1"),
|
|
s3_constants.ExtObjectLockModeKey: []byte("COMPLIANCE"),
|
|
s3_constants.ExtETagKey: []byte("d41d8cd98f00b204e9800998ecf8427e"),
|
|
s3_constants.SeaweedFSWriteId: []byte("write-id"),
|
|
"X-Amz-Meta-Foo": []byte("bar"),
|
|
"Cache-Control": []byte("no-cache"),
|
|
"xattr-user.note": []byte("n"),
|
|
},
|
|
}
|
|
|
|
for _, method := range []string{http.MethodGet, http.MethodHead} {
|
|
w := httptest.NewRecorder()
|
|
s3a.setResponseHeaders(w, httptest.NewRequest(method, "/bucket/object", nil), entry, 8)
|
|
|
|
for key := range w.Header() {
|
|
lowerKey := strings.ToLower(key)
|
|
if lowerKey == s3_constants.AmzTagCount {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(lowerKey, strings.ToLower(s3_constants.ExtAmzPrefix)) {
|
|
t.Errorf("%s: internal header %q leaked into response", method, key)
|
|
}
|
|
if strings.HasPrefix(lowerKey, strings.ToLower(s3_constants.AmzObjectTaggingPrefix)) {
|
|
t.Errorf("%s: object tag value %q leaked into response", method, key)
|
|
}
|
|
}
|
|
if got := w.Header().Get(s3_constants.AmzTagCount); got != "2" {
|
|
t.Errorf("%s: tag count = %q, want 2", method, got)
|
|
}
|
|
if got := w.Header()["X-Amz-Meta-foo"]; len(got) != 1 || got[0] != "bar" {
|
|
t.Errorf("%s: user metadata dropped, X-Amz-Meta-foo = %v", method, got)
|
|
}
|
|
if got := w.Header().Get("Cache-Control"); got != "no-cache" {
|
|
t.Errorf("%s: Cache-Control = %q", method, got)
|
|
}
|
|
}
|
|
}
|