Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion internal/tufclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,14 @@ func RepoClient(ctx context.Context, cacheDir *pathrs.Root, repoLike tufext.Repo
// Fallback to fetch from the trusted root source.
rootData, err := repo.RootTrust.FetchRoot(ctx, repo)
if err != nil {
return nil, skippableErr(fmt.Errorf("fetch trusted root.json: %w", err))
err = fmt.Errorf("fetch trusted root.json: %w", err)
// Only a missing root.json means the repository may not exist
// yet. Any other failure (auth, network, server error) must not
// look like an empty repository list.
if errors.Is(err, fs.ErrNotExist) {
err = skippableErr(err)
}
return nil, err
Comment on lines +93 to +99

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is actually correct -- if I configure a repository with broken auth (or a repository where the server hasn't yet allowed access to this client) then I think the behaviour should be the same as if I configured a repository that does not exist.

If the root_trust is configured to wait until the repository is accessible, I don't think 404s are less problematic than 403s. And again, this only applies to first-pull (and eventually this will not be used by AOS anyway).

I've moved this to a separate PR -- #95.

Originally posted by @cyphar in #82

}
rootFile, err = repoCacheDir.Create(".", unix.O_TMPFILE|unix.O_RDWR|unix.O_NOFOLLOW, 0o644)
if err != nil {
Expand Down
19 changes: 19 additions & 0 deletions internal/tufclient/client_repos_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,25 @@ func TestNewClient_SkipsInvalidRemoteRoot(t *testing.T) {
require.ErrorContains(t, err, "root_trust root.json is invalid JSON")
}

// A remote root_trust fetch that fails for any reason but a 404 (here, the
// server refusing the client) is fatal for NewClient: only a missing
// repository may be skipped.
func TestNewClient_RemoteRootFetchErrorFatal(t *testing.T) {
valid, denied := testrepo.New(t), testrepo.NewBroken(t)
denied.Handle("/1.root.json", http.HandlerFunc(func(wtr http.ResponseWriter, _ *http.Request) {
wtr.WriteHeader(http.StatusUnauthorized)
}))
cfg := testrepo.Config(t,
valid.ConfigBlock("valid-repo"),
denied.ConfigBlock("denied-repo"),
)

_, err := tufclient.NewClient(t.Context(), cfg)
require.NotErrorIs(t, err, tufclient.ErrSkippableRepo)
require.ErrorContains(t, err, "bad repo denied-repo")
require.ErrorContains(t, err, "status code 401")
}

// A repository with a non-remote root_trust source that fails to load is a
// fatal error for NewClient, never a skippable one -- local sources are meant
// to always exist and be valid.
Expand Down
Loading