@sigstore/tuf
Advanced tools
Comparing version
@@ -10,4 +10,5 @@ import type { MakeFetchHappenOptions } from 'make-fetch-happen'; | ||
mirrorURL: string; | ||
rootPath: string; | ||
force: boolean; | ||
rootPath?: string; | ||
forceCache: boolean; | ||
forceInit: boolean; | ||
} & FetchOptions; | ||
@@ -14,0 +15,0 @@ export interface TUF { |
@@ -25,8 +25,25 @@ "use strict"; | ||
const tuf_js_1 = require("tuf-js"); | ||
const _1 = require("."); | ||
const target_1 = require("./target"); | ||
const TUF_SEEDS_PATH = require.resolve('../seeds.json'); | ||
const TARGETS_DIR_NAME = 'targets'; | ||
class TUFClient { | ||
constructor(options) { | ||
initTufCache(options); | ||
const remote = initRemoteConfig(options); | ||
this.updater = initClient(options.cachePath, remote, options); | ||
const url = new URL(options.mirrorURL); | ||
const repoName = encodeURIComponent(url.host + url.pathname.replace(/\/$/, '')); | ||
const cachePath = path_1.default.join(options.cachePath, repoName); | ||
initTufCache(cachePath); | ||
seedCache({ | ||
cachePath, | ||
mirrorURL: options.mirrorURL, | ||
tufRootPath: options.rootPath, | ||
forceInit: options.forceInit, | ||
}); | ||
this.updater = initClient({ | ||
mirrorURL: options.mirrorURL, | ||
cachePath, | ||
forceCache: options.forceCache, | ||
retry: options.retry, | ||
timeout: options.timeout, | ||
}); | ||
} | ||
@@ -46,5 +63,4 @@ async refresh() { | ||
// rootPath argument. | ||
function initTufCache({ cachePath, rootPath: tufRootPath, force, }) { | ||
const targetsPath = path_1.default.join(cachePath, 'targets'); | ||
const cachedRootPath = path_1.default.join(cachePath, 'root.json'); | ||
function initTufCache(cachePath) { | ||
const targetsPath = path_1.default.join(cachePath, TARGETS_DIR_NAME); | ||
if (!fs_1.default.existsSync(cachePath)) { | ||
@@ -56,31 +72,33 @@ fs_1.default.mkdirSync(cachePath, { recursive: true }); | ||
} | ||
} | ||
// Populates the TUF cache with the initial root.json file. If the root.json | ||
// file does not exist (or we're forcing re-initialization), copy it from either | ||
// the rootPath argument or from one of the repo seeds. | ||
function seedCache({ cachePath, mirrorURL, tufRootPath, forceInit, }) { | ||
const cachedRootPath = path_1.default.join(cachePath, 'root.json'); | ||
// If the root.json file does not exist (or we're forcing re-initialization), | ||
// copy it from the rootPath argument | ||
if (!fs_1.default.existsSync(cachedRootPath) || force) { | ||
fs_1.default.copyFileSync(tufRootPath, cachedRootPath); | ||
// populate it either from the supplied rootPath or from one of the repo seeds. | ||
if (!fs_1.default.existsSync(cachedRootPath) || forceInit) { | ||
if (tufRootPath) { | ||
fs_1.default.copyFileSync(tufRootPath, cachedRootPath); | ||
} | ||
else { | ||
// Load the embedded repo seeds | ||
const seeds = JSON.parse(fs_1.default.readFileSync(TUF_SEEDS_PATH).toString('utf-8')); | ||
const repoSeed = seeds[mirrorURL]; | ||
if (!repoSeed) { | ||
throw new _1.TUFError({ | ||
code: 'TUF_INIT_CACHE_ERROR', | ||
message: `No root.json found for mirror: ${mirrorURL}`, | ||
}); | ||
} | ||
fs_1.default.writeFileSync(cachedRootPath, Buffer.from(repoSeed['root.json'], 'base64')); | ||
// Copy any seed targets into the cache | ||
Object.entries(repoSeed.targets).forEach(([targetName, target]) => { | ||
fs_1.default.writeFileSync(path_1.default.join(cachePath, TARGETS_DIR_NAME, targetName), Buffer.from(target, 'base64')); | ||
}); | ||
} | ||
} | ||
return cachePath; | ||
} | ||
// Initializes the remote.json file, which contains the URL of the TUF | ||
// repository. If the file does not exist, it will be created. If the file | ||
// exists, it will be parsed and returned. | ||
function initRemoteConfig({ cachePath, mirrorURL, force, }) { | ||
let remoteConfig; | ||
const remoteConfigPath = path_1.default.join(cachePath, 'remote.json'); | ||
// If the remote config file exists, read it and parse it (skip if force is | ||
// true) | ||
if (!force && fs_1.default.existsSync(remoteConfigPath)) { | ||
const data = fs_1.default.readFileSync(remoteConfigPath, 'utf-8'); | ||
remoteConfig = JSON.parse(data); | ||
} | ||
// If the remote config file does not exist (or we're forcing initialization), | ||
// create it | ||
if (!remoteConfig || force) { | ||
remoteConfig = { mirror: mirrorURL }; | ||
fs_1.default.writeFileSync(remoteConfigPath, JSON.stringify(remoteConfig)); | ||
} | ||
return remoteConfig; | ||
} | ||
function initClient(cachePath, remote, options) { | ||
const baseURL = remote.mirror; | ||
function initClient(options) { | ||
const config = { | ||
@@ -91,8 +109,9 @@ fetchTimeout: options.timeout, | ||
return new tuf_js_1.Updater({ | ||
metadataBaseUrl: baseURL, | ||
targetBaseUrl: `${baseURL}/targets`, | ||
metadataDir: cachePath, | ||
targetDir: path_1.default.join(cachePath, 'targets'), | ||
metadataBaseUrl: options.mirrorURL, | ||
targetBaseUrl: `${options.mirrorURL}/targets`, | ||
metadataDir: options.cachePath, | ||
targetDir: path_1.default.join(options.cachePath, TARGETS_DIR_NAME), | ||
forceCache: options.forceCache, | ||
config, | ||
}); | ||
} |
@@ -1,2 +0,2 @@ | ||
type TUFErrorCode = 'TUF_FIND_TARGET_ERROR' | 'TUF_REFRESH_METADATA_ERROR' | 'TUF_DOWNLOAD_TARGET_ERROR' | 'TUF_READ_TARGET_ERROR'; | ||
type TUFErrorCode = 'TUF_INIT_CACHE_ERROR' | 'TUF_FIND_TARGET_ERROR' | 'TUF_REFRESH_METADATA_ERROR' | 'TUF_DOWNLOAD_TARGET_ERROR' | 'TUF_READ_TARGET_ERROR'; | ||
export declare class TUFError extends Error { | ||
@@ -3,0 +3,0 @@ code: TUFErrorCode; |
import { TrustedRoot } from '@sigstore/protobuf-specs'; | ||
import { TUFOptions as RequiredTUFOptions, TUF } from './client'; | ||
export declare const DEFAULT_MIRROR_URL = "https://tuf-repo-cdn.sigstore.dev"; | ||
export type TUFOptions = Partial<RequiredTUFOptions>; | ||
export type TUFOptions = Partial<RequiredTUFOptions> & { | ||
force?: boolean; | ||
}; | ||
export declare function getTrustedRoot(options?: TUFOptions): Promise<TrustedRoot>; | ||
@@ -6,0 +8,0 @@ export declare function initTUF(options?: TUFOptions): Promise<TUF>; |
@@ -24,3 +24,2 @@ "use strict"; | ||
const DEFAULT_CACHE_DIR = 'sigstore-js'; | ||
const DEFAULT_TUF_ROOT_PATH = '../store/public-good-instance-root.json'; | ||
const DEFAULT_RETRY = { retries: 2 }; | ||
@@ -49,7 +48,8 @@ const DEFAULT_TIMEOUT = 5000; | ||
cachePath: options.cachePath || (0, appdata_1.appDataPath)(DEFAULT_CACHE_DIR), | ||
rootPath: options.rootPath || require.resolve(DEFAULT_TUF_ROOT_PATH), | ||
rootPath: options.rootPath, | ||
mirrorURL: options.mirrorURL || exports.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, | ||
}); | ||
@@ -56,0 +56,0 @@ } |
{ | ||
"name": "@sigstore/tuf", | ||
"version": "2.2.0", | ||
"version": "2.3.0", | ||
"description": "Client for the Sigstore TUF repository", | ||
@@ -14,3 +14,3 @@ "main": "dist/index.js", | ||
"dist", | ||
"store" | ||
"seeds.json" | ||
], | ||
@@ -33,7 +33,7 @@ "author": "bdehamer@github.com", | ||
"@tufjs/repo-mock": "^2.0.0", | ||
"@types/make-fetch-happen": "^10.0.0" | ||
"@types/make-fetch-happen": "^10.0.4" | ||
}, | ||
"dependencies": { | ||
"@sigstore/protobuf-specs": "^0.2.1", | ||
"tuf-js": "^2.1.0" | ||
"tuf-js": "^2.2.0" | ||
}, | ||
@@ -40,0 +40,0 @@ "engines": { |
@@ -15,3 +15,3 @@ # @sigstore/tuf · [](https://www.npmjs.com/package/@sigstore/tuf) [](https://github.com/sigstore/sigstore-js/actions/workflows/ci.yml) [](https://github.com/sigstore/sigstore-js/actions/workflows/smoke-test.yml) | ||
- Node.js version >= 14.17.0 | ||
- Node.js version >= 16.14.0 | ||
@@ -45,3 +45,5 @@ ## Installation | ||
- `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). | ||
@@ -63,3 +65,5 @@ The `TUF` client object returned from `initTUF` has a single `getTarget` | ||
- `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). | ||
@@ -66,0 +70,0 @@ [1]: https://theupdateframework.io/ |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
47532
44.87%411
21.96%70
6.06%5
-28.57%Updated