Merge pull request #2147 from versity/sis/post-object-key-validation

fix: reject invalid PostObject keys
This commit is contained in:
Ben McClelland
2026-05-22 11:53:24 -07:00
committed by GitHub
5 changed files with 51 additions and 31 deletions
+1 -9
View File
@@ -112,15 +112,7 @@ func (c S3ApiController) POSTObject(ctx *fiber.Ctx) (*Response, error) {
cacheControl := parsed.Fields["cache-control"]
expires := parsed.Fields["expires"]
key, ok := parsed.Fields["key"]
if !ok || key == "" {
debuglogger.Logf("missing object key")
return &Response{
MetaOpts: &MetaOptions{
BucketOwner: parsedAcl.Owner,
},
}, s3err.PostAuth.MissingField("key")
}
key := parsed.Fields["key"]
err := auth.VerifyAccess(ctx.Context(), c.be,
auth.AccessOptions{
-22
View File
@@ -254,28 +254,6 @@ func TestS3ApiController_POSTObject(t *testing.T) {
input testInput
output testOutput
}{
{
name: "missing key",
input: testInput{
locals: postObjectLocalsForTest(middlewares.PostObjectResult{
Fields: map[string]string{
"policy": basePolicy,
"file": "ignored",
"x-amz-signature": "ignored",
},
FileRdr: newMockFileReader("payload"),
ContentLength: int64(len("payload")),
}),
},
output: testOutput{
response: &Response{
MetaOpts: &MetaOptions{
BucketOwner: "root",
},
},
err: s3err.PostAuth.MissingField("key"),
},
},
{
name: "verify access fails",
input: testInput{
+10
View File
@@ -86,6 +86,16 @@ func AuthorizePostObject(root RootUserConfig, iam auth.IAMService, region string
fields := result.Fields
if fields["key"] == "" {
debuglogger.Logf("missing object key")
return s3err.PostAuth.MissingField("key")
}
if !utils.IsObjectNameValid(fields["key"]) {
debuglogger.Logf("invalid POST object key: %q", fields["key"])
return s3err.GetAPIError(s3err.ErrBadRequest)
}
policyB64 := fields[formFieldPolicy]
algorithm := fields[formFieldAlgorithm]
credentialStr := fields[formFieldCredential]
+38
View File
@@ -309,6 +309,44 @@ func PostObject_access_denied(s *S3Conf) error {
})
}
func PostObject_invalid_object_names(s *S3Conf) error {
testName := "PostObject_invalid_object_names"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
for _, obj := range []string{
".",
"..",
"./",
"/.",
"//",
"../",
"/..",
"../.",
"../../../.",
"../../../etc/passwd",
"../../../../tmp/foo",
"for/../../bar/",
"a/a/a/../../../../../etc/passwd",
"/a/../../b/../../c/../../../etc/passwd",
} {
resp, err := sendPostObject(PostRequestConfig{
bucket: bucket,
key: obj,
s3Conf: s,
fileContent: []byte("data"),
})
if err != nil {
return err
}
if err := checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrBadRequest)); err != nil {
return err
}
}
return nil
})
}
func PostObject_policy_access_control(s *S3Conf) error {
testName := "PostObject_policy_access_control"
return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error {
+2
View File
@@ -1196,6 +1196,7 @@ func TestPostObject(ts *TestState) {
ts.Run(PostObject_signature_mismatch)
ts.Run(PostObject_expired_due_to_date)
ts.Run(PostObject_access_denied)
ts.Run(PostObject_invalid_object_names)
ts.Run(PostObject_policy_access_control)
ts.Run(PostObject_policy_expired)
ts.Run(PostObject_invalid_policy_document)
@@ -2038,6 +2039,7 @@ func GetIntTests() IntTests {
"PostObject_signature_mismatch": PostObject_signature_mismatch,
"PostObject_expired_due_to_date": PostObject_expired_due_to_date,
"PostObject_access_denied": PostObject_access_denied,
"PostObject_invalid_object_names": PostObject_invalid_object_names,
"PostObject_policy_access_control": PostObject_policy_access_control,
"PostObject_policy_expired": PostObject_policy_expired,
"PostObject_invalid_policy_document": PostObject_invalid_policy_document,