Skip to content

Commit

Permalink
expose forceCache option in tuf package
Browse files Browse the repository at this point in the history
Signed-off-by: Brian DeHamer <bdehamer@github.com>
  • Loading branch information
bdehamer committed Jan 10, 2024
1 parent 4471a4d commit c21afdc
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 35 deletions.
5 changes: 5 additions & 0 deletions .changeset/new-birds-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sigstore/tuf": minor
---

Expose `forceCache` option for TUF client
16 changes: 8 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions packages/tuf/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ process.
- `mirrorURL` `<string>`: Base URL for the Sigstore TUF repository. Defaults to `'https://tuf-repo-cdn.sigstore.dev'`
- `cachePath` `<string>`: Absolute path to the directory to be used for caching downloaded TUF metadata and targets. Defaults to a directory named "sigstore-js" within the platform-specific application data directory.
- `rootPath` `<string>`: Path to the initial trust root for the TUF repository. Defaults to the [embedded root](./store/public-good-instance-root.json).
- `force` `boolean`: Force re-initialization of the TUF cache even if it already exists. Defaults to `false`.
- `forceInit` `boolean`: Force re-initialization of the TUF cache even if it already exists. Defaults to `false`.
- `forceCache` `boolean`: Prevents any downloads from the remote TUF repository as long as all cached metadata files are un-expired. Defaults to `false`.
- `force` `boolean`: Same as `forceInit` (deprecated).

The `TUF` client object returned from `initTUF` has a single `getTarget`
function which takes the name of a target in the Sigstore TUF repository
Expand All @@ -58,7 +60,9 @@ verification materials for the Sigstore public-good instance.
- `mirrorURL` `<string>`: Base URL for the Sigstore TUF repository. Defaults to `'https://tuf-repo-cdn.sigstore.dev'`
- `cachePath` `<string>`: Absolute path to the directory to be used for caching downloaded TUF metadata and targets. Defaults to a directory named "sigstore-js" within the platform-specific application data directory.
- `rootPath` `<string>`: Path to the initial trust root for the TUF repository. Defaults to the [embedded root](./store/public-good-instance-root.json).
- `force` `boolean`: Force re-initialization of the TUF cache even if it already exists. Defaults to `false`.
- `forceInit` `boolean`: Force re-initialization of the TUF cache even if it already exists. Defaults to `false`.
- `forceCache` `boolean`: Prevents any downloads from the remote TUF repository as long as all cached metadata files are un-expired. Defaults to `false`.
- `force` `boolean`: Same as `forceInit` (deprecated).

[1]: https://theupdateframework.io/
[2]: https://sigstore-tuf-root.storage.googleapis.com/
Expand Down
2 changes: 1 addition & 1 deletion packages/tuf/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
},
"dependencies": {
"@sigstore/protobuf-specs": "^0.2.1",
"tuf-js": "^2.1.0"
"tuf-js": "^2.2.0"
},
"engines": {
"node": "^16.14.0 || >=18.0.0"
Expand Down
56 changes: 47 additions & 9 deletions packages/tuf/src/__tests__/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ describe('TUFClient', () => {
const mirrorURL = `https://${repoName}`;
const cacheRoot = path.join(os.tmpdir(), 'tuf-client-test');
const cacheDir = path.join(cacheRoot, repoName);
const force = false;
const forceCache = false;
const forceInit = false;

beforeEach(() => {
rootSeedDir = fs.mkdtempSync(
Expand All @@ -55,7 +56,13 @@ describe('TUFClient', () => {

describe('when the cache directory does not exist', () => {
it('creates the cache directory', () => {
new TUFClient({ cachePath: cacheRoot, mirrorURL, rootPath, force });
new TUFClient({
cachePath: cacheRoot,
mirrorURL,
rootPath,
forceCache,
forceInit,
});
expect(fs.existsSync(cacheDir)).toEqual(true);
expect(fs.existsSync(path.join(cacheDir, 'root.json'))).toEqual(true);
});
Expand All @@ -70,7 +77,13 @@ describe('TUFClient', () => {
it('loads config from the existing directory without error', () => {
expect(
() =>
new TUFClient({ cachePath: cacheDir, mirrorURL, rootPath, force })
new TUFClient({
cachePath: cacheDir,
mirrorURL,
rootPath,
forceCache,
forceInit,
})
).not.toThrow();
});
});
Expand All @@ -80,7 +93,13 @@ describe('TUFClient', () => {
const mirrorURL = 'https://oops.net';
it('throws an error', () => {
expect(
() => new TUFClient({ cachePath: cacheDir, mirrorURL, force })
() =>
new TUFClient({
cachePath: cacheDir,
mirrorURL,
forceCache,
forceInit,
})
).toThrowWithCode(TUFError, 'TUF_INIT_CACHE_ERROR');
});
});
Expand All @@ -89,14 +108,20 @@ describe('TUFClient', () => {
const mirrorURL = 'https://tuf-repo-cdn.sigstore.dev';
it('loads the embedded root.json', () => {
expect(
() => new TUFClient({ cachePath: cacheDir, mirrorURL, force })
() =>
new TUFClient({
cachePath: cacheDir,
mirrorURL,
forceCache,
forceInit,
})
).not.toThrow();
});
});
});

describe('when forcing re-initialization of an existing directory', () => {
const force = true;
const forceInit = true;

beforeEach(() => {
fs.mkdirSync(cacheDir, { recursive: true });
Expand All @@ -106,12 +131,24 @@ describe('TUFClient', () => {
it('initializes the client without error', () => {
expect(
() =>
new TUFClient({ cachePath: cacheRoot, mirrorURL, rootPath, force })
new TUFClient({
cachePath: cacheRoot,
mirrorURL,
rootPath,
forceCache,
forceInit,
})
).not.toThrow();
});

it('overwrites the existing values', () => {
new TUFClient({ cachePath: cacheRoot, mirrorURL, rootPath, force });
new TUFClient({
cachePath: cacheRoot,
mirrorURL,
rootPath,
forceCache,
forceInit,
});

const root = fs.readFileSync(path.join(cacheDir, 'root.json'), 'utf-8');
expect(root).toBeDefined();
Expand All @@ -136,7 +173,8 @@ describe('TUFClient', () => {
cachePath: tufRepo.cachePath,
retry: false,
rootPath: path.join(tufRepo.cachePath, 'root.json'),
force: false,
forceCache: false,
forceInit: false,
};
});

Expand Down
36 changes: 23 additions & 13 deletions packages/tuf/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ export type TUFOptions = {
cachePath: string;
mirrorURL: string;
rootPath?: string;
force: boolean;
forceCache: boolean;
forceInit: boolean;
} & FetchOptions;

export interface TUF {
Expand All @@ -57,10 +58,16 @@ export class TUFClient implements TUF {
cachePath,
mirrorURL: options.mirrorURL,
tufRootPath: options.rootPath,
force: options.force,
forceInit: options.forceInit,
});

this.updater = initClient(options.mirrorURL, cachePath, options);
this.updater = initClient({
mirrorURL: options.mirrorURL,
cachePath,
forceCache: options.forceCache,
retry: options.retry,
timeout: options.timeout,
});
}

public async refresh(): Promise<void> {
Expand Down Expand Up @@ -96,18 +103,18 @@ function seedCache({
cachePath,
mirrorURL,
tufRootPath,
force,
forceInit,
}: {
cachePath: string;
mirrorURL: string;
tufRootPath?: string;
force: boolean;
forceInit: boolean;
}): void {
const cachedRootPath = path.join(cachePath, 'root.json');

// If the root.json file does not exist (or we're forcing re-initialization),
// populate it either from the supplied rootPath or from one of the repo seeds.
if (!fs.existsSync(cachedRootPath) || force) {
if (!fs.existsSync(cachedRootPath) || forceInit) {
if (tufRootPath) {
fs.copyFileSync(tufRootPath, cachedRootPath);
} else {
Expand Down Expand Up @@ -137,20 +144,23 @@ function seedCache({
}

function initClient(
mirrorURL: string,
cachePath: string,
options: FetchOptions
options: {
mirrorURL: string;
cachePath: string;
forceCache: boolean;
} & FetchOptions
): Updater {
const config: Partial<Config> = {
fetchTimeout: options.timeout,
fetchRetry: options.retry,
};

return new Updater({
metadataBaseUrl: mirrorURL,
targetBaseUrl: `${mirrorURL}/targets`,
metadataDir: cachePath,
targetDir: path.join(cachePath, TARGETS_DIR_NAME),
metadataBaseUrl: options.mirrorURL,
targetBaseUrl: `${options.mirrorURL}/targets`,
metadataDir: options.cachePath,
targetDir: path.join(options.cachePath, TARGETS_DIR_NAME),
forceCache: options.forceCache,
config,
});
}
8 changes: 6 additions & 2 deletions packages/tuf/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ const DEFAULT_TIMEOUT = 5000;

const TRUSTED_ROOT_TARGET = 'trusted_root.json';

export type TUFOptions = Partial<RequiredTUFOptions>;
export type TUFOptions = Partial<RequiredTUFOptions> & {
// Deprecated, use forceInit instead
force?: boolean;
};

export async function getTrustedRoot(
/* istanbul ignore next */
Expand Down Expand Up @@ -57,7 +60,8 @@ function createClient(options: TUFOptions) {
mirrorURL: options.mirrorURL || DEFAULT_MIRROR_URL,
retry: options.retry ?? DEFAULT_RETRY,
timeout: options.timeout ?? DEFAULT_TIMEOUT,
force: options.force ?? false,
forceCache: options.forceCache ?? false,
forceInit: options.forceInit ?? options.force ?? false,
});
}

Expand Down

0 comments on commit c21afdc

Please sign in to comment.