Add checksums to SHA256 mismatch error message (#107461)

This should make it easier to debug this issue if we see it again.
This commit is contained in:
beejeebus
2025-07-01 12:02:54 -04:00
committed by GitHub
parent 1620f028b4
commit 73e2ead04b
3 changed files with 13 additions and 10 deletions

View File

@ -100,7 +100,7 @@ func (c *Client) SendReq(ctx context.Context, url *url.URL, compatOpts CompatOpt
return io.ReadAll(bodyReader) return io.ReadAll(bodyReader)
} }
func (c *Client) downloadFile(ctx context.Context, tmpFile *os.File, pluginURL, checksum string, compatOpts CompatOpts) (err error) { func (c *Client) downloadFile(ctx context.Context, tmpFile *os.File, pluginURL, expectedChecksum string, compatOpts CompatOpts) (err error) {
// Try handling URL as a local file path first // Try handling URL as a local file path first
if _, err := os.Stat(pluginURL); err == nil { if _, err := os.Stat(pluginURL); err == nil {
// TODO re-verify // TODO re-verify
@ -136,7 +136,7 @@ func (c *Client) downloadFile(ctx context.Context, tmpFile *os.File, pluginURL,
if err != nil { if err != nil {
return return
} }
err = c.downloadFile(ctx, tmpFile, pluginURL, checksum, compatOpts) err = c.downloadFile(ctx, tmpFile, pluginURL, expectedChecksum, compatOpts)
} else { } else {
c.retryCount = 0 c.retryCount = 0
failure := fmt.Sprintf("%v", r) failure := fmt.Sprintf("%v", r)
@ -169,7 +169,7 @@ func (c *Client) downloadFile(ctx context.Context, tmpFile *os.File, pluginURL,
if c.retryCount < 3 { if c.retryCount < 3 {
c.retryCount++ c.retryCount++
c.log.Debug("Failed downloading. Will retry.") c.log.Debug("Failed downloading. Will retry.")
err = c.downloadFile(ctx, tmpFile, pluginURL, checksum, compatOpts) err = c.downloadFile(ctx, tmpFile, pluginURL, expectedChecksum, compatOpts)
} }
return err return err
} }
@ -187,8 +187,9 @@ func (c *Client) downloadFile(ctx context.Context, tmpFile *os.File, pluginURL,
if err = w.Flush(); err != nil { if err = w.Flush(); err != nil {
return fmt.Errorf("failed to write to %q: %w", tmpFile.Name(), err) return fmt.Errorf("failed to write to %q: %w", tmpFile.Name(), err)
} }
if len(checksum) > 0 && checksum != fmt.Sprintf("%x", h.Sum(nil)) { computedChecksum := fmt.Sprintf("%x", h.Sum(nil))
return ErrChecksumMismatch(pluginURL) if len(expectedChecksum) > 0 && expectedChecksum != computedChecksum {
return ErrChecksumMismatch(pluginURL, expectedChecksum, computedChecksum)
} }
c.retryCount = 0 c.retryCount = 0

View File

@ -60,7 +60,7 @@ var (
ErrArcNotFoundBase = errutil.NotFound("plugin.archNotFound"). ErrArcNotFoundBase = errutil.NotFound("plugin.archNotFound").
MustTemplate(ErrArcNotFoundMsg, errutil.WithPublic(ErrArcNotFoundMsg)) MustTemplate(ErrArcNotFoundMsg, errutil.WithPublic(ErrArcNotFoundMsg))
ErrChecksumMismatchMsg = "expected SHA256 checksum does not match the downloaded archive ({{.Public.ArchiveURL}}) - please contact security@grafana.com" ErrChecksumMismatchMsg = "expected SHA256 checksum ({{.Public.ExpectedSHA256}}) does not match the downloaded archive ({{.Public.ArchiveURL}}) computed SHA256 checksum ({{.Public.ComputedSHA256}}) - please contact security@grafana.com"
ErrChecksumMismatchBase = errutil.UnprocessableEntity("plugin.checksumMismatch"). ErrChecksumMismatchBase = errutil.UnprocessableEntity("plugin.checksumMismatch").
MustTemplate(ErrChecksumMismatchMsg, errutil.WithPublic(ErrChecksumMismatchMsg)) MustTemplate(ErrChecksumMismatchMsg, errutil.WithPublic(ErrChecksumMismatchMsg))
@ -85,8 +85,8 @@ func ErrArcNotFound(pluginID, systemInfo string) error {
return ErrArcNotFoundBase.Build(errutil.TemplateData{Public: map[string]any{"PluginID": pluginID, "SysInfo": systemInfo}}) return ErrArcNotFoundBase.Build(errutil.TemplateData{Public: map[string]any{"PluginID": pluginID, "SysInfo": systemInfo}})
} }
func ErrChecksumMismatch(archiveURL string) error { func ErrChecksumMismatch(archiveURL, expectedSHA256, computedSHA256 string) error {
return ErrChecksumMismatchBase.Build(errutil.TemplateData{Public: map[string]any{"ArchiveURL": archiveURL}}) return ErrChecksumMismatchBase.Build(errutil.TemplateData{Public: map[string]any{"ArchiveURL": archiveURL, "ExpectedSHA256": expectedSHA256, "ComputedSHA256": computedSHA256}})
} }
func ErrCorePlugin(pluginID string) error { func ErrCorePlugin(pluginID string) error {

View File

@ -49,11 +49,13 @@ func TestErrorTemplates(t *testing.T) {
require.Equal(t, "plugin.archNotFound", base.Public().MessageID) require.Equal(t, "plugin.archNotFound", base.Public().MessageID)
require.Equal(t, "grafana-test-app is not compatible with your system architecture: darwin-amd64", base.Public().Message) require.Equal(t, "grafana-test-app is not compatible with your system architecture: darwin-amd64", base.Public().Message)
err = ErrChecksumMismatch("http://localhost:6481/grafana-test-app/versions/1.0.0/download") expectedChecksum := "abcdef1234567890"
computedChecksum := "abcdef0987654321"
err = ErrChecksumMismatch("http://localhost:6481/grafana-test-app/versions/1.0.0/download", expectedChecksum, computedChecksum)
require.True(t, errors.As(err, base)) require.True(t, errors.As(err, base))
require.Equal(t, http.StatusUnprocessableEntity, base.Public().StatusCode) require.Equal(t, http.StatusUnprocessableEntity, base.Public().StatusCode)
require.Equal(t, "plugin.checksumMismatch", base.Public().MessageID) require.Equal(t, "plugin.checksumMismatch", base.Public().MessageID)
require.Equal(t, "expected SHA256 checksum does not match the downloaded archive (http://localhost:6481/grafana-test-app/versions/1.0.0/download) - please contact security@grafana.com", base.Public().Message) require.Equal(t, "expected SHA256 checksum (abcdef1234567890) does not match the downloaded archive (http://localhost:6481/grafana-test-app/versions/1.0.0/download) computed SHA256 checksum (abcdef0987654321) - please contact security@grafana.com", base.Public().Message)
err = ErrCorePlugin("grafana-test-app") err = ErrCorePlugin("grafana-test-app")
require.True(t, errors.As(err, base)) require.True(t, errors.As(err, base))