diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index e9dab2125..5273c5800 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -1085,7 +1085,7 @@ func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Reque // Avoid reusing tcp connection if read timeout is hit // This is needed to make r.Context().Done() work as // expected in case of read timeout - w.Header().Add("Connection", "close") + w.Header().Set("Connection", "close") setEventStreamHeaders(w) diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index bc0a89fde..7d4e3d0c4 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -804,7 +804,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h if _, err = globalBucketSSEConfigSys.Get(bucket); err == nil || globalAutoEncryption { // This request header needs to be set prior to setting ObjectOptions if !crypto.SSEC.IsRequested(r.Header) { - r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256) + r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256) } } diff --git a/cmd/generic-handlers_test.go b/cmd/generic-handlers_test.go index 1a6f4f11f..168fc543f 100644 --- a/cmd/generic-handlers_test.go +++ b/cmd/generic-handlers_test.go @@ -155,12 +155,12 @@ var isHTTPHeaderSizeTooLargeTests = []struct { func generateHeader(size, usersize int) http.Header { header := http.Header{} for i := 0; i < size; i++ { - header.Add(strconv.Itoa(i), "") + header.Set(strconv.Itoa(i), "") } userlength := 0 for i := 0; userlength < usersize; i++ { userlength += len(userMetadataKeyPrefixes[0] + strconv.Itoa(i)) - header.Add(userMetadataKeyPrefixes[0]+strconv.Itoa(i), "") + header.Set(userMetadataKeyPrefixes[0]+strconv.Itoa(i), "") } return header } diff --git a/cmd/http-tracer.go b/cmd/http-tracer.go index ee225674c..eb44185ff 100644 --- a/cmd/http-tracer.go +++ b/cmd/http-tracer.go @@ -104,9 +104,8 @@ func Trace(f http.HandlerFunc, logBody bool, w http.ResponseWriter, r *http.Requ reqHeaders.Set("Host", r.Host) if len(r.TransferEncoding) == 0 { reqHeaders.Set("Content-Length", strconv.Itoa(int(r.ContentLength))) - } - for _, enc := range r.TransferEncoding { - reqHeaders.Add("Transfer-Encoding", enc) + } else { + reqHeaders.Set("Transfer-Encoding", strings.Join(r.TransferEncoding, ",")) } var reqBodyRecorder *recordRequest diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index f83ba8b56..d96076661 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -881,7 +881,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re _, err = globalBucketSSEConfigSys.Get(dstBucket) // This request header needs to be set prior to setting ObjectOptions if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) { - r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256) + r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256) } var srcOpts, dstOpts ObjectOptions @@ -1449,7 +1449,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req _, err = globalBucketSSEConfigSys.Get(bucket) // This request header needs to be set prior to setting ObjectOptions if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) && !crypto.S3KMS.IsRequested(r.Header) { - r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256) + r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256) } actualSize := size @@ -1648,7 +1648,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r _, err = globalBucketSSEConfigSys.Get(bucket) // This request header needs to be set prior to setting ObjectOptions if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) && !crypto.S3KMS.IsRequested(r.Header) { - r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256) + r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256) } // Validate storage class metadata if present diff --git a/cmd/object-handlers_test.go b/cmd/object-handlers_test.go index 0f6b598dd..25c7d168c 100644 --- a/cmd/object-handlers_test.go +++ b/cmd/object-handlers_test.go @@ -529,7 +529,7 @@ func testAPIGetObjectHandler(obj ObjectLayer, instanceType, bucketName string, a t.Fatalf("Test %d: Failed to create HTTP request for Get Object: %v", i+1, err) } if testCase.byteRange != "" { - req.Header.Add("Range", testCase.byteRange) + req.Header.Set("Range", testCase.byteRange) } // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. // Call the ServeHTTP to execute the handler,`func (api objectAPIHandlers) GetObjectHandler` handles the request. @@ -577,7 +577,7 @@ func testAPIGetObjectHandler(obj ObjectLayer, instanceType, bucketName string, a } if testCase.byteRange != "" { - reqV2.Header.Add("Range", testCase.byteRange) + reqV2.Header.Set("Range", testCase.byteRange) } // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. @@ -741,7 +741,7 @@ func testAPIGetObjectWithMPHandler(obj ObjectLayer, instanceType, bucketName str } if byteRange != "" { - req.Header.Add("Range", byteRange) + req.Header.Set("Range", byteRange) } apiRouter.ServeHTTP(rec, req) diff --git a/cmd/server_test.go b/cmd/server_test.go index 8162a8e7a..a2acb9f25 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -191,8 +191,8 @@ func (s *TestSuiteCommon) TestBucketSQSNotificationWebHook(c *check) { func (s *TestSuiteCommon) TestCors(c *check) { expectedMap := http.Header{} - expectedMap.Add("Access-Control-Allow-Credentials", "true") - expectedMap.Add("Access-Control-Allow-Origin", "http://foobar.com") + expectedMap.Set("Access-Control-Allow-Credentials", "true") + expectedMap.Set("Access-Control-Allow-Origin", "http://foobar.com") expectedMap["Access-Control-Expose-Headers"] = []string{ "Date", "Etag", @@ -214,10 +214,10 @@ func (s *TestSuiteCommon) TestCors(c *check) { "X-Amz*", "*", } - expectedMap.Add("Vary", "Origin") + expectedMap.Set("Vary", "Origin") req, _ := http.NewRequest(http.MethodOptions, s.endPoint, nil) - req.Header.Add("Origin", "http://foobar.com") + req.Header.Set("Origin", "http://foobar.com") res, err := s.client.Do(req) if err != nil { c.Fatal(err) @@ -1561,7 +1561,7 @@ func (s *TestSuiteCommon) TestPartialContent(c *check) { request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, "bar"), 0, nil, s.accessKey, s.secretKey, s.signer) c.Assert(err, nil) - request.Header.Add("Range", "bytes=6-7") + request.Header.Set("Range", "bytes=6-7") response, err = s.client.Do(request) c.Assert(err, nil) @@ -1906,7 +1906,7 @@ func (s *TestSuiteCommon) TestGetPartialObjectMisAligned(c *check) { 0, nil, s.accessKey, s.secretKey, s.signer) c.Assert(err, nil) // Get partial content based on the byte range set. - request.Header.Add("Range", "bytes="+t.byteRange) + request.Header.Set("Range", "bytes="+t.byteRange) // execute the HTTP request. response, err = s.client.Do(request) @@ -1972,7 +1972,7 @@ func (s *TestSuiteCommon) TestGetPartialObjectLarge11MiB(c *check) { 0, nil, s.accessKey, s.secretKey, s.signer) c.Assert(err, nil) // This range spans into first two blocks. - request.Header.Add("Range", "bytes=10485750-10485769") + request.Header.Set("Range", "bytes=10485750-10485769") // execute the HTTP request. response, err = s.client.Do(request) @@ -2039,7 +2039,7 @@ func (s *TestSuiteCommon) TestGetPartialObjectLarge10MiB(c *check) { 0, nil, s.accessKey, s.secretKey, s.signer) c.Assert(err, nil) // Get partial content based on the byte range set. - request.Header.Add("Range", "bytes=2048-2058") + request.Header.Set("Range", "bytes=2048-2058") // execute the HTTP request to download the partial content. response, err = s.client.Do(request) @@ -2126,7 +2126,7 @@ func (s *TestSuiteCommon) TestGetObjectRangeErrors(c *check) { request, err = newTestSignedRequest(http.MethodGet, getGetObjectURL(s.endPoint, bucketName, objectName), 0, nil, s.accessKey, s.secretKey, s.signer) // Invalid byte range set. - request.Header.Add("Range", "bytes=-0") + request.Header.Set("Range", "bytes=-0") c.Assert(err, nil) // execute the HTTP request. diff --git a/cmd/signature-v4-utils.go b/cmd/signature-v4-utils.go index 6f09d6881..6563cdf51 100644 --- a/cmd/signature-v4-utils.go +++ b/cmd/signature-v4-utils.go @@ -163,9 +163,7 @@ func extractSignedHeaders(signedHeaders []string, r *http.Request) (http.Header, val, ok = reqQueries[header] } if ok { - for _, enc := range val { - extractedSignedHeaders.Add(header, enc) - } + extractedSignedHeaders[http.CanonicalHeaderKey(header)] = val continue } switch header { @@ -192,9 +190,7 @@ func extractSignedHeaders(signedHeaders []string, r *http.Request) (http.Header, extractedSignedHeaders.Set(header, r.Host) case "transfer-encoding": // Go http server removes "host" from Request.Header - for _, enc := range r.TransferEncoding { - extractedSignedHeaders.Add(header, enc) - } + extractedSignedHeaders[http.CanonicalHeaderKey(header)] = r.TransferEncoding case "content-length": // Signature-V4 spec excludes Content-Length from signed headers list for signature calculation. // But some clients deviate from this rule. Hence we consider Content-Length for signature diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index 05ef65818..b16d05ec5 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -1127,7 +1127,7 @@ func newTestSignedRequestV2(method, urlStr string, contentLength int64, body io. } for k, v := range headers { - req.Header.Add(k, v) + req.Header.Set(k, v) } err = signRequestV2(req, accessKey, secretKey) @@ -1151,7 +1151,7 @@ func newTestSignedRequestV4(method, urlStr string, contentLength int64, body io. } for k, v := range headers { - req.Header.Add(k, v) + req.Header.Set(k, v) } err = signRequestV4(req, accessKey, secretKey) diff --git a/cmd/web-handlers.go b/cmd/web-handlers.go index 36e622617..44354fc86 100644 --- a/cmd/web-handlers.go +++ b/cmd/web-handlers.go @@ -1040,7 +1040,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) { // Check if bucket encryption is enabled _, err = globalBucketSSEConfigSys.Get(bucket) if (globalAutoEncryption || err == nil) && !crypto.SSEC.IsRequested(r.Header) { - r.Header.Add(crypto.SSEHeader, crypto.SSEAlgorithmAES256) + r.Header.Set(crypto.SSEHeader, crypto.SSEAlgorithmAES256) } // Require Content-Length to be set in the request diff --git a/mint/run/core/aws-sdk-go/main.go b/mint/run/core/aws-sdk-go/main.go index e9f4a887d..3e0a9b89e 100644 --- a/mint/run/core/aws-sdk-go/main.go +++ b/mint/run/core/aws-sdk-go/main.go @@ -234,14 +234,14 @@ func testPresignedPutInvalidHash(s3Client *s3.S3) { return } - rreq, err := http.NewRequest("PUT", url, bytes.NewReader([]byte(""))) + rreq, err := http.NewRequest(http.MethodPut, url, bytes.NewReader([]byte(""))) if err != nil { failureLog(function, args, startTime, "", "AWS SDK Go presigned PUT request failed", err).Fatal() return } - rreq.Header.Add("X-Amz-Content-Sha256", "invalid-sha256") - rreq.Header.Add("Content-Type", "application/octet-stream") + rreq.Header.Set("X-Amz-Content-Sha256", "invalid-sha256") + rreq.Header.Set("Content-Type", "application/octet-stream") resp, err := http.DefaultClient.Do(rreq) if err != nil { diff --git a/ruleguard.rules.go b/ruleguard.rules.go index 190eebd90..3f94fa44d 100644 --- a/ruleguard.rules.go +++ b/ruleguard.rules.go @@ -418,3 +418,32 @@ func mailaddress(m fluent.Matcher) { Suggest("(&mail.Address{Name:$NAME, Address:$EMAIL}).String()") } + +func errnetclosed(m fluent.Matcher) { + m.Match( + `strings.Contains($err.Error(), $text)`, + ). + Where(m["text"].Text.Matches("\".*closed network connection.*\"")). + Report(`String matching against error texts is fragile; use net.ErrClosed instead`). + Suggest(`errors.Is($err, net.ErrClosed)`) + +} + +func httpheaderadd(m fluent.Matcher) { + m.Match( + `$H.Add($KEY, $VALUE)`, + ). + Where(m["H"].Type.Is("http.Header")). + Report("use http.Header.Set method instead of Add to overwrite all existing header values"). + Suggest(`$H.Set($KEY, $VALUE)`) +} + + +func hmacnew(m fluent.Matcher) { + m.Match("hmac.New(func() hash.Hash { return $x }, $_)", + `$f := func() hash.Hash { return $x } + $*_ + hmac.New($f, $_)`, + ).Where(m["x"].Pure). + Report("invalid hash passed to hmac.New()") +}