s3api: reject malformed Range offsets (#10034)

This commit is contained in:
patrick
2026-06-22 22:52:01 +08:00
committed by GitHub
parent 888ecc5325
commit d7860ddf24
2 changed files with 54 additions and 4 deletions
+12 -4
View File
@@ -190,14 +190,22 @@ func (s3a *S3ApiServer) parseAndValidateRange(w http.ResponseWriter, r *http.Req
endOffset = totalSize - 1
if parts[0] != "" {
if parsed, err := strconv.ParseInt(parts[0], 10, 64); err == nil {
startOffset = parsed
parsed, parseErr := strconv.ParseInt(parts[0], 10, 64)
if parseErr != nil {
w.Header().Set("Content-Range", fmt.Sprintf("bytes */%d", totalSize))
s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRange)
return 0, 0, false, newStreamErrorWithResponse(fmt.Errorf("invalid range start: %w", parseErr))
}
startOffset = parsed
}
if parts[1] != "" {
if parsed, err := strconv.ParseInt(parts[1], 10, 64); err == nil {
endOffset = parsed
parsed, parseErr := strconv.ParseInt(parts[1], 10, 64)
if parseErr != nil {
w.Header().Set("Content-Range", fmt.Sprintf("bytes */%d", totalSize))
s3err.WriteErrorResponse(w, r, s3err.ErrInvalidRange)
return 0, 0, false, newStreamErrorWithResponse(fmt.Errorf("invalid range end: %w", parseErr))
}
endOffset = parsed
}
// Special case: range requests on empty files should return 416
+42
View File
@@ -0,0 +1,42 @@
package s3api
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
)
func TestParseAndValidateRangeRejectsMalformedNumericOffsets(t *testing.T) {
s3a := &S3ApiServer{}
entry := &filer_pb.Entry{}
tests := []struct {
name string
rangeHeader string
}{
{"invalid start", "bytes=abc-5"},
{"invalid end", "bytes=5-abc"},
{"invalid open ended start", "bytes=abc-"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/bucket/object", nil)
req.Header.Set("Range", tt.rangeHeader)
rec := httptest.NewRecorder()
_, _, _, streamErr := s3a.parseAndValidateRange(rec, req, entry, 100, "bucket", "object")
if streamErr == nil {
t.Fatalf("parseAndValidateRange(%q) error = nil, want invalid range error", tt.rangeHeader)
}
if rec.Code != http.StatusRequestedRangeNotSatisfiable {
t.Fatalf("parseAndValidateRange(%q) status = %d, want %d", tt.rangeHeader, rec.Code, http.StatusRequestedRangeNotSatisfiable)
}
if got := rec.Header().Get("Content-Range"); got != "bytes */100" {
t.Fatalf("parseAndValidateRange(%q) Content-Range = %q, want %q", tt.rangeHeader, got, "bytes */100")
}
})
}
}