Skip to content

Commit

Permalink
Merge pull request #464 from versity/ben/presign_escape
Browse files Browse the repository at this point in the history
fix: escape path and query for presign signature validation
  • Loading branch information
benmcclelland authored Mar 20, 2024
2 parents b592cfb + b9ed7cb commit 83f6ca7
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 4 deletions.
8 changes: 6 additions & 2 deletions s3api/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@ import (
"fmt"
"io"
"net/http"
"net/url"
"regexp"
"strconv"
"strings"
"time"

"github.com/aws/smithy-go/encoding/httpbinding"
"github.com/gofiber/fiber/v2"
"github.com/valyala/fasthttp"
"github.com/versity/versitygw/s3err"
Expand Down Expand Up @@ -114,16 +116,18 @@ func createPresignedHttpRequestFromCtx(ctx *fiber.Ctx, signedHdrs []string, cont
}

uri := string(ctx.Request().URI().Path())
uri = httpbinding.EscapePath(uri, false)
isFirst := true

ctx.Request().URI().QueryArgs().VisitAll(func(key, value []byte) {
_, ok := signedQueryArgs[string(key)]
if !ok {
escapeValue := url.QueryEscape(string(value))
if isFirst {
uri += fmt.Sprintf("?%s=%s", key, value)
uri += fmt.Sprintf("?%s=%s", key, escapeValue)
isFirst = false
} else {
uri += fmt.Sprintf("&%s=%s", key, value)
uri += fmt.Sprintf("&%s=%s", key, escapeValue)
}
}
})
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/group-tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func TestPresignedAuthentication(s *S3Conf) {
PresignedAuth_incorrect_secret_key(s)
PresignedAuth_PutObject_success(s)
PresignedAuth_Put_GetObject_with_data(s)
PresignedAuth_Put_GetObject_with_UTF8_chars(s)
PresignedAuth_UploadPart(s)
}

Expand Down Expand Up @@ -329,6 +330,7 @@ func GetIntTests() IntTests {
"PresignedAuth_incorrect_secret_key": PresignedAuth_incorrect_secret_key,
"PresignedAuth_PutObject_success": PresignedAuth_PutObject_success,
"PresignedAuth_Put_GetObject_with_data": PresignedAuth_Put_GetObject_with_data,
"PresignedAuth_Put_GetObject_with_UTF8_chars": PresignedAuth_Put_GetObject_with_UTF8_chars,
"PresignedAuth_UploadPart": PresignedAuth_UploadPart,
"CreateBucket_invalid_bucket_name": CreateBucket_invalid_bucket_name,
"CreateBucket_existing_bucket": CreateBucket_existing_bucket,
Expand Down
68 changes: 66 additions & 2 deletions tests/integration/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -1448,8 +1448,6 @@ func PresignedAuth_Put_GetObject_with_data(s *S3Conf) error {
return fmt.Errorf("read get object response body %w", err)
}

fmt.Println(resp.Request.Method, resp.ContentLength, string(respBody))

if string(respBody) != data {
return fmt.Errorf("expected get object response body to be %v, instead got %s", data, respBody)
}
Expand All @@ -1463,6 +1461,72 @@ func PresignedAuth_Put_GetObject_with_data(s *S3Conf) error {
})
}

func PresignedAuth_Put_GetObject_with_UTF8_chars(s *S3Conf) error {
testName := "PresignedAuth_Put_GetObject_with_data"
return presignedAuthHandler(s, testName, func(client *s3.PresignClient) error {
bucket, obj := getBucketName(), "my-$%^&*;"
err := setup(s, bucket)
if err != nil {
return err
}

ctx, cancel := context.WithTimeout(context.Background(), shortTimeout)
v4req, err := client.PresignPutObject(ctx, &s3.PutObjectInput{Bucket: &bucket, Key: &obj})
cancel()
if err != nil {
return err
}

httpClient := http.Client{
Timeout: shortTimeout,
}

req, err := http.NewRequest(v4req.Method, v4req.URL, nil)
if err != nil {
return err
}

req.Header = v4req.SignedHeader

resp, err := httpClient.Do(req)
if err != nil {
return err
}

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("expected my-obj to be successfully uploaded and get %v response status, instead got %v", http.StatusOK, resp.StatusCode)
}

ctx, cancel = context.WithTimeout(context.Background(), shortTimeout)
v4GetReq, err := client.PresignGetObject(ctx, &s3.GetObjectInput{Bucket: &bucket, Key: &obj})
cancel()
if err != nil {
return err
}

req, err = http.NewRequest(v4GetReq.Method, v4GetReq.URL, nil)
if err != nil {
return err
}

resp, err = httpClient.Do(req)
if err != nil {
return err
}

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("expected get object response status to be %v, instead got %v", http.StatusOK, resp.StatusCode)
}

err = teardown(s, bucket)
if err != nil {
return err
}

return nil
})
}

func PresignedAuth_UploadPart(s *S3Conf) error {
testName := "PresignedAuth_UploadPart"
return presignedAuthHandler(s, testName, func(client *s3.PresignClient) error {
Expand Down

0 comments on commit 83f6ca7

Please sign in to comment.