Deployed the page to Github Pages.
This commit is contained in:
parent
1d79754e93
commit
2c89899458
62797 changed files with 6551425 additions and 15279 deletions
21
node_modules/tuf-js/LICENSE
generated
vendored
Normal file
21
node_modules/tuf-js/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 GitHub and the TUF Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
3
node_modules/tuf-js/README.md
generated
vendored
Normal file
3
node_modules/tuf-js/README.md
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
# tuf-js
|
||||
|
||||
JavaScript TUF client implementation.
|
14
node_modules/tuf-js/dist/config.d.ts
generated
vendored
Normal file
14
node_modules/tuf-js/dist/config.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
import type { MakeFetchHappenOptions } from 'make-fetch-happen';
|
||||
export type Config = {
|
||||
maxRootRotations: number;
|
||||
maxDelegations: number;
|
||||
rootMaxLength: number;
|
||||
timestampMaxLength: number;
|
||||
snapshotMaxLength: number;
|
||||
targetsMaxLength: number;
|
||||
prefixTargetsWithHash: boolean;
|
||||
fetchTimeout: number;
|
||||
fetchRetries: number | undefined;
|
||||
fetchRetry: MakeFetchHappenOptions['retry'];
|
||||
};
|
||||
export declare const defaultConfig: Config;
|
15
node_modules/tuf-js/dist/config.js
generated
vendored
Normal file
15
node_modules/tuf-js/dist/config.js
generated
vendored
Normal 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,
|
||||
};
|
22
node_modules/tuf-js/dist/error.d.ts
generated
vendored
Normal file
22
node_modules/tuf-js/dist/error.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
export declare class ValueError extends Error {
|
||||
}
|
||||
export declare class RuntimeError extends Error {
|
||||
}
|
||||
export declare class PersistError extends Error {
|
||||
}
|
||||
export declare class RepositoryError extends Error {
|
||||
}
|
||||
export declare class BadVersionError extends RepositoryError {
|
||||
}
|
||||
export declare class EqualVersionError extends BadVersionError {
|
||||
}
|
||||
export declare class ExpiredMetadataError extends RepositoryError {
|
||||
}
|
||||
export declare class DownloadError extends Error {
|
||||
}
|
||||
export declare class DownloadLengthMismatchError extends DownloadError {
|
||||
}
|
||||
export declare class DownloadHTTPError extends DownloadError {
|
||||
statusCode: number;
|
||||
constructor(message: string, statusCode: number);
|
||||
}
|
48
node_modules/tuf-js/dist/error.js
generated
vendored
Normal file
48
node_modules/tuf-js/dist/error.js
generated
vendored
Normal 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;
|
25
node_modules/tuf-js/dist/fetcher.d.ts
generated
vendored
Normal file
25
node_modules/tuf-js/dist/fetcher.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import type { MakeFetchHappenOptions } from 'make-fetch-happen';
|
||||
type DownloadFileHandler<T> = (file: string) => Promise<T>;
|
||||
export interface Fetcher {
|
||||
downloadFile<T>(url: string, maxLength: number, handler: DownloadFileHandler<T>): Promise<T>;
|
||||
downloadBytes(url: string, maxLength: number): Promise<Buffer>;
|
||||
}
|
||||
export declare abstract class BaseFetcher implements Fetcher {
|
||||
abstract fetch(url: string): Promise<NodeJS.ReadableStream>;
|
||||
downloadFile<T>(url: string, maxLength: number, handler: DownloadFileHandler<T>): Promise<T>;
|
||||
downloadBytes(url: string, maxLength: number): Promise<Buffer>;
|
||||
}
|
||||
type Retry = MakeFetchHappenOptions['retry'];
|
||||
interface FetcherOptions {
|
||||
timeout?: number;
|
||||
retry?: Retry;
|
||||
}
|
||||
export declare class DefaultFetcher extends BaseFetcher {
|
||||
private timeout?;
|
||||
private retry?;
|
||||
constructor(options?: FetcherOptions);
|
||||
fetch(url: string): Promise<NodeJS.ReadableStream>;
|
||||
}
|
||||
export {};
|
84
node_modules/tuf-js/dist/fetcher.js
generated
vendored
Normal file
84
node_modules/tuf-js/dist/fetcher.js
generated
vendored
Normal 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);
|
||||
});
|
||||
});
|
||||
};
|
4
node_modules/tuf-js/dist/index.d.ts
generated
vendored
Normal file
4
node_modules/tuf-js/dist/index.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
export { TargetFile } from '@tufjs/models';
|
||||
export { BaseFetcher, Fetcher } from './fetcher';
|
||||
export { Updater, UpdaterOptions } from './updater';
|
||||
export type { Config } from './config';
|
9
node_modules/tuf-js/dist/index.js
generated
vendored
Normal file
9
node_modules/tuf-js/dist/index.js
generated
vendored
Normal 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; } });
|
19
node_modules/tuf-js/dist/store.d.ts
generated
vendored
Normal file
19
node_modules/tuf-js/dist/store.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
/// <reference types="node" />
|
||||
import { Metadata, Root, Snapshot, Targets, Timestamp } from '@tufjs/models';
|
||||
export declare class TrustedMetadataStore {
|
||||
private trustedSet;
|
||||
private referenceTime;
|
||||
constructor(rootData: Buffer);
|
||||
get root(): Metadata<Root>;
|
||||
get timestamp(): Metadata<Timestamp> | undefined;
|
||||
get snapshot(): Metadata<Snapshot> | undefined;
|
||||
get targets(): Metadata<Targets> | undefined;
|
||||
getRole(name: string): Metadata<Targets> | undefined;
|
||||
updateRoot(bytesBuffer: Buffer): Metadata<Root>;
|
||||
updateTimestamp(bytesBuffer: Buffer): Metadata<Timestamp>;
|
||||
updateSnapshot(bytesBuffer: Buffer, trusted?: boolean): Metadata<Snapshot>;
|
||||
updateDelegatedTargets(bytesBuffer: Buffer, roleName: string, delegatorName: string): void;
|
||||
private loadTrustedRoot;
|
||||
private checkFinalTimestamp;
|
||||
private checkFinalSnapsnot;
|
||||
}
|
208
node_modules/tuf-js/dist/store.js
generated
vendored
Normal file
208
node_modules/tuf-js/dist/store.js
generated
vendored
Normal 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 role’s 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 role’s 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;
|
35
node_modules/tuf-js/dist/updater.d.ts
generated
vendored
Normal file
35
node_modules/tuf-js/dist/updater.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { TargetFile } from '@tufjs/models';
|
||||
import { Config } from './config';
|
||||
import { Fetcher } from './fetcher';
|
||||
export interface UpdaterOptions {
|
||||
metadataDir: string;
|
||||
metadataBaseUrl: string;
|
||||
targetDir?: string;
|
||||
targetBaseUrl?: string;
|
||||
fetcher?: Fetcher;
|
||||
forceCache?: boolean;
|
||||
config?: Partial<Config>;
|
||||
}
|
||||
export declare class Updater {
|
||||
private dir;
|
||||
private metadataBaseUrl;
|
||||
private targetDir?;
|
||||
private targetBaseUrl?;
|
||||
private forceCache;
|
||||
private trustedSet;
|
||||
private config;
|
||||
private fetcher;
|
||||
constructor(options: UpdaterOptions);
|
||||
refresh(): Promise<void>;
|
||||
getTargetInfo(targetPath: string): Promise<TargetFile | undefined>;
|
||||
downloadTarget(targetInfo: TargetFile, filePath?: string, targetBaseUrl?: string): Promise<string>;
|
||||
findCachedTarget(targetInfo: TargetFile, filePath?: string): Promise<string | undefined>;
|
||||
private loadLocalMetadata;
|
||||
private loadRoot;
|
||||
private loadTimestamp;
|
||||
private loadSnapshot;
|
||||
private loadTargets;
|
||||
private preorderDepthFirstWalk;
|
||||
private generateTargetPath;
|
||||
private persistMetadata;
|
||||
}
|
343
node_modules/tuf-js/dist/updater.js
generated
vendored
Normal file
343
node_modules/tuf-js/dist/updater.js
generated
vendored
Normal 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;
|
3
node_modules/tuf-js/dist/utils/tmpfile.d.ts
generated
vendored
Normal file
3
node_modules/tuf-js/dist/utils/tmpfile.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
type TempFileHandler<T> = (file: string) => Promise<T>;
|
||||
export declare const withTempFile: <T>(handler: TempFileHandler<T>) => Promise<T>;
|
||||
export {};
|
25
node_modules/tuf-js/dist/utils/tmpfile.js
generated
vendored
Normal file
25
node_modules/tuf-js/dist/utils/tmpfile.js
generated
vendored
Normal 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 });
|
||||
}
|
||||
};
|
1
node_modules/tuf-js/dist/utils/url.d.ts
generated
vendored
Normal file
1
node_modules/tuf-js/dist/utils/url.d.ts
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
export declare function join(base: string, path: string): string;
|
14
node_modules/tuf-js/dist/utils/url.js
generated
vendored
Normal file
14
node_modules/tuf-js/dist/utils/url.js
generated
vendored
Normal 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;
|
||||
}
|
43
node_modules/tuf-js/package.json
generated
vendored
Normal file
43
node_modules/tuf-js/package.json
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "tuf-js",
|
||||
"version": "2.2.1",
|
||||
"description": "JavaScript implementation of The Update Framework (TUF)",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc --build",
|
||||
"clean": "rm -rf dist",
|
||||
"test": "jest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/theupdateframework/tuf-js.git"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"keywords": [
|
||||
"tuf",
|
||||
"security",
|
||||
"update"
|
||||
],
|
||||
"author": "bdehamer@github.com",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/theupdateframework/tuf-js/issues"
|
||||
},
|
||||
"homepage": "https://github.com/theupdateframework/tuf-js/tree/main/packages/client#readme",
|
||||
"devDependencies": {
|
||||
"@tufjs/repo-mock": "2.0.1",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/make-fetch-happen": "^10.0.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tufjs/models": "2.0.1",
|
||||
"debug": "^4.3.4",
|
||||
"make-fetch-happen": "^13.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.14.0 || >=18.0.0"
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue