fix(master): seed the tag digest from running services and hide registry error text (#9)
This commit is contained in:
@@ -3,10 +3,15 @@ package registry
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -22,6 +27,10 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var discardLog = slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
|
||||
func newClient() *Client { return New("test", discardLog) }
|
||||
|
||||
// request is one request the test registry received.
|
||||
type request struct{ method, path string }
|
||||
|
||||
@@ -108,7 +117,7 @@ func TestDigestOfIndexIsIndexDigest(t *testing.T) {
|
||||
reg := newTestRegistry(t, "", "")
|
||||
want := reg.pushIndex(t, "team/app", "1.2")
|
||||
|
||||
got, err := New("test").Digest(context.Background(), reg.host+"/team/app:1.2")
|
||||
got, err := newClient().Digest(context.Background(), reg.host+"/team/app:1.2")
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, want, got)
|
||||
@@ -119,7 +128,7 @@ func TestDigestIgnoresDigestSuffix(t *testing.T) {
|
||||
reg := newTestRegistry(t, "", "")
|
||||
want := reg.pushIndex(t, "team/app", "1.2")
|
||||
|
||||
got, err := New("test").Digest(context.Background(), reg.host+"/team/app:1.2@sha256:"+strings.Repeat("0", 64))
|
||||
got, err := newClient().Digest(context.Background(), reg.host+"/team/app:1.2@sha256:"+strings.Repeat("0", 64))
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, want, got)
|
||||
@@ -131,7 +140,7 @@ func TestDigestUsesHeadOnly(t *testing.T) {
|
||||
reg.pushIndex(t, "team/app", "1.2")
|
||||
reg.reset()
|
||||
|
||||
_, err := New("test").Digest(context.Background(), reg.host+"/team/app:1.2")
|
||||
_, err := newClient().Digest(context.Background(), reg.host+"/team/app:1.2")
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []request{{http.MethodHead, "/v2/team/app/manifests/1.2"}}, reg.manifestRequests())
|
||||
@@ -151,7 +160,7 @@ func TestDigestSendsUserAgent(t *testing.T) {
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
_, _ = New("package-updater-test/1").Digest(context.Background(), strings.TrimPrefix(srv.URL, "http://")+"/a/b:1")
|
||||
_, _ = New("package-updater-test/1", discardLog).Digest(context.Background(), strings.TrimPrefix(srv.URL, "http://")+"/a/b:1")
|
||||
|
||||
assert.Contains(t, ua, "package-updater-test/1")
|
||||
}
|
||||
@@ -162,7 +171,7 @@ func TestCredentialsFromDockerConfig(t *testing.T) {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte("bot:s3cret"))
|
||||
dockerConfig(t, `{"auths":{"`+reg.host+`":{"auth":"`+auth+`"}}}`)
|
||||
|
||||
got, err := New("test").Digest(context.Background(), reg.host+"/team/app:1.2")
|
||||
got, err := newClient().Digest(context.Background(), reg.host+"/team/app:1.2")
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, want, got)
|
||||
@@ -174,7 +183,7 @@ func TestWrongCredentialsFail(t *testing.T) {
|
||||
auth := base64.StdEncoding.EncodeToString([]byte("bot:wrong"))
|
||||
dockerConfig(t, `{"auths":{"`+reg.host+`":{"auth":"`+auth+`"}}}`)
|
||||
|
||||
_, err := New("test").Digest(context.Background(), reg.host+"/team/app:1.2")
|
||||
_, err := newClient().Digest(context.Background(), reg.host+"/team/app:1.2")
|
||||
|
||||
require.Error(t, err)
|
||||
assert.NotContains(t, err.Error(), "wrong", "credentials do not leak into errors")
|
||||
@@ -187,11 +196,11 @@ func TestAnonymousWithoutConfig(t *testing.T) {
|
||||
want := open.pushIndex(t, "team/app", "1.2")
|
||||
protected := newTestRegistry(t, "bot", "s3cret")
|
||||
|
||||
got, err := New("test").Digest(context.Background(), open.host+"/team/app:1.2")
|
||||
got, err := newClient().Digest(context.Background(), open.host+"/team/app:1.2")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, want, got, "anonymous access works without a config file")
|
||||
|
||||
_, err = New("test").Digest(context.Background(), protected.host+"/team/app:1.2")
|
||||
_, err = newClient().Digest(context.Background(), protected.host+"/team/app:1.2")
|
||||
assert.Error(t, err, "a registry that needs credentials fails without them")
|
||||
}
|
||||
|
||||
@@ -200,13 +209,13 @@ func TestUnknownTagIsAnError(t *testing.T) {
|
||||
reg := newTestRegistry(t, "", "")
|
||||
reg.pushIndex(t, "team/app", "1.2")
|
||||
|
||||
_, err := New("test").Digest(context.Background(), reg.host+"/team/app:nope")
|
||||
_, err := newClient().Digest(context.Background(), reg.host+"/team/app:nope")
|
||||
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestRefWithoutTagIsAnError(t *testing.T) {
|
||||
_, err := New("test").Digest(context.Background(), "team/app@sha256:"+strings.Repeat("a", 64))
|
||||
_, err := newClient().Digest(context.Background(), "team/app@sha256:"+strings.Repeat("a", 64))
|
||||
|
||||
assert.ErrorContains(t, err, "has no tag")
|
||||
}
|
||||
@@ -220,7 +229,7 @@ func TestCredsStoreWithoutHelperFailsTheLookup(t *testing.T) {
|
||||
reg.pushIndex(t, "team/app", "1.2")
|
||||
dockerConfig(t, `{"credsStore":"no-such-helper-for-package-updater-tests"}`)
|
||||
|
||||
_, err := New("test").Digest(context.Background(), reg.host+"/team/app:1.2")
|
||||
_, err := newClient().Digest(context.Background(), reg.host+"/team/app:1.2")
|
||||
|
||||
assert.ErrorContains(t, err, "error getting credentials")
|
||||
assert.ErrorContains(t, err, "docker-credential-no-such-helper-for-package-updater-tests")
|
||||
@@ -244,7 +253,7 @@ func TestMissingDigestHeaderDoesNotFallBackToDownload(t *testing.T) {
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
|
||||
_, err := New("test").Digest(context.Background(), strings.TrimPrefix(srv.URL, "http://")+"/a/b:1")
|
||||
_, err := newClient().Digest(context.Background(), strings.TrimPrefix(srv.URL, "http://")+"/a/b:1")
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, []string{http.MethodHead}, methods)
|
||||
@@ -274,7 +283,7 @@ func TestRegistryErrorBodyDoesNotLeak(t *testing.T) {
|
||||
t.Cleanup(srv.Close)
|
||||
host := strings.TrimPrefix(srv.URL, "http://")
|
||||
|
||||
_, err := New("test").Digest(context.Background(), host+"/a/b:1")
|
||||
_, err := newClient().Digest(context.Background(), host+"/a/b:1")
|
||||
|
||||
require.Error(t, err)
|
||||
assert.NotContains(t, err.Error(), "internal-secret-body")
|
||||
@@ -282,3 +291,46 @@ func TestRegistryErrorBodyDoesNotLeak(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// ggcr puts the token server's answer into its error when the answer has no
|
||||
// token, and the token server is named by the registry's challenge.
|
||||
func TestNonRegistryErrorsAreFixedText(t *testing.T) {
|
||||
dockerConfig(t, "")
|
||||
tokenSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write([]byte(`{"detail":"internal-secret-body"}`))
|
||||
}))
|
||||
t.Cleanup(tokenSrv.Close)
|
||||
reg := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("WWW-Authenticate", `Bearer realm="`+tokenSrv.URL+`/internal-secret-realm",service="internal-secret-service"`)
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
}))
|
||||
t.Cleanup(reg.Close)
|
||||
host := strings.TrimPrefix(reg.URL, "http://")
|
||||
|
||||
_, err := newClient().Digest(context.Background(), host+"/a/b:1")
|
||||
|
||||
require.Error(t, err)
|
||||
assert.NotContains(t, err.Error(), "internal-secret")
|
||||
assert.Equal(t, "lookup "+host+"/a/b:1: lookup failed", err.Error())
|
||||
}
|
||||
|
||||
func TestClassify(t *testing.T) {
|
||||
refused := &url.Error{Op: "Get", URL: "http://internal-secret-host/v2/", Err: errors.New("connection refused")}
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
want string
|
||||
}{
|
||||
{"deadline", fmt.Errorf("x: %w", context.DeadlineExceeded), "timeout"},
|
||||
{"canceled", context.Canceled, "timeout"},
|
||||
{"url error", refused, "cannot reach registry"},
|
||||
{"net error", &net.DNSError{Name: "internal-secret-host"}, "cannot reach registry"},
|
||||
{"deadline inside url error", &url.Error{Op: "Get", URL: "http://x", Err: context.DeadlineExceeded}, "timeout"},
|
||||
{"anything else", errors.New("internal-secret"), "lookup failed"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, classify(tt.err))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user