From 814fc9dfc037ff86fedf6c3e9aed3ca08fc21053 Mon Sep 17 00:00:00 2001
From: andres-portainer <91705312+andres-portainer@users.noreply.github.com>
Date: Mon, 27 Mar 2023 15:14:16 -0300
Subject: [PATCH] fix(http): drain and close HTTP response bodies EE-5280
 (#8716)

---
 api/agent/version.go                          |  5 +++-
 api/hostmanagement/fdo/owner_client.go        | 24 ++++++++++++++-----
 api/hostmanagement/openamt/authorization.go   |  3 +++
 api/hostmanagement/openamt/configCIRA.go      |  3 +++
 api/hostmanagement/openamt/openamt.go         |  6 +++++
 api/http/handler/backup/backup_test.go        |  2 ++
 api/http/handler/backup/restore_test.go       |  2 ++
 .../endpoints/endpoint_dockerhub_status.go    |  6 +++--
 api/oauth/oauth.go                            |  3 ++-
 pkg/libhelm/binary/search_repo.go             |  1 +
 10 files changed, 45 insertions(+), 10 deletions(-)

diff --git a/api/agent/version.go b/api/agent/version.go
index bdb7ebce1..03d3cf4f6 100644
--- a/api/agent/version.go
+++ b/api/agent/version.go
@@ -4,6 +4,7 @@ import (
 	"crypto/tls"
 	"errors"
 	"fmt"
+	"io"
 	"net/http"
 	"strconv"
 	"time"
@@ -42,7 +43,9 @@ func GetAgentVersionAndPlatform(endpointUrl string, tlsConfig *tls.Config) (port
 	if err != nil {
 		return 0, "", err
 	}
-	defer resp.Body.Close()
+
+	io.Copy(io.Discard, resp.Body)
+	resp.Body.Close()
 
 	if resp.StatusCode != http.StatusNoContent {
 		return 0, "", fmt.Errorf("Failed request with status %d", resp.StatusCode)
diff --git a/api/hostmanagement/fdo/owner_client.go b/api/hostmanagement/fdo/owner_client.go
index bfd10b8ce..0f3aba696 100644
--- a/api/hostmanagement/fdo/owner_client.go
+++ b/api/hostmanagement/fdo/owner_client.go
@@ -113,7 +113,9 @@ func (c FDOOwnerClient) PutDeviceSVI(info ServiceInfo) error {
 	if err != nil {
 		return err
 	}
-	defer resp.Body.Close()
+
+	io.Copy(io.Discard, resp.Body)
+	resp.Body.Close()
 
 	if resp.StatusCode != http.StatusOK {
 		return errors.New(http.StatusText(resp.StatusCode))
@@ -132,7 +134,9 @@ func (c FDOOwnerClient) PutDeviceSVIRaw(info url.Values, body []byte) error {
 	if err != nil {
 		return err
 	}
-	defer resp.Body.Close()
+
+	io.Copy(io.Discard, resp.Body)
+	resp.Body.Close()
 
 	if resp.StatusCode != http.StatusOK {
 		return errors.New(http.StatusText(resp.StatusCode))
@@ -151,7 +155,9 @@ func (c FDOOwnerClient) GetVouchers() ([]string, error) {
 	if err != nil {
 		return nil, err
 	}
-	defer resp.Body.Close()
+
+	io.Copy(io.Discard, resp.Body)
+	resp.Body.Close()
 
 	if resp.StatusCode != http.StatusOK {
 		return nil, errors.New(http.StatusText(resp.StatusCode))
@@ -182,7 +188,9 @@ func (c FDOOwnerClient) DeleteVoucher(guid string) error {
 	if err != nil {
 		return err
 	}
-	defer resp.Body.Close()
+
+	io.Copy(io.Discard, resp.Body)
+	resp.Body.Close()
 
 	if resp.StatusCode != http.StatusOK {
 		return errors.New(http.StatusText(resp.StatusCode))
@@ -201,7 +209,9 @@ func (c FDOOwnerClient) GetDeviceSVI(guid string) (string, error) {
 	if err != nil {
 		return "", err
 	}
-	defer resp.Body.Close()
+
+	io.Copy(io.Discard, resp.Body)
+	resp.Body.Close()
 
 	body, err := io.ReadAll(resp.Body)
 	if err != nil {
@@ -225,7 +235,9 @@ func (c FDOOwnerClient) DeleteDeviceSVI(id string) error {
 	if err != nil {
 		return err
 	}
-	defer resp.Body.Close()
+
+	io.Copy(io.Discard, resp.Body)
+	resp.Body.Close()
 
 	if resp.StatusCode != http.StatusOK {
 		return errors.New(http.StatusText(resp.StatusCode))
diff --git a/api/hostmanagement/openamt/authorization.go b/api/hostmanagement/openamt/authorization.go
index 9d5050d2e..463cb4dcf 100644
--- a/api/hostmanagement/openamt/authorization.go
+++ b/api/hostmanagement/openamt/authorization.go
@@ -33,10 +33,13 @@ func (service *Service) Authorization(configuration portainer.OpenAMTConfigurati
 	if err != nil {
 		return "", err
 	}
+	defer response.Body.Close()
+
 	responseBody, readErr := io.ReadAll(response.Body)
 	if readErr != nil {
 		return "", readErr
 	}
+
 	errorResponse := parseError(responseBody)
 	if errorResponse != nil {
 		return "", errorResponse
diff --git a/api/hostmanagement/openamt/configCIRA.go b/api/hostmanagement/openamt/configCIRA.go
index def1f423c..feac5821a 100644
--- a/api/hostmanagement/openamt/configCIRA.go
+++ b/api/hostmanagement/openamt/configCIRA.go
@@ -128,6 +128,7 @@ func (service *Service) getCIRACertificate(configuration portainer.OpenAMTConfig
 	if err != nil {
 		return "", err
 	}
+	defer response.Body.Close()
 
 	if response.StatusCode != http.StatusOK {
 		return "", fmt.Errorf("unexpected status code %s", response.Status)
@@ -137,6 +138,8 @@ func (service *Service) getCIRACertificate(configuration portainer.OpenAMTConfig
 	if err != nil {
 		return "", err
 	}
+
 	block, _ := pem.Decode(certificate)
+
 	return base64.StdEncoding.EncodeToString(block.Bytes), nil
 }
diff --git a/api/hostmanagement/openamt/openamt.go b/api/hostmanagement/openamt/openamt.go
index 590664961..c7a738fc4 100644
--- a/api/hostmanagement/openamt/openamt.go
+++ b/api/hostmanagement/openamt/openamt.go
@@ -103,6 +103,8 @@ func (service *Service) executeSaveRequest(method string, url string, token stri
 	if err != nil {
 		return nil, err
 	}
+	defer response.Body.Close()
+
 	responseBody, readErr := io.ReadAll(response.Body)
 	if readErr != nil {
 		return nil, readErr
@@ -132,6 +134,8 @@ func (service *Service) executeGetRequest(url string, token string) ([]byte, err
 	if err != nil {
 		return nil, err
 	}
+	defer response.Body.Close()
+
 	responseBody, readErr := io.ReadAll(response.Body)
 	if readErr != nil {
 		return nil, readErr
@@ -141,10 +145,12 @@ func (service *Service) executeGetRequest(url string, token string) ([]byte, err
 		if response.StatusCode == http.StatusNotFound {
 			return nil, nil
 		}
+
 		errorResponse := parseError(responseBody)
 		if errorResponse != nil {
 			return nil, errorResponse
 		}
+
 		return nil, fmt.Errorf("unexpected status code %s", response.Status)
 	}
 
diff --git a/api/http/handler/backup/backup_test.go b/api/http/handler/backup/backup_test.go
index a35754db3..23954b9c9 100644
--- a/api/http/handler/backup/backup_test.go
+++ b/api/http/handler/backup/backup_test.go
@@ -53,6 +53,7 @@ func Test_backupHandlerWithoutPassword_shouldCreateATarballArchive(t *testing.T)
 
 	response := w.Result()
 	body, _ := io.ReadAll(response.Body)
+	response.Body.Close()
 
 	tmpdir := t.TempDir()
 
@@ -89,6 +90,7 @@ func Test_backupHandlerWithPassword_shouldCreateEncryptedATarballArchive(t *test
 
 	response := w.Result()
 	body, _ := io.ReadAll(response.Body)
+	response.Body.Close()
 
 	tmpdir := t.TempDir()
 
diff --git a/api/http/handler/backup/restore_test.go b/api/http/handler/backup/restore_test.go
index 5c21a67d7..087179fa3 100644
--- a/api/http/handler/backup/restore_test.go
+++ b/api/http/handler/backup/restore_test.go
@@ -99,6 +99,8 @@ func backup(t *testing.T, h *Handler, password string) []byte {
 
 	response := w.Result()
 	archive, _ := io.ReadAll(response.Body)
+	response.Body.Close()
+
 	return archive
 }
 
diff --git a/api/http/handler/endpoints/endpoint_dockerhub_status.go b/api/http/handler/endpoints/endpoint_dockerhub_status.go
index 1c4e05227..a102c695b 100644
--- a/api/http/handler/endpoints/endpoint_dockerhub_status.go
+++ b/api/http/handler/endpoints/endpoint_dockerhub_status.go
@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"io"
 	"net/http"
 	"strconv"
 	"strings"
@@ -128,7 +129,6 @@ func getDockerHubToken(httpClient *client.HTTPClient, registry *portainer.Regist
 }
 
 func getDockerHubLimits(httpClient *client.HTTPClient, token string) (*dockerhubStatusResponse, error) {
-
 	requestURL := "https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest"
 
 	req, err := http.NewRequest(http.MethodHead, requestURL, nil)
@@ -142,7 +142,9 @@ func getDockerHubLimits(httpClient *client.HTTPClient, token string) (*dockerhub
 	if err != nil {
 		return nil, err
 	}
-	defer resp.Body.Close()
+
+	io.Copy(io.Discard, resp.Body)
+	resp.Body.Close()
 
 	if resp.StatusCode != http.StatusOK {
 		return nil, errors.New("failed fetching dockerhub limits")
diff --git a/api/oauth/oauth.go b/api/oauth/oauth.go
index 9843258b2..3f15f8f92 100644
--- a/api/oauth/oauth.go
+++ b/api/oauth/oauth.go
@@ -121,12 +121,13 @@ func getResource(token string, configuration *portainer.OAuthSettings) (map[stri
 
 	client := &http.Client{}
 	req.Header.Set("Authorization", "Bearer "+token)
+
 	resp, err := client.Do(req)
 	if err != nil {
 		return nil, err
 	}
-
 	defer resp.Body.Close()
+
 	body, err := io.ReadAll(resp.Body)
 	if err != nil {
 		return nil, err
diff --git a/pkg/libhelm/binary/search_repo.go b/pkg/libhelm/binary/search_repo.go
index fbdbfdf08..a959f512e 100644
--- a/pkg/libhelm/binary/search_repo.go
+++ b/pkg/libhelm/binary/search_repo.go
@@ -68,6 +68,7 @@ func (hbpm *helmBinaryPackageManager) SearchRepo(searchRepoOpts options.SearchRe
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to get index file")
 	}
+	defer resp.Body.Close()
 
 	var file File
 	err = yaml.NewDecoder(resp.Body).Decode(&file)