This commit is contained in:
2025-08-18 23:06:34 +08:00
parent 0bc04fb659
commit ed18af0cad
1926 changed files with 275098 additions and 0 deletions

15
package/node_modules/tuf-js/dist/config.js generated vendored Normal file
View File

@@ -0,0 +1,15 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultConfig = void 0;
exports.defaultConfig = {
maxRootRotations: 32,
maxDelegations: 32,
rootMaxLength: 512000, //bytes
timestampMaxLength: 16384, // bytes
snapshotMaxLength: 2000000, // bytes
targetsMaxLength: 5000000, // bytes
prefixTargetsWithHash: true,
fetchTimeout: 100000, // milliseconds
fetchRetries: undefined,
fetchRetry: 2,
};

48
package/node_modules/tuf-js/dist/error.js generated vendored Normal file
View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DownloadHTTPError = exports.DownloadLengthMismatchError = exports.DownloadError = exports.ExpiredMetadataError = exports.EqualVersionError = exports.BadVersionError = exports.RepositoryError = exports.PersistError = exports.RuntimeError = exports.ValueError = void 0;
// An error about insufficient values
class ValueError extends Error {
}
exports.ValueError = ValueError;
class RuntimeError extends Error {
}
exports.RuntimeError = RuntimeError;
class PersistError extends Error {
}
exports.PersistError = PersistError;
// An error with a repository's state, such as a missing file.
// It covers all exceptions that come from the repository side when
// looking from the perspective of users of metadata API or ngclient.
class RepositoryError extends Error {
}
exports.RepositoryError = RepositoryError;
// An error for metadata that contains an invalid version number.
class BadVersionError extends RepositoryError {
}
exports.BadVersionError = BadVersionError;
// An error for metadata containing a previously verified version number.
class EqualVersionError extends BadVersionError {
}
exports.EqualVersionError = EqualVersionError;
// Indicate that a TUF Metadata file has expired.
class ExpiredMetadataError extends RepositoryError {
}
exports.ExpiredMetadataError = ExpiredMetadataError;
//----- Download Errors -------------------------------------------------------
// An error occurred while attempting to download a file.
class DownloadError extends Error {
}
exports.DownloadError = DownloadError;
// Indicate that a mismatch of lengths was seen while downloading a file
class DownloadLengthMismatchError extends DownloadError {
}
exports.DownloadLengthMismatchError = DownloadLengthMismatchError;
// Returned by FetcherInterface implementations for HTTP errors.
class DownloadHTTPError extends DownloadError {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
}
}
exports.DownloadHTTPError = DownloadHTTPError;

84
package/node_modules/tuf-js/dist/fetcher.js generated vendored Normal file
View File

@@ -0,0 +1,84 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultFetcher = exports.BaseFetcher = void 0;
const debug_1 = __importDefault(require("debug"));
const fs_1 = __importDefault(require("fs"));
const make_fetch_happen_1 = __importDefault(require("make-fetch-happen"));
const util_1 = __importDefault(require("util"));
const error_1 = require("./error");
const tmpfile_1 = require("./utils/tmpfile");
const log = (0, debug_1.default)('tuf:fetch');
class BaseFetcher {
// Download file from given URL. The file is downloaded to a temporary
// location and then passed to the given handler. The handler is responsible
// for moving the file to its final location. The temporary file is deleted
// after the handler returns.
async downloadFile(url, maxLength, handler) {
return (0, tmpfile_1.withTempFile)(async (tmpFile) => {
const reader = await this.fetch(url);
let numberOfBytesReceived = 0;
const fileStream = fs_1.default.createWriteStream(tmpFile);
// Read the stream a chunk at a time so that we can check
// the length of the file as we go
try {
for await (const chunk of reader) {
const bufferChunk = Buffer.from(chunk);
numberOfBytesReceived += bufferChunk.length;
if (numberOfBytesReceived > maxLength) {
throw new error_1.DownloadLengthMismatchError('Max length reached');
}
await writeBufferToStream(fileStream, bufferChunk);
}
}
finally {
// Make sure we always close the stream
await util_1.default.promisify(fileStream.close).bind(fileStream)();
}
return handler(tmpFile);
});
}
// Download bytes from given URL.
async downloadBytes(url, maxLength) {
return this.downloadFile(url, maxLength, async (file) => {
const stream = fs_1.default.createReadStream(file);
const chunks = [];
for await (const chunk of stream) {
chunks.push(chunk);
}
return Buffer.concat(chunks);
});
}
}
exports.BaseFetcher = BaseFetcher;
class DefaultFetcher extends BaseFetcher {
constructor(options = {}) {
super();
this.timeout = options.timeout;
this.retry = options.retry;
}
async fetch(url) {
log('GET %s', url);
const response = await (0, make_fetch_happen_1.default)(url, {
timeout: this.timeout,
retry: this.retry,
});
if (!response.ok || !response?.body) {
throw new error_1.DownloadHTTPError('Failed to download', response.status);
}
return response.body;
}
}
exports.DefaultFetcher = DefaultFetcher;
const writeBufferToStream = async (stream, buffer) => {
return new Promise((resolve, reject) => {
stream.write(buffer, (err) => {
if (err) {
reject(err);
}
resolve(true);
});
});
};

9
package/node_modules/tuf-js/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,9 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Updater = exports.BaseFetcher = exports.TargetFile = void 0;
var models_1 = require("@tufjs/models");
Object.defineProperty(exports, "TargetFile", { enumerable: true, get: function () { return models_1.TargetFile; } });
var fetcher_1 = require("./fetcher");
Object.defineProperty(exports, "BaseFetcher", { enumerable: true, get: function () { return fetcher_1.BaseFetcher; } });
var updater_1 = require("./updater");
Object.defineProperty(exports, "Updater", { enumerable: true, get: function () { return updater_1.Updater; } });

208
package/node_modules/tuf-js/dist/store.js generated vendored Normal file
View File

@@ -0,0 +1,208 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TrustedMetadataStore = void 0;
const models_1 = require("@tufjs/models");
const error_1 = require("./error");
class TrustedMetadataStore {
constructor(rootData) {
this.trustedSet = {};
// Client workflow 5.1: record fixed update start time
this.referenceTime = new Date();
// Client workflow 5.2: load trusted root metadata
this.loadTrustedRoot(rootData);
}
get root() {
if (!this.trustedSet.root) {
throw new ReferenceError('No trusted root metadata');
}
return this.trustedSet.root;
}
get timestamp() {
return this.trustedSet.timestamp;
}
get snapshot() {
return this.trustedSet.snapshot;
}
get targets() {
return this.trustedSet.targets;
}
getRole(name) {
return this.trustedSet[name];
}
updateRoot(bytesBuffer) {
const data = JSON.parse(bytesBuffer.toString('utf8'));
const newRoot = models_1.Metadata.fromJSON(models_1.MetadataKind.Root, data);
if (newRoot.signed.type != models_1.MetadataKind.Root) {
throw new error_1.RepositoryError(`Expected 'root', got ${newRoot.signed.type}`);
}
// Client workflow 5.4: check for arbitrary software attack
this.root.verifyDelegate(models_1.MetadataKind.Root, newRoot);
// Client workflow 5.5: check for rollback attack
if (newRoot.signed.version != this.root.signed.version + 1) {
throw new error_1.BadVersionError(`Expected version ${this.root.signed.version + 1}, got ${newRoot.signed.version}`);
}
// Check that new root is signed by self
newRoot.verifyDelegate(models_1.MetadataKind.Root, newRoot);
// Client workflow 5.7: set new root as trusted root
this.trustedSet.root = newRoot;
return newRoot;
}
updateTimestamp(bytesBuffer) {
if (this.snapshot) {
throw new error_1.RuntimeError('Cannot update timestamp after snapshot');
}
if (this.root.signed.isExpired(this.referenceTime)) {
throw new error_1.ExpiredMetadataError('Final root.json is expired');
}
const data = JSON.parse(bytesBuffer.toString('utf8'));
const newTimestamp = models_1.Metadata.fromJSON(models_1.MetadataKind.Timestamp, data);
if (newTimestamp.signed.type != models_1.MetadataKind.Timestamp) {
throw new error_1.RepositoryError(`Expected 'timestamp', got ${newTimestamp.signed.type}`);
}
// Client workflow 5.4.2: check for arbitrary software attack
this.root.verifyDelegate(models_1.MetadataKind.Timestamp, newTimestamp);
if (this.timestamp) {
// Prevent rolling back timestamp version
// Client workflow 5.4.3.1: check for rollback attack
if (newTimestamp.signed.version < this.timestamp.signed.version) {
throw new error_1.BadVersionError(`New timestamp version ${newTimestamp.signed.version} is less than current version ${this.timestamp.signed.version}`);
}
// Keep using old timestamp if versions are equal.
if (newTimestamp.signed.version === this.timestamp.signed.version) {
throw new error_1.EqualVersionError(`New timestamp version ${newTimestamp.signed.version} is equal to current version ${this.timestamp.signed.version}`);
}
// Prevent rolling back snapshot version
// Client workflow 5.4.3.2: check for rollback attack
const snapshotMeta = this.timestamp.signed.snapshotMeta;
const newSnapshotMeta = newTimestamp.signed.snapshotMeta;
if (newSnapshotMeta.version < snapshotMeta.version) {
throw new error_1.BadVersionError(`New snapshot version ${newSnapshotMeta.version} is less than current version ${snapshotMeta.version}`);
}
}
// expiry not checked to allow old timestamp to be used for rollback
// protection of new timestamp: expiry is checked in update_snapshot
this.trustedSet.timestamp = newTimestamp;
// Client workflow 5.4.4: check for freeze attack
this.checkFinalTimestamp();
return newTimestamp;
}
updateSnapshot(bytesBuffer, trusted = false) {
if (!this.timestamp) {
throw new error_1.RuntimeError('Cannot update snapshot before timestamp');
}
if (this.targets) {
throw new error_1.RuntimeError('Cannot update snapshot after targets');
}
// Snapshot cannot be loaded if final timestamp is expired
this.checkFinalTimestamp();
const snapshotMeta = this.timestamp.signed.snapshotMeta;
// Verify non-trusted data against the hashes in timestamp, if any.
// Trusted snapshot data has already been verified once.
// Client workflow 5.5.2: check against timestamp role's snaphsot hash
if (!trusted) {
snapshotMeta.verify(bytesBuffer);
}
const data = JSON.parse(bytesBuffer.toString('utf8'));
const newSnapshot = models_1.Metadata.fromJSON(models_1.MetadataKind.Snapshot, data);
if (newSnapshot.signed.type != models_1.MetadataKind.Snapshot) {
throw new error_1.RepositoryError(`Expected 'snapshot', got ${newSnapshot.signed.type}`);
}
// Client workflow 5.5.3: check for arbitrary software attack
this.root.verifyDelegate(models_1.MetadataKind.Snapshot, newSnapshot);
// version check against meta version (5.5.4) is deferred to allow old
// snapshot to be used in rollback protection
// Client workflow 5.5.5: check for rollback attack
if (this.snapshot) {
Object.entries(this.snapshot.signed.meta).forEach(([fileName, fileInfo]) => {
const newFileInfo = newSnapshot.signed.meta[fileName];
if (!newFileInfo) {
throw new error_1.RepositoryError(`Missing file ${fileName} in new snapshot`);
}
if (newFileInfo.version < fileInfo.version) {
throw new error_1.BadVersionError(`New version ${newFileInfo.version} of ${fileName} is less than current version ${fileInfo.version}`);
}
});
}
this.trustedSet.snapshot = newSnapshot;
// snapshot is loaded, but we raise if it's not valid _final_ snapshot
// Client workflow 5.5.4 & 5.5.6
this.checkFinalSnapsnot();
return newSnapshot;
}
updateDelegatedTargets(bytesBuffer, roleName, delegatorName) {
if (!this.snapshot) {
throw new error_1.RuntimeError('Cannot update delegated targets before snapshot');
}
// Targets cannot be loaded if final snapshot is expired or its version
// does not match meta version in timestamp.
this.checkFinalSnapsnot();
const delegator = this.trustedSet[delegatorName];
if (!delegator) {
throw new error_1.RuntimeError(`No trusted ${delegatorName} metadata`);
}
// Extract metadata for the delegated role from snapshot
const meta = this.snapshot.signed.meta?.[`${roleName}.json`];
if (!meta) {
throw new error_1.RepositoryError(`Missing ${roleName}.json in snapshot`);
}
// Client workflow 5.6.2: check against snapshot role's targets hash
meta.verify(bytesBuffer);
const data = JSON.parse(bytesBuffer.toString('utf8'));
const newDelegate = models_1.Metadata.fromJSON(models_1.MetadataKind.Targets, data);
if (newDelegate.signed.type != models_1.MetadataKind.Targets) {
throw new error_1.RepositoryError(`Expected 'targets', got ${newDelegate.signed.type}`);
}
// Client workflow 5.6.3: check for arbitrary software attack
delegator.verifyDelegate(roleName, newDelegate);
// Client workflow 5.6.4: Check against snapshot roles targets version
const version = newDelegate.signed.version;
if (version != meta.version) {
throw new error_1.BadVersionError(`Version ${version} of ${roleName} does not match snapshot version ${meta.version}`);
}
// Client workflow 5.6.5: check for a freeze attack
if (newDelegate.signed.isExpired(this.referenceTime)) {
throw new error_1.ExpiredMetadataError(`${roleName}.json is expired`);
}
this.trustedSet[roleName] = newDelegate;
}
// Verifies and loads data as trusted root metadata.
// Note that an expired initial root is still considered valid.
loadTrustedRoot(bytesBuffer) {
const data = JSON.parse(bytesBuffer.toString('utf8'));
const root = models_1.Metadata.fromJSON(models_1.MetadataKind.Root, data);
if (root.signed.type != models_1.MetadataKind.Root) {
throw new error_1.RepositoryError(`Expected 'root', got ${root.signed.type}`);
}
root.verifyDelegate(models_1.MetadataKind.Root, root);
this.trustedSet['root'] = root;
}
checkFinalTimestamp() {
// Timestamp MUST be loaded
if (!this.timestamp) {
throw new ReferenceError('No trusted timestamp metadata');
}
// Client workflow 5.4.4: check for freeze attack
if (this.timestamp.signed.isExpired(this.referenceTime)) {
throw new error_1.ExpiredMetadataError('Final timestamp.json is expired');
}
}
checkFinalSnapsnot() {
// Snapshot and timestamp MUST be loaded
if (!this.snapshot) {
throw new ReferenceError('No trusted snapshot metadata');
}
if (!this.timestamp) {
throw new ReferenceError('No trusted timestamp metadata');
}
// Client workflow 5.5.6: check for freeze attack
if (this.snapshot.signed.isExpired(this.referenceTime)) {
throw new error_1.ExpiredMetadataError('snapshot.json is expired');
}
// Client workflow 5.5.4: check against timestamp roles snapshot version
const snapshotMeta = this.timestamp.signed.snapshotMeta;
if (this.snapshot.signed.version !== snapshotMeta.version) {
throw new error_1.BadVersionError("Snapshot version doesn't match timestamp");
}
}
}
exports.TrustedMetadataStore = TrustedMetadataStore;

343
package/node_modules/tuf-js/dist/updater.js generated vendored Normal file
View File

@@ -0,0 +1,343 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Updater = void 0;
const models_1 = require("@tufjs/models");
const debug_1 = __importDefault(require("debug"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const config_1 = require("./config");
const error_1 = require("./error");
const fetcher_1 = require("./fetcher");
const store_1 = require("./store");
const url = __importStar(require("./utils/url"));
const log = (0, debug_1.default)('tuf:cache');
class Updater {
constructor(options) {
const { metadataDir, metadataBaseUrl, targetDir, targetBaseUrl, fetcher, config, } = options;
this.dir = metadataDir;
this.metadataBaseUrl = metadataBaseUrl;
this.targetDir = targetDir;
this.targetBaseUrl = targetBaseUrl;
this.forceCache = options.forceCache ?? false;
const data = this.loadLocalMetadata(models_1.MetadataKind.Root);
this.trustedSet = new store_1.TrustedMetadataStore(data);
this.config = { ...config_1.defaultConfig, ...config };
this.fetcher =
fetcher ||
new fetcher_1.DefaultFetcher({
timeout: this.config.fetchTimeout,
retry: this.config.fetchRetries ?? this.config.fetchRetry,
});
}
// refresh and load the metadata before downloading the target
// refresh should be called once after the client is initialized
async refresh() {
// If forceCache is true, try to load the timestamp from local storage
// without fetching it from the remote. Otherwise, load the root and
// timestamp from the remote per the TUF spec.
if (this.forceCache) {
// If anything fails, load the root and timestamp from the remote. This
// should cover any situation where the local metadata is corrupted or
// expired.
try {
await this.loadTimestamp({ checkRemote: false });
}
catch (error) {
await this.loadRoot();
await this.loadTimestamp();
}
}
else {
await this.loadRoot();
await this.loadTimestamp();
}
await this.loadSnapshot();
await this.loadTargets(models_1.MetadataKind.Targets, models_1.MetadataKind.Root);
}
// Returns the TargetFile instance with information for the given target path.
//
// Implicitly calls refresh if it hasn't already been called.
async getTargetInfo(targetPath) {
if (!this.trustedSet.targets) {
await this.refresh();
}
return this.preorderDepthFirstWalk(targetPath);
}
async downloadTarget(targetInfo, filePath, targetBaseUrl) {
const targetPath = filePath || this.generateTargetPath(targetInfo);
if (!targetBaseUrl) {
if (!this.targetBaseUrl) {
throw new error_1.ValueError('Target base URL not set');
}
targetBaseUrl = this.targetBaseUrl;
}
let targetFilePath = targetInfo.path;
const consistentSnapshot = this.trustedSet.root.signed.consistentSnapshot;
if (consistentSnapshot && this.config.prefixTargetsWithHash) {
const hashes = Object.values(targetInfo.hashes);
const { dir, base } = path.parse(targetFilePath);
const filename = `${hashes[0]}.${base}`;
targetFilePath = dir ? `${dir}/${filename}` : filename;
}
const targetUrl = url.join(targetBaseUrl, targetFilePath);
// Client workflow 5.7.3: download target file
await this.fetcher.downloadFile(targetUrl, targetInfo.length, async (fileName) => {
// Verify hashes and length of downloaded file
await targetInfo.verify(fs.createReadStream(fileName));
// Copy file to target path
log('WRITE %s', targetPath);
fs.copyFileSync(fileName, targetPath);
});
return targetPath;
}
async findCachedTarget(targetInfo, filePath) {
if (!filePath) {
filePath = this.generateTargetPath(targetInfo);
}
try {
if (fs.existsSync(filePath)) {
await targetInfo.verify(fs.createReadStream(filePath));
return filePath;
}
}
catch (error) {
return; // File not found
}
return; // File not found
}
loadLocalMetadata(fileName) {
const filePath = path.join(this.dir, `${fileName}.json`);
log('READ %s', filePath);
return fs.readFileSync(filePath);
}
// Sequentially load and persist on local disk every newer root metadata
// version available on the remote.
// Client workflow 5.3: update root role
async loadRoot() {
// Client workflow 5.3.2: version of trusted root metadata file
const rootVersion = this.trustedSet.root.signed.version;
const lowerBound = rootVersion + 1;
const upperBound = lowerBound + this.config.maxRootRotations;
for (let version = lowerBound; version <= upperBound; version++) {
const rootUrl = url.join(this.metadataBaseUrl, `${version}.root.json`);
try {
// Client workflow 5.3.3: download new root metadata file
const bytesData = await this.fetcher.downloadBytes(rootUrl, this.config.rootMaxLength);
// Client workflow 5.3.4 - 5.4.7
this.trustedSet.updateRoot(bytesData);
// Client workflow 5.3.8: persist root metadata file
this.persistMetadata(models_1.MetadataKind.Root, bytesData);
}
catch (error) {
break;
}
}
}
// Load local and remote timestamp metadata.
// Client workflow 5.4: update timestamp role
async loadTimestamp({ checkRemote } = { checkRemote: true }) {
// Load local and remote timestamp metadata
try {
const data = this.loadLocalMetadata(models_1.MetadataKind.Timestamp);
this.trustedSet.updateTimestamp(data);
// If checkRemote is disabled, return here to avoid fetching the remote
// timestamp metadata.
if (!checkRemote) {
return;
}
}
catch (error) {
// continue
}
//Load from remote (whether local load succeeded or not)
const timestampUrl = url.join(this.metadataBaseUrl, 'timestamp.json');
// Client workflow 5.4.1: download timestamp metadata file
const bytesData = await this.fetcher.downloadBytes(timestampUrl, this.config.timestampMaxLength);
try {
// Client workflow 5.4.2 - 5.4.4
this.trustedSet.updateTimestamp(bytesData);
}
catch (error) {
// If new timestamp version is same as current, discardd the new one.
// This is normal and should NOT raise an error.
if (error instanceof error_1.EqualVersionError) {
return;
}
// Re-raise any other error
throw error;
}
// Client workflow 5.4.5: persist timestamp metadata
this.persistMetadata(models_1.MetadataKind.Timestamp, bytesData);
}
// Load local and remote snapshot metadata.
// Client workflow 5.5: update snapshot role
async loadSnapshot() {
//Load local (and if needed remote) snapshot metadata
try {
const data = this.loadLocalMetadata(models_1.MetadataKind.Snapshot);
this.trustedSet.updateSnapshot(data, true);
}
catch (error) {
if (!this.trustedSet.timestamp) {
throw new ReferenceError('No timestamp metadata');
}
const snapshotMeta = this.trustedSet.timestamp.signed.snapshotMeta;
const maxLength = snapshotMeta.length || this.config.snapshotMaxLength;
const version = this.trustedSet.root.signed.consistentSnapshot
? snapshotMeta.version
: undefined;
const snapshotUrl = url.join(this.metadataBaseUrl, version ? `${version}.snapshot.json` : 'snapshot.json');
try {
// Client workflow 5.5.1: download snapshot metadata file
const bytesData = await this.fetcher.downloadBytes(snapshotUrl, maxLength);
// Client workflow 5.5.2 - 5.5.6
this.trustedSet.updateSnapshot(bytesData);
// Client workflow 5.5.7: persist snapshot metadata file
this.persistMetadata(models_1.MetadataKind.Snapshot, bytesData);
}
catch (error) {
throw new error_1.RuntimeError(`Unable to load snapshot metadata error ${error}`);
}
}
}
// Load local and remote targets metadata.
// Client workflow 5.6: update targets role
async loadTargets(role, parentRole) {
if (this.trustedSet.getRole(role)) {
return this.trustedSet.getRole(role);
}
try {
const buffer = this.loadLocalMetadata(role);
this.trustedSet.updateDelegatedTargets(buffer, role, parentRole);
}
catch (error) {
// Local 'role' does not exist or is invalid: update from remote
if (!this.trustedSet.snapshot) {
throw new ReferenceError('No snapshot metadata');
}
const metaInfo = this.trustedSet.snapshot.signed.meta[`${role}.json`];
// TODO: use length for fetching
const maxLength = metaInfo.length || this.config.targetsMaxLength;
const version = this.trustedSet.root.signed.consistentSnapshot
? metaInfo.version
: undefined;
const metadataUrl = url.join(this.metadataBaseUrl, version ? `${version}.${role}.json` : `${role}.json`);
try {
// Client workflow 5.6.1: download targets metadata file
const bytesData = await this.fetcher.downloadBytes(metadataUrl, maxLength);
// Client workflow 5.6.2 - 5.6.6
this.trustedSet.updateDelegatedTargets(bytesData, role, parentRole);
// Client workflow 5.6.7: persist targets metadata file
this.persistMetadata(role, bytesData);
}
catch (error) {
throw new error_1.RuntimeError(`Unable to load targets error ${error}`);
}
}
return this.trustedSet.getRole(role);
}
async preorderDepthFirstWalk(targetPath) {
// Interrogates the tree of target delegations in order of appearance
// (which implicitly order trustworthiness), and returns the matching
// target found in the most trusted role.
// List of delegations to be interrogated. A (role, parent role) pair
// is needed to load and verify the delegated targets metadata.
const delegationsToVisit = [
{
roleName: models_1.MetadataKind.Targets,
parentRoleName: models_1.MetadataKind.Root,
},
];
const visitedRoleNames = new Set();
// Client workflow 5.6.7: preorder depth-first traversal of the graph of
// target delegations
while (visitedRoleNames.size <= this.config.maxDelegations &&
delegationsToVisit.length > 0) {
// Pop the role name from the top of the stack.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const { roleName, parentRoleName } = delegationsToVisit.pop();
// Skip any visited current role to prevent cycles.
// Client workflow 5.6.7.1: skip already-visited roles
if (visitedRoleNames.has(roleName)) {
continue;
}
// The metadata for 'role_name' must be downloaded/updated before
// its targets, delegations, and child roles can be inspected.
const targets = (await this.loadTargets(roleName, parentRoleName))
?.signed;
if (!targets) {
continue;
}
const target = targets.targets?.[targetPath];
if (target) {
return target;
}
// After preorder check, add current role to set of visited roles.
visitedRoleNames.add(roleName);
if (targets.delegations) {
const childRolesToVisit = [];
// NOTE: This may be a slow operation if there are many delegated roles.
const rolesForTarget = targets.delegations.rolesForTarget(targetPath);
for (const { role: childName, terminating } of rolesForTarget) {
childRolesToVisit.push({
roleName: childName,
parentRoleName: roleName,
});
// Client workflow 5.6.7.2.1
if (terminating) {
delegationsToVisit.splice(0); // empty the array
break;
}
}
childRolesToVisit.reverse();
delegationsToVisit.push(...childRolesToVisit);
}
}
return; // no matching target found
}
generateTargetPath(targetInfo) {
if (!this.targetDir) {
throw new error_1.ValueError('Target directory not set');
}
// URL encode target path
const filePath = encodeURIComponent(targetInfo.path);
return path.join(this.targetDir, filePath);
}
persistMetadata(metaDataName, bytesData) {
try {
const filePath = path.join(this.dir, `${metaDataName}.json`);
log('WRITE %s', filePath);
fs.writeFileSync(filePath, bytesData.toString('utf8'));
}
catch (error) {
throw new error_1.PersistError(`Failed to persist metadata ${metaDataName} error: ${error}`);
}
}
}
exports.Updater = Updater;

25
package/node_modules/tuf-js/dist/utils/tmpfile.js generated vendored Normal file
View File

@@ -0,0 +1,25 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.withTempFile = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
// Invokes the given handler with the path to a temporary file. The file
// is deleted after the handler returns.
const withTempFile = async (handler) => withTempDir(async (dir) => handler(path_1.default.join(dir, 'tempfile')));
exports.withTempFile = withTempFile;
// Invokes the given handler with a temporary directory. The directory is
// deleted after the handler returns.
const withTempDir = async (handler) => {
const tmpDir = await promises_1.default.realpath(os_1.default.tmpdir());
const dir = await promises_1.default.mkdtemp(tmpDir + path_1.default.sep);
try {
return await handler(dir);
}
finally {
await promises_1.default.rm(dir, { force: true, recursive: true, maxRetries: 3 });
}
};

14
package/node_modules/tuf-js/dist/utils/url.js generated vendored Normal file
View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.join = void 0;
const url_1 = require("url");
function join(base, path) {
return new url_1.URL(ensureTrailingSlash(base) + removeLeadingSlash(path)).toString();
}
exports.join = join;
function ensureTrailingSlash(path) {
return path.endsWith('/') ? path : path + '/';
}
function removeLeadingSlash(path) {
return path.startsWith('/') ? path.slice(1) : path;
}