Skip to content

Commit

Permalink
raise error when required metadata fields missing (#785)
Browse files Browse the repository at this point in the history
Signed-off-by: Brian DeHamer <bdehamer@github.com>
  • Loading branch information
bdehamer authored Oct 14, 2024
1 parent 7f1aa4b commit 777b805
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/popular-rings-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tufjs/models': patch
---

Raise error when metadata version is missing
49 changes: 42 additions & 7 deletions packages/models/src/__tests__/base.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,39 +12,39 @@ describe('Signed', () => {
describe('constructor', () => {
describe('when called with no arguments', () => {
it('constructs an object', () => {
const subject = new DummySigned({});
const subject = new DummySigned({} as SignedOptions);
expect(subject).toBeTruthy();
});
});

describe('when spec version is too short', () => {
it('constructs an object', () => {
expect(() => {
new DummySigned({ specVersion: '1' });
new DummySigned({ specVersion: '1' } as SignedOptions);
}).toThrow(ValueError);
});
});

describe('when spec version is too long', () => {
it('constructs an object', () => {
expect(() => {
new DummySigned({ specVersion: '1.0.0.0' });
new DummySigned({ specVersion: '1.0.0.0' } as SignedOptions);
}).toThrow(ValueError);
});
});

describe('when spec version includes non number', () => {
it('constructs an object', () => {
expect(() => {
new DummySigned({ specVersion: '1.b.c' });
new DummySigned({ specVersion: '1.b.c' } as SignedOptions);
}).toThrow(ValueError);
});
});

describe('when spec version is unsupported', () => {
it('constructs an object', () => {
expect(() => {
new DummySigned({ specVersion: '2.0.0' });
new DummySigned({ specVersion: '2.0.0' } as SignedOptions);
}).toThrow(ValueError);
});
});
Expand All @@ -53,7 +53,7 @@ describe('Signed', () => {
describe('isExpired', () => {
const subject = new DummySigned({
expires: '2021-12-18T13:28:12.99008-06:00',
});
} as SignedOptions);

describe('when reference time is not provided', () => {
it('returns true', () => {
Expand Down Expand Up @@ -106,7 +106,9 @@ describe('Signed', () => {
});

describe('isExpired', () => {
const subject = new DummySigned({ expires: '1970-01-01T00:00:01.000Z' });
const subject = new DummySigned({
expires: '1970-01-01T00:00:01.000Z',
} as SignedOptions);

describe('when reference time is not provided', () => {
it('returns true', () => {
Expand Down Expand Up @@ -143,6 +145,17 @@ describe('Signed', () => {
});
});

describe('when the version is not included', () => {
it('throws an error', () => {
expect(() => {
DummySigned.commonFieldsFromJSON({
...json,
version: undefined,
} as any); /* eslint-disable-line @typescript-eslint/no-explicit-any */
}).toThrow(ValueError);
});
});

describe('when there is a type error with spec_version', () => {
it('throws an error', () => {
expect(() => {
Expand All @@ -151,6 +164,17 @@ describe('Signed', () => {
});
});

describe('when the spec_version is not included', () => {
it('throws an error', () => {
expect(() => {
DummySigned.commonFieldsFromJSON({
...json,
spec_version: undefined,
} as any); /* eslint-disable-line @typescript-eslint/no-explicit-any */
}).toThrow(ValueError);
});
});

describe('when there is a type error with expires', () => {
it('throws an error', () => {
expect(() => {
Expand All @@ -159,6 +183,17 @@ describe('Signed', () => {
});
});

describe('when the expires is not included', () => {
it('throws an error', () => {
expect(() => {
DummySigned.commonFieldsFromJSON({
...json,
expires: undefined,
} as any); /* eslint-disable-line @typescript-eslint/no-explicit-any */
}).toThrow(ValueError);
});
});

describe('when the JSON is valid', () => {
it('throws an error', () => {
const opts = DummySigned.commonFieldsFromJSON(json);
Expand Down
6 changes: 3 additions & 3 deletions packages/models/src/__tests__/key.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Signed } from '../base';
import { Signed, SignedOptions } from '../base';
import { UnsignedMetadataError } from '../error';
import { Key } from '../key';
import { Signature } from '../signature';
Expand Down Expand Up @@ -42,7 +42,7 @@ describe('Key', () => {
}
}
const metadata = {
signed: new DummySigned({}),
signed: new DummySigned({} as SignedOptions),
signatures: { [keyID]: signature },
};

Expand All @@ -63,7 +63,7 @@ describe('Key', () => {

describe('when no signature does NOT match', () => {
const badMetadata = {
signed: new DummySigned({}),
signed: new DummySigned({} as SignedOptions),
signatures: { [keyID]: new Signature({ keyID: keyID, sig: 'bad' }) },
};

Expand Down
5 changes: 4 additions & 1 deletion packages/models/src/__tests__/metadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ describe('Metadata', () => {
sig: 'BEEF',
});

const subject = new Metadata(new Root({}), { [sig1.keyID]: sig1 });
const subject = new Metadata(
new Root({ version: 1, specVersion: '1.0.0', expires: '' }),
{ [sig1.keyID]: sig1 }
);

describe('when appending a signature', () => {
const append = true;
Expand Down
2 changes: 1 addition & 1 deletion packages/models/src/__tests__/root.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ describe('Root', () => {
scheme: 'ed25519',
});

const root = new Root({});
const root = new Root({ version: 1, specVersion: '1.0.0', expires: '' });

describe('when called with a valid role', () => {
it('adds the key', () => {
Expand Down
6 changes: 5 additions & 1 deletion packages/models/src/__tests__/targets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ describe('Targets', () => {
hashes: { sha256: 'abc' },
});

const targets = new Targets({});
const targets = new Targets({
version: 1,
specVersion: '1.0.0',
expires: '',
});

it('adds a target', () => {
targets.addTarget(targetFile);
Expand Down
22 changes: 14 additions & 8 deletions packages/models/src/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ export interface Signable {
}

export interface SignedOptions {
version?: number;
specVersion?: string;
expires?: string;
version: number;
specVersion: string;
expires: string;
unrecognizedFields?: Record<string, JSONValue>;
}

Expand Down Expand Up @@ -60,8 +60,8 @@ export abstract class Signed {
throw new ValueError('Unsupported specVersion');
}

this.expires = options.expires || new Date().toISOString();
this.version = options.version || 1;
this.expires = options.expires;
this.version = options.version;
this.unrecognizedFields = options.unrecognizedFields || {};
}

Expand All @@ -88,15 +88,21 @@ export abstract class Signed {
public static commonFieldsFromJSON(data: JSONObject): SignedOptions {
const { spec_version, expires, version, ...rest } = data;

if (guard.isDefined(spec_version) && !(typeof spec_version === 'string')) {
if (!guard.isDefined(spec_version)) {
throw new ValueError('spec_version is not defined');
} else if (typeof spec_version !== 'string') {
throw new TypeError('spec_version must be a string');
}

if (guard.isDefined(expires) && !(typeof expires === 'string')) {
if (!guard.isDefined(expires)) {
throw new ValueError('expires is not defined');
} else if (!(typeof expires === 'string')) {
throw new TypeError('expires must be a string');
}

if (guard.isDefined(version) && !(typeof version === 'number')) {
if (!guard.isDefined(version)) {
throw new ValueError('version is not defined');
} else if (!(typeof version === 'number')) {
throw new TypeError('version must be a number');
}

Expand Down
8 changes: 8 additions & 0 deletions packages/repo-mock/src/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export function createTargetsMeta(
): Metadata<Targets> {
const targets = new Metadata(
new Targets({
version: 1,
specVersion: '1.0.0',
expires: getExpires(),
})
);
Expand All @@ -33,6 +35,8 @@ export function createSnapshotMeta(
): Metadata<Snapshot> {
const snapshot = new Metadata(
new Snapshot({
version: 1,
specVersion: '1.0.0',
expires: getExpires(),
meta: { 'targets.json': toMetaFile(targets) },
})
Expand All @@ -48,6 +52,8 @@ export function createTimestampMeta(
): Metadata<Timestamp> {
const timestamp = new Metadata(
new Timestamp({
version: 1,
specVersion: '1.0.0',
expires: getExpires(),
snapshotMeta: toMetaFile(snapshot),
})
Expand All @@ -61,6 +67,8 @@ export function createTimestampMeta(
export function createRootMeta(keyPair: KeyPair): Metadata<Root> {
const root = new Metadata(
new Root({
version: 1,
specVersion: '1.0.0',
expires: getExpires(),
consistentSnapshot: false,
})
Expand Down

0 comments on commit 777b805

Please sign in to comment.