fix: reject unknown response-* query parameters in GetObject and HeadObject
getobject and headobject silently ignored any response-* query parameter outside the six overridable headers. they now return 400 InvalidArgument naming the parameter and its value, like s3, and the rest tests skipped for this issue are enabled. Fixes #2075
This commit is contained in:
1 parent
5a14104c85
commit
86ff18f8c1
13 files changed
+208
-6
No files matched your search
@@ -1305,6 +1305,10 @@ func GetObject_overrides_fail_public(s *S3Conf) error {
|
||||
}, withAnonymousClient())
|
||||
}
|
||||
|
||||
func GetObject_overrides_fail_invalid_header(s *S3Conf) error {
|
||||
return testInvalidResponseOverride(s, "GetObject_overrides_fail_invalid_header", http.MethodGet)
|
||||
}
|
||||
|
||||
func GetObject_invalid_part_number(s *S3Conf) error {
|
||||
testName := "GetObject_invalid_part_number"
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
|
||||
@@ -1511,6 +1511,10 @@ func HeadObject_overrides_fail_public(s *S3Conf) error {
|
||||
}, withAnonymousClient())
|
||||
}
|
||||
|
||||
func HeadObject_overrides_fail_invalid_header(s *S3Conf) error {
|
||||
return testInvalidResponseOverride(s, "HeadObject_overrides_fail_invalid_header", http.MethodHead)
|
||||
}
|
||||
|
||||
func HeadObject_empty_version_id(s *S3Conf) error {
|
||||
return testEmptyVersionId(s, "HeadObject_empty_version_id", http.MethodHead, "", nil)
|
||||
}
|
||||
@@ -246,6 +246,7 @@ func TestHeadObject(ts *TestState) {
|
||||
ts.Run(HeadObject_overrides_success)
|
||||
ts.Run(HeadObject_overrides_presign_success)
|
||||
ts.Run(HeadObject_overrides_fail_public)
|
||||
ts.Run(HeadObject_overrides_fail_invalid_header)
|
||||
ts.Run(HeadObject_range_and_part_number)
|
||||
ts.Run(HeadObject_mp_part_number_exceeds_parts_count)
|
||||
ts.Run(HeadObject_mp_part_number_success)
|
||||
@@ -294,6 +295,7 @@ func TestGetObject(ts *TestState) {
|
||||
ts.Run(GetObject_overrides_success)
|
||||
ts.Run(GetObject_overrides_presign_success)
|
||||
ts.Run(GetObject_overrides_fail_public)
|
||||
ts.Run(GetObject_overrides_fail_invalid_header)
|
||||
ts.Run(GetObject_invalid_part_number)
|
||||
ts.Run(GetObject_range_and_part_number)
|
||||
ts.Run(GetObject_mp_part_number_exceeds_parts_count)
|
||||
@@ -3064,6 +3066,7 @@ func GetIntTests() IntTests {
|
||||
"HeadObject_overrides_success": HeadObject_overrides_success,
|
||||
"HeadObject_overrides_presign_success": HeadObject_overrides_presign_success,
|
||||
"HeadObject_overrides_fail_public": HeadObject_overrides_fail_public,
|
||||
"HeadObject_overrides_fail_invalid_header": HeadObject_overrides_fail_invalid_header,
|
||||
"HeadObject_range_and_part_number": HeadObject_range_and_part_number,
|
||||
"HeadObject_mp_part_number_exceeds_parts_count": HeadObject_mp_part_number_exceeds_parts_count,
|
||||
"HeadObject_mp_part_number_success": HeadObject_mp_part_number_success,
|
||||
@@ -3100,6 +3103,7 @@ func GetIntTests() IntTests {
|
||||
"GetObject_overrides_success": GetObject_overrides_success,
|
||||
"GetObject_overrides_presign_success": GetObject_overrides_presign_success,
|
||||
"GetObject_overrides_fail_public": GetObject_overrides_fail_public,
|
||||
"GetObject_overrides_fail_invalid_header": GetObject_overrides_fail_invalid_header,
|
||||
"GetObject_invalid_part_number": GetObject_invalid_part_number,
|
||||
"GetObject_range_and_part_number": GetObject_range_and_part_number,
|
||||
"GetObject_mp_part_number_exceeds_parts_count": GetObject_mp_part_number_exceeds_parts_count,
|
||||
|
||||
@@ -612,6 +612,63 @@ func testEmptyVersionId(s *S3Conf, testName, method, subresource string, body []
|
||||
})
|
||||
}
|
||||
|
||||
// testInvalidResponseOverride verifies GetObject or HeadObject rejects a
|
||||
// response-* query parameter that isn't an overridable response header. The
|
||||
// SDK only sends the known overrides, so the request is signed and sent by
|
||||
// hand.
|
||||
func testInvalidResponseOverride(s *S3Conf, testName, method string) error {
|
||||
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
|
||||
obj := "my-obj"
|
||||
_, err := putObjects(s3client, []string{obj}, bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
query string
|
||||
expected s3err.InvalidArgumentError
|
||||
}{
|
||||
{
|
||||
query: "response-fdsjkljj=dummy",
|
||||
expected: s3err.GetInvalidArgResponseOverride("response-fdsjkljj", "dummy"),
|
||||
},
|
||||
// a valid override doesn't let an unknown one through
|
||||
{
|
||||
query: "response-content-type=text%2Fplain&response-invalid=dummy",
|
||||
expected: s3err.GetInvalidArgResponseOverride("response-invalid", "dummy"),
|
||||
},
|
||||
} {
|
||||
req, err := createSignedReq(method, s.endpoint,
|
||||
fmt.Sprintf("%v/%v?%v", bucket, obj, test.query), s.awsID, s.awsSecret,
|
||||
"s3", s.awsRegion, "", nil, time.Now(), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// HEAD responses carry no body: only the status code is verifiable
|
||||
if method == http.MethodHead {
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode != test.expected.StatusCode() {
|
||||
return fmt.Errorf("%v: expected response status code to be %v, instead got %v",
|
||||
test.query, test.expected.StatusCode(), resp.StatusCode)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if err := checkHTTPResponseApiErr(resp, test.expected); err != nil {
|
||||
return fmt.Errorf("%v: %w", test.query, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func checkIAMAuthRequest(s *S3Conf, req *http.Request, expected iamerr.APIError) error {
|
||||
resp, err := s.httpClient.Do(req)
|
||||
if err != nil {
|
||||
|
||||
Reference in new issue
Block a user