Skip to content

Commit 6d15461

Browse files
[testing] validate artifact hashes in artifact fetcher (#8670) (#8688)
* feat: validate artifact hashes in artifact fetcher * fix: do not load the whole file in memory * fix: reuse existing VerifySHA512Hash to verify package hash (cherry picked from commit 707c63f) Co-authored-by: Panos Koutsovasilis <[email protected]>
1 parent 9f60b28 commit 6d15461

File tree

3 files changed

+50
-19
lines changed

3 files changed

+50
-19
lines changed

pkg/testing/fetcher_artifact.go

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import (
1717
"sync/atomic"
1818
"time"
1919

20+
"github.com/cenkalti/backoff/v5"
21+
22+
"github.com/elastic/elastic-agent/internal/pkg/agent/application/upgrade/artifact/download"
2023
semver "github.com/elastic/elastic-agent/pkg/version"
2124
)
2225

@@ -116,21 +119,43 @@ func (r *artifactResult) Fetch(ctx context.Context, l Logger, dir string) error
116119
return fmt.Errorf("failed to create path %q: %w", dst, err)
117120
}
118121

119-
err = DownloadPackage(ctx, l, r.doer, r.src, dst)
122+
_, err = backoff.Retry(ctx, func() (any, error) {
123+
if err = r.fetch(ctx, l, dst); err != nil {
124+
return nil, err
125+
}
126+
127+
// check package hash
128+
if err = download.VerifySHA512Hash(dst); err != nil {
129+
l.Logf("inconsistent package hash detected: %s", err)
130+
return nil, fmt.Errorf("inconsistent package hash: %w", err)
131+
}
132+
133+
return nil, nil
134+
},
135+
backoff.WithMaxTries(3),
136+
backoff.WithBackOff(backoff.NewConstantBackOff(3*time.Second)),
137+
)
138+
120139
if err != nil {
140+
return fmt.Errorf("failed to fetch %s: %w", r.src, err)
141+
}
142+
143+
return nil
144+
}
145+
146+
func (r *artifactResult) fetch(ctx context.Context, l Logger, dst string) error {
147+
if err := DownloadPackage(ctx, l, r.doer, r.src, dst); err != nil {
121148
return fmt.Errorf("failed to download %s: %w", r.src, err)
122149
}
123150

124151
// fetch package hash
125-
err = DownloadPackage(ctx, l, r.doer, r.src+extHash, dst+extHash)
126-
if err != nil {
127-
return fmt.Errorf("failed to download %s: %w", r.src, err)
152+
if err := DownloadPackage(ctx, l, r.doer, r.src+extHash, dst+extHash); err != nil {
153+
return fmt.Errorf("failed to download %s: %w", r.src+extHash, err)
128154
}
129155

130156
// fetch package asc
131-
err = DownloadPackage(ctx, l, r.doer, r.src+extAsc, dst+extAsc)
132-
if err != nil {
133-
return fmt.Errorf("failed to download %s: %w", r.src, err)
157+
if err := DownloadPackage(ctx, l, r.doer, r.src+extAsc, dst+extAsc); err != nil {
158+
return fmt.Errorf("failed to download %s: %w", r.src+extAsc, err)
134159
}
135160

136161
return nil
@@ -144,7 +169,12 @@ func findURI(ctx context.Context, doer httpDoer, version *semver.ParsedSemVer) (
144169
return fmt.Sprintf("https://snapshots.elastic.co/%s-%s/downloads/beats/elastic-agent/", version.CoreVersion(), version.BuildMetadata()), nil
145170
}
146171

147-
buildID, err := findLatestSnapshot(ctx, doer, version.CoreVersion())
172+
buildID, err := backoff.Retry(ctx, func() (any, error) {
173+
return findLatestSnapshot(ctx, doer, version.CoreVersion())
174+
},
175+
backoff.WithMaxTries(3),
176+
backoff.WithBackOff(backoff.NewConstantBackOff(3*time.Second)),
177+
)
148178
if err != nil {
149179
return "", fmt.Errorf("failed to find snapshot information for version %q: %w", version, err)
150180
}

pkg/testing/fetcher_artifact_test.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,16 +110,17 @@ func (c *fakeHttpClient) Do(req *http.Request) (*http.Response, error) {
110110

111111
func newFakeHttpClient(t *testing.T) *fakeHttpClient {
112112
releaseResponse, err := os.ReadFile("./testdata/release-response.json")
113-
require.NoError(t, err)
113+
require.NoError(t, err, "failed to read release response")
114114

115115
snapshotResponse, err := os.ReadFile("./testdata/snapshot-response.json")
116-
require.NoError(t, err)
116+
require.NoError(t, err, "failed to read snapshot response")
117117

118118
manifestResponse, err := os.ReadFile("./testdata/build-manifest.json")
119-
require.NoError(t, err)
119+
require.NoError(t, err, "failed to read manifest response")
120120

121-
binaryResponse := "not valid data; but its very fast to download something this small"
122-
hashResponse := "c2f59774022b79b61a7e6bbe28f3388d00a5bc2c7416a5c8fda79042af491d335f9b87adf905d1b154abdd2e31b200e4b1bb23cb472297596b25edef0a3b8d59"
121+
binaryResponse, err := os.ReadFile("./testdata/data.tar.gz")
122+
require.NoError(t, err, "failed to read binary response")
123+
hashResponse := "cc52f8aa1106857dae8d380f6c2cf789d5d52730df0b0e6aba908a5e1f3cb947fda63bb4bc0301a3bc3329ef4b2f3c5fa9be9d8975a4d0f8f43076cfd5a5ec8a"
123124
ascResponse := `-----BEGIN PGP SIGNATURE-----
124125
125126
wsBcBAABCAAQBQJlTLh5CRD2Vuvax5DnywAAzNcIADKuYov0CMeK938JQEzR4mXP
@@ -153,11 +154,11 @@ func newFakeHttpClient(t *testing.T) *fakeHttpClient {
153154
// 8.12 release
154155
"https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-8.12.0-linux-x86_64.tar.gz": {
155156
StatusCode: 200,
156-
Body: io.NopCloser(bytes.NewReader([]byte(binaryResponse))),
157+
Body: io.NopCloser(bytes.NewReader(binaryResponse)),
157158
},
158159
"https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-8.12.0-linux-x86_64.tar.gz.sha512": {
159160
StatusCode: 200,
160-
Body: io.NopCloser(bytes.NewReader([]byte(hashResponse))),
161+
Body: io.NopCloser(bytes.NewReader([]byte(hashResponse + " elastic-agent-8.12.0-linux-x86_64.tar.gz"))),
161162
},
162163
"https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-8.12.0-linux-x86_64.tar.gz.asc": {
163164
StatusCode: 200,
@@ -166,11 +167,11 @@ func newFakeHttpClient(t *testing.T) *fakeHttpClient {
166167
// 8.13 SNAPSHOT
167168
"https://snapshots.elastic.co/8.13.0-yil7wib0/downloads/beats/elastic-agent/elastic-agent-8.13.0-SNAPSHOT-linux-x86_64.tar.gz": {
168169
StatusCode: 200,
169-
Body: io.NopCloser(bytes.NewReader([]byte(binaryResponse))),
170+
Body: io.NopCloser(bytes.NewReader(binaryResponse)),
170171
},
171172
"https://snapshots.elastic.co/8.13.0-yil7wib0/downloads/beats/elastic-agent/elastic-agent-8.13.0-SNAPSHOT-linux-x86_64.tar.gz.sha512": {
172173
StatusCode: 200,
173-
Body: io.NopCloser(bytes.NewReader([]byte(hashResponse))),
174+
Body: io.NopCloser(bytes.NewReader([]byte(hashResponse + " elastic-agent-8.13.0-SNAPSHOT-linux-x86_64.tar.gz"))),
174175
},
175176
"https://snapshots.elastic.co/8.13.0-yil7wib0/downloads/beats/elastic-agent/elastic-agent-8.13.0-SNAPSHOT-linux-x86_64.tar.gz.asc": {
176177
StatusCode: 200,
@@ -184,11 +185,11 @@ func newFakeHttpClient(t *testing.T) *fakeHttpClient {
184185
// 8.13 build l5snflwr
185186
"https://snapshots.elastic.co/8.13.0-l5snflwr/downloads/beats/elastic-agent/elastic-agent-8.13.0-SNAPSHOT-linux-x86_64.tar.gz": {
186187
StatusCode: 200,
187-
Body: io.NopCloser(bytes.NewReader([]byte(binaryResponse))),
188+
Body: io.NopCloser(bytes.NewReader(binaryResponse)),
188189
},
189190
"https://snapshots.elastic.co/8.13.0-l5snflwr/downloads/beats/elastic-agent/elastic-agent-8.13.0-SNAPSHOT-linux-x86_64.tar.gz.sha512": {
190191
StatusCode: 200,
191-
Body: io.NopCloser(bytes.NewReader([]byte(hashResponse))),
192+
Body: io.NopCloser(bytes.NewReader([]byte(hashResponse + " elastic-agent-8.13.0-SNAPSHOT-linux-x86_64.tar.gz"))),
192193
},
193194
"https://snapshots.elastic.co/8.13.0-l5snflwr/downloads/beats/elastic-agent/elastic-agent-8.13.0-SNAPSHOT-linux-x86_64.tar.gz.asc": {
194195
StatusCode: 200,

pkg/testing/testdata/data.tar.gz

402 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)