mirror of
https://github.com/ferdzo/fs.git
synced 2026-06-04 04:26:46 +00:00
Reject unsupported aws-chunked uploads
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
67
api/api.go
67
api/api.go
@@ -41,6 +41,7 @@ const (
|
||||
maxXMLBodyBytes int64 = 1 << 20
|
||||
maxDeleteObjects = 1000
|
||||
maxObjectKeyBytes = 1024
|
||||
maxAWSChunkedLineBytes = 8 << 10
|
||||
serverReadHeaderTimeout = 5 * time.Second
|
||||
serverReadTimeout = 60 * time.Second
|
||||
serverWriteTimeout = 120 * time.Second
|
||||
@@ -387,6 +388,10 @@ func (h *Handler) handlePutObject(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
bodyReader := io.Reader(r.Body)
|
||||
var decodeStream io.ReadCloser
|
||||
if hasUnsupportedAWSChunkedPayload(r) {
|
||||
writeS3Error(w, r, s3ErrInvalidArgument, r.URL.Path)
|
||||
return
|
||||
}
|
||||
if shouldDecodeAWSChunkedPayload(r) {
|
||||
decodeStream = newAWSChunkedDecodingReader(r.Body)
|
||||
defer decodeStream.Close()
|
||||
@@ -461,6 +466,10 @@ func (h *Handler) handlePutObject(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
bodyReader := io.Reader(r.Body)
|
||||
var decodeStream io.ReadCloser
|
||||
if hasUnsupportedAWSChunkedPayload(r) {
|
||||
writeS3Error(w, r, s3ErrInvalidArgument, r.URL.Path)
|
||||
return
|
||||
}
|
||||
if shouldDecodeAWSChunkedPayload(r) {
|
||||
decodeStream = newAWSChunkedDecodingReader(r.Body)
|
||||
defer decodeStream.Close()
|
||||
@@ -516,17 +525,18 @@ func (h *Handler) handleListMultipartParts(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
|
||||
func shouldDecodeAWSChunkedPayload(r *http.Request) bool {
|
||||
contentEncoding := strings.ToLower(r.Header.Get("Content-Encoding"))
|
||||
if strings.Contains(contentEncoding, "aws-chunked") {
|
||||
return true
|
||||
}
|
||||
signingMode := strings.ToLower(r.Header.Get("x-amz-content-sha256"))
|
||||
if strings.HasPrefix(signingMode, "streaming-aws4-hmac-sha256-payload") {
|
||||
return true
|
||||
}
|
||||
return strings.HasPrefix(signingMode, "streaming-unsigned-payload")
|
||||
}
|
||||
|
||||
func hasUnsupportedAWSChunkedPayload(r *http.Request) bool {
|
||||
contentEncoding := strings.ToLower(r.Header.Get("Content-Encoding"))
|
||||
if !strings.Contains(contentEncoding, "aws-chunked") {
|
||||
return false
|
||||
}
|
||||
return !shouldDecodeAWSChunkedPayload(r)
|
||||
}
|
||||
|
||||
func newAWSChunkedDecodingReader(src io.Reader) io.ReadCloser {
|
||||
probedReader, isAWSChunked := probeAWSChunkedPayload(src)
|
||||
if !isAWSChunked {
|
||||
@@ -545,9 +555,12 @@ func newAWSChunkedDecodingReader(src io.Reader) io.ReadCloser {
|
||||
}
|
||||
|
||||
func probeAWSChunkedPayload(src io.Reader) (io.Reader, bool) {
|
||||
reader := bufio.NewReaderSize(src, 512)
|
||||
reader := bufio.NewReaderSize(src, maxAWSChunkedLineBytes)
|
||||
headerLine, err := reader.ReadSlice('\n')
|
||||
replay := io.MultiReader(bytes.NewReader(headerLine), reader)
|
||||
if errors.Is(err, bufio.ErrBufferFull) {
|
||||
return replay, true
|
||||
}
|
||||
if err != nil {
|
||||
return replay, false
|
||||
}
|
||||
@@ -569,9 +582,9 @@ func probeAWSChunkedPayload(src io.Reader) (io.Reader, bool) {
|
||||
}
|
||||
|
||||
func decodeAWSChunkedPayload(src io.Reader, dst io.Writer) error {
|
||||
reader := bufio.NewReader(src)
|
||||
reader := bufio.NewReaderSize(src, maxAWSChunkedLineBytes)
|
||||
for {
|
||||
headerLine, err := reader.ReadString('\n')
|
||||
headerLine, err := readAWSChunkedLine(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -588,6 +601,17 @@ func decodeAWSChunkedPayload(src io.Reader, dst io.Writer) error {
|
||||
if chunkSize < 0 {
|
||||
return fmt.Errorf("invalid aws-chunked size: %d", chunkSize)
|
||||
}
|
||||
if chunkSize == 0 {
|
||||
for {
|
||||
line, err := readAWSChunkedLine(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if line == "\r\n" || line == "\n" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if chunkSize > 0 {
|
||||
if _, err := io.CopyN(dst, reader, chunkSize); err != nil {
|
||||
return err
|
||||
@@ -601,21 +625,20 @@ func decodeAWSChunkedPayload(src io.Reader, dst io.Writer) error {
|
||||
if crlf[0] != '\r' || crlf[1] != '\n' {
|
||||
return errors.New("invalid aws-chunked payload terminator")
|
||||
}
|
||||
|
||||
if chunkSize == 0 {
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if line == "\r\n" || line == "\n" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func readAWSChunkedLine(reader *bufio.Reader) (string, error) {
|
||||
line, err := reader.ReadSlice('\n')
|
||||
if errors.Is(err, bufio.ErrBufferFull) {
|
||||
return "", service.ErrEntityTooLarge
|
||||
}
|
||||
if len(line) > maxAWSChunkedLineBytes {
|
||||
return "", service.ErrEntityTooLarge
|
||||
}
|
||||
return string(line), err
|
||||
}
|
||||
|
||||
func ifNoneMatchPreconditionFailed(headerValue, etag string) bool {
|
||||
for _, rawToken := range strings.Split(headerValue, ",") {
|
||||
token := strings.TrimSpace(rawToken)
|
||||
|
||||
Reference in New Issue
Block a user