diff --git a/.changeset/curvy-ducks-cross.md b/.changeset/curvy-ducks-cross.md new file mode 100644 index 00000000..f664bf8d --- /dev/null +++ b/.changeset/curvy-ducks-cross.md @@ -0,0 +1,5 @@ +--- +'tuf-js': patch +--- + +Ensure errors are thrown when root metadata is invalid diff --git a/packages/client/src/__tests__/updater.test.ts b/packages/client/src/__tests__/updater.test.ts index 6ebf067e..eae1cd10 100644 --- a/packages/client/src/__tests__/updater.test.ts +++ b/packages/client/src/__tests__/updater.test.ts @@ -1,6 +1,7 @@ import { clearMock, mockRepo } from '@tufjs/repo-mock'; import assert from 'assert'; import fs from 'fs'; +import nock from 'nock'; import os from 'os'; import path from 'path'; import { Updater, UpdaterOptions } from '../updater'; @@ -76,6 +77,26 @@ describe('Updater', () => { await expect(updater.refresh()).resolves.toBe(undefined); }); }); + + describe('when repo serves invalid root metadata', () => { + beforeEach(() => { + // Remove 404 response for 2.root.json + const repoURL = new URL(baseURL); + nock.removeInterceptor({ + hostname: repoURL.hostname, + port: repoURL.port, + path: '/metadata/2.root.json', + }); + + // Serve 2.root.json w/ invalid data + nock(baseURL).get('/metadata/2.root.json').reply(200, {}); + }); + + it('resolves with no error', async () => { + const updater = new Updater(options); + await expect(updater.refresh()).rejects.toThrow(TypeError); + }); + }); }); describe('#getTargetInfo', () => { diff --git a/packages/client/src/updater.ts b/packages/client/src/updater.ts index aa4570c0..f8b23ed2 100644 --- a/packages/client/src/updater.ts +++ b/packages/client/src/updater.ts @@ -4,6 +4,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { Config, defaultConfig } from './config'; import { + DownloadHTTPError, EqualVersionError, PersistError, RuntimeError, @@ -200,7 +201,14 @@ export class Updater { // Client workflow 5.3.8: persist root metadata file this.persistMetadata(MetadataKind.Root, bytesData); } catch (error) { - break; + if (error instanceof DownloadHTTPError) { + // 404/403 means current root is newest available + if ([403, 404].includes(error.statusCode)) { + break; + } + } + + throw error; } } }