Skip to content

Commit b08d284

Browse files
committed
tufclient: skip a repo only when its root.json is missing
Any failure to fetch a repo's first root.json skipped the repo. A machine that could not authenticate then updated nothing and still reported success. Now only a missing root.json, which means the repo may not exist yet, skips the repo. Any other failure is an error, so one repo that fails this way stops the whole run. Signed-off-by: Joaquim Rocha <joaquim@amutable.com>
1 parent d08f5eb commit b08d284

2 files changed

Lines changed: 27 additions & 1 deletion

File tree

‎internal/tufclient/client.go‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,14 @@ func RepoClient(ctx context.Context, cacheDir *pathrs.Root, repo *config.Reposit
7878
// Fallback to fetch from the trusted root source.
7979
rootData, err := repo.RootTrust.FetchRoot(ctx, repo)
8080
if err != nil {
81-
return nil, skippableErr(fmt.Errorf("fetch trusted root.json: %w", err))
81+
err = fmt.Errorf("fetch trusted root.json: %w", err)
82+
// Only a missing root.json means the repository may not exist
83+
// yet. Any other failure (auth, network, server error) must not
84+
// look like an empty repository list.
85+
if errors.Is(err, fs.ErrNotExist) {
86+
err = skippableErr(err)
87+
}
88+
return nil, err
8289
}
8390
rootFile, err = repoCacheDir.Create(".", unix.O_TMPFILE|unix.O_RDWR|unix.O_NOFOLLOW, 0o644)
8491
if err != nil {

‎internal/tufclient/client_repos_test.go‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,25 @@ func TestNewClient_SkipsInvalidRemoteRoot(t *testing.T) {
184184
require.ErrorContains(t, err, "root_trust root.json is invalid JSON")
185185
}
186186

187+
// A remote root_trust fetch that fails for any reason but a 404 (here, the
188+
// server refusing the client) is fatal for NewClient: only a missing
189+
// repository may be skipped.
190+
func TestNewClient_RemoteRootFetchErrorFatal(t *testing.T) {
191+
valid, denied := testrepo.New(t), testrepo.NewBroken(t)
192+
denied.Handle("/1.root.json", http.HandlerFunc(func(wtr http.ResponseWriter, _ *http.Request) {
193+
wtr.WriteHeader(http.StatusUnauthorized)
194+
}))
195+
cfg := testrepo.Config(t,
196+
valid.ConfigBlock("valid-repo"),
197+
denied.ConfigBlock("denied-repo"),
198+
)
199+
200+
_, err := tufclient.NewClient(t.Context(), cfg)
201+
require.NotErrorIs(t, err, tufclient.ErrSkippableRepo)
202+
require.ErrorContains(t, err, "bad repo denied-repo")
203+
require.ErrorContains(t, err, "status code 401")
204+
}
205+
187206
// A repository with a non-remote root_trust source that fails to load is a
188207
// fatal error for NewClient, never a skippable one -- local sources are meant
189208
// to always exist and be valid.

0 commit comments

Comments
 (0)