Compare commits

...

22 commits
v0.1.0 ... main

Author SHA1 Message Date
renovate[bot]
345017c52d
Update dependency @types/node to v22.13.17 ()
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-01 21:57:00 +00:00
renovate[bot]
529d6a3483
Update actions/setup-node digest to cdca736 ()
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-01 05:48:04 +00:00
renovate[bot]
09960e348d
Update dependency tsx to v4.19.3 ()
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 10:34:19 +00:00
renovate[bot]
2e9d3bc281
Update actions/setup-node digest to 1d0ff46 ()
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-01 06:05:23 +00:00
kou029w
7e43c94bfb Release 1.0.1 2025-02-19 08:21:04 +00:00
9a2e7ad45c
Update tests to assert expected output structure for IntegrityMetadataSet 2025-02-19 17:19:45 +09:00
d0415bbd04
Fixed "Maximum call stack size exceeded" error when using IntegrityMetadataSet#strongest 2025-02-19 17:14:01 +09:00
kou029w
22450619c4 Release 1.0.0 2025-01-06 04:47:32 +00:00
83860ef7f8
update changelog 2025-01-06 13:43:21 +09:00
93af3bce3c
change strongest property type from Array<IntegrityMetadata> to IntegrityMetadataSet 2025-01-06 13:41:24 +09:00
3d1a4acd2f
update changelog 2025-01-06 11:38:20 +09:00
52ff1f7e9a
add documentation for IntegrityMetadata class properties 2025-01-06 11:32:52 +09:00
renovate[bot]
c8e7319396
Update dependency @types/node to v22.10.3 ()
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-01 07:35:29 +00:00
renovate[bot]
5310a04b87
Update dependency pkgroll to v2.6.0 ()
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-01 04:06:42 +00:00
renovate[bot]
7d53588259
Update dependency release-it to v17.10.0 ()
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-01 07:01:59 +00:00
renovate[bot]
33044b912a
Update dependency @types/node to v22.10.1 ()
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-01 04:23:10 +00:00
f4c800793b
update examples 2024-11-06 11:31:38 +09:00
renovate[bot]
f2854638df Update dependency tsx to v4.19.2 2024-11-01 09:01:58 +00:00
renovate[bot]
17fce57d8b Update dependency pkgroll to v2.5.1 2024-11-01 06:44:31 +00:00
renovate[bot]
5f6ceea225 Update actions/setup-node digest to 39370e3 2024-11-01 04:32:09 +00:00
renovate[bot]
5e369ade90 Update actions/checkout digest to 11bd719 2024-11-01 02:40:51 +00:00
8f7b6bd105
specify code block syntax 2024-10-03 16:13:52 +09:00
8 changed files with 1285 additions and 1316 deletions

View file

@ -16,10 +16,10 @@ jobs:
contents: write contents: write
packages: write packages: write
steps: steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4 - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
with: with:
node-version: lts/* node-version: lts/*
cache: npm cache: npm

View file

@ -11,8 +11,8 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4 - uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: npm cache: npm
@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: denoland/setup-deno@v1 - uses: denoland/setup-deno@v1
with: with:
deno-version: latest deno-version: latest
@ -34,7 +34,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 15 timeout-minutes: 15
steps: steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2 - uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2
with: with:
bun-version: latest bun-version: latest

View file

@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
## [1.0.1] - 2025-02-19
- Fixed "Maximum call stack size exceeded" error when using `IntegrityMetadataSet#strongest`
## [1.0.0] - 2025-01-06
- Change `strongest` property type from `Array<IntegrityMetadata>` to `IntegrityMetadataSet`
- Improve documentation
## [0.1.0] - 2024-10-02 ## [0.1.0] - 2024-10-02
- **IntegrityMetadataSet Enhancements**: Refactored `IntegrityMetadataSet` to improve structure and flexibility, including new methods (`match`, `strongestHashAlgorithms`, `iterator`, and `size`), support for more flexible input types, and enhanced validation logic. - **IntegrityMetadataSet Enhancements**: Refactored `IntegrityMetadataSet` to improve structure and flexibility, including new methods (`match`, `strongestHashAlgorithms`, `iterator`, and `size`), support for more flexible input types, and enhanced validation logic.
@ -25,8 +34,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- First release - First release
[1.0.1]: https://github.com/kou029w/websri/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/kou029w/websri/compare/v0.1.0...v1.0.0
[0.1.0]: https://github.com/kou029w/websri/compare/v0.0.3...v0.1.0 [0.1.0]: https://github.com/kou029w/websri/compare/v0.0.3...v0.1.0
[0.0.3]: https://github.com/kou029w/websri/compare/v0.0.2...v0.0.3 [0.0.3]: https://github.com/kou029w/websri/compare/v0.0.2...v0.0.3
[0.0.2]: https://github.com/kou029w/websri/compare/v0.0.1...v0.0.2 [0.0.2]: https://github.com/kou029w/websri/compare/v0.0.1...v0.0.2
[0.0.1]: https://github.com/kou029w/usri/releases/tag/v0.0.1 [0.0.1]: https://github.com/kou029w/usri/releases/tag/v0.0.1
[unreleased]: https://github.com/kou029w/websri/compare/v0.1.0...HEAD [unreleased]: https://github.com/kou029w/websri/compare/v1.0.1...HEAD

2242
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{ {
"name": "websri", "name": "websri",
"version": "0.1.0", "version": "1.0.1",
"description": "A universal Subresource Integrity (SRI) utility for Node.js, browsers, Cloudflare Workers, Deno, Bun, and other web-compatible runtimes.", "description": "A universal Subresource Integrity (SRI) utility for Node.js, browsers, Cloudflare Workers, Deno, Bun, and other web-compatible runtimes.",
"license": "MIT", "license": "MIT",
"author": "Kohei Watanabe <nebel@fogtype.com>", "author": "Kohei Watanabe <nebel@fogtype.com>",
@ -45,10 +45,10 @@
}, },
"devDependencies": { "devDependencies": {
"@release-it/keep-a-changelog": "5.0.0", "@release-it/keep-a-changelog": "5.0.0",
"@types/node": "22.7.4", "@types/node": "22.13.17",
"pkgroll": "2.5.0", "pkgroll": "2.6.0",
"release-it": "17.6.0", "release-it": "17.10.0",
"tsx": "4.19.1", "tsx": "4.19.3",
"typescript": "5.6.2" "typescript": "5.6.2"
} }
} }

View file

@ -90,21 +90,24 @@ export type IntegrityMetadataLike = {
* Class representing integrity metadata, consisting of a hash algorithm and hash value. * Class representing integrity metadata, consisting of a hash algorithm and hash value.
*/ */
export class IntegrityMetadata implements IntegrityMetadataLike { export class IntegrityMetadata implements IntegrityMetadataLike {
/** Hash algorithm */
alg: PrioritizedHashAlgorithm; alg: PrioritizedHashAlgorithm;
/** The base64-encoded hash value of the resource */
val: string; val: string;
/** Optional additional attributes */
opt: string[]; opt: string[];
/** /**
* Creates an instance of `IntegrityMetadata` from a given object or string. * Creates an instance of `IntegrityMetadata` from a given object or string.
* @param integrity The integrity metadata input, which can be a string or object. * @param integrity The integrity metadata input, which can be a string or object.
* @example * @example
* ``` * ```js
* new IntegrityMetadata("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=") * new IntegrityMetadata("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
* ``` * ```
* *
* or * or
* *
* ``` * ```js
* new IntegrityMetadata({ * new IntegrityMetadata({
* alg: "sha256", * alg: "sha256",
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", * val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
@ -135,17 +138,17 @@ export class IntegrityMetadata implements IntegrityMetadataLike {
* @param integrity The integrity metadata to compare with. * @param integrity The integrity metadata to compare with.
* @returns `true` if the integrity metadata matches, `false` otherwise. * @returns `true` if the integrity metadata matches, `false` otherwise.
* @example * @example
* ``` * ```js
* integrityMetadata.match({ * integrityMetadata.match("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
* alg: "sha256",
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
* })
* ``` * ```
* *
* or * or
* *
* ``` * ```js
* integrityMetadata.match("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=") * integrityMetadata.match({
* alg: "sha256",
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
* })
* ``` * ```
*/ */
match(integrity: IntegrityMetadataLike | string | null | undefined): boolean { match(integrity: IntegrityMetadataLike | string | null | undefined): boolean {
@ -177,7 +180,7 @@ export class IntegrityMetadata implements IntegrityMetadataLike {
* @param integrity The integrity metadata object to stringify. * @param integrity The integrity metadata object to stringify.
* @returns The stringified integrity metadata. * @returns The stringified integrity metadata.
* @example * @example
* ``` * ```js
* IntegrityMetadata.stringify({ * IntegrityMetadata.stringify({
* alg: "sha256", * alg: "sha256",
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", * val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
@ -199,7 +202,7 @@ export class IntegrityMetadata implements IntegrityMetadataLike {
* @param opt Optional additional attributes. * @param opt Optional additional attributes.
* @returns A promise that resolves to an `IntegrityMetadata` object. * @returns A promise that resolves to an `IntegrityMetadata` object.
* @example * @example
* ``` * ```js
* const res = new Response("Hello, world!"); * const res = new Response("Hello, world!");
* const data = await res.arrayBuffer(); * const data = await res.arrayBuffer();
* const integrityMetadata = await createIntegrityMetadata("sha256", data); * const integrityMetadata = await createIntegrityMetadata("sha256", data);
@ -238,12 +241,7 @@ export type IntegrityMetadataSetOptions = {
*/ */
export class IntegrityMetadataSet { export class IntegrityMetadataSet {
#set: ReadonlyArray<IntegrityMetadata>; #set: ReadonlyArray<IntegrityMetadata>;
#getPrioritizedHashAlgorithm = getPrioritizedHashAlgorithm;
/**
* The strongest (most secure) integrity metadata from the set.
* @see {@link https://www.w3.org/TR/SRI/#get-the-strongest-metadata-from-set}
*/
readonly strongest: Array<IntegrityMetadata> = [];
/** /**
* Create an instance of `IntegrityMetadataSet` from integrity metadata or an array of integrity * Create an instance of `IntegrityMetadataSet` from integrity metadata or an array of integrity
@ -251,23 +249,23 @@ export class IntegrityMetadataSet {
* @param integrity The integrity metadata or an array of integrity metadata. * @param integrity The integrity metadata or an array of integrity metadata.
* @param options Optional configuration options for hash algorithm prioritization. * @param options Optional configuration options for hash algorithm prioritization.
* @example * @example
* ``` * ```js
* new IntegrityMetadataSet(`
* sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=
* sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r
* sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==
* `)
* ```
*
* or
*
* ```
* new IntegrityMetadataSet([ * new IntegrityMetadataSet([
* "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", * "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
* "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", * "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
* "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", * "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
* ]) * ])
* ``` * ```
*
* or
*
* ```js
* new IntegrityMetadataSet(`
* sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=
* sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r
* sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==
* `)
* ```
*/ */
constructor( constructor(
integrity: integrity:
@ -281,53 +279,36 @@ export class IntegrityMetadataSet {
_getPrioritizedHashAlgorithm = getPrioritizedHashAlgorithm, _getPrioritizedHashAlgorithm = getPrioritizedHashAlgorithm,
}: IntegrityMetadataSetOptions = {}, }: IntegrityMetadataSetOptions = {},
) { ) {
const set: ReadonlyArray< this.#set = [integrity]
IntegrityMetadataLike | string | null | undefined
> = [integrity]
.flat() .flat()
.flatMap( .flatMap(
( (
integrity: IntegrityMetadataLike | string | null | undefined, integrity: IntegrityMetadataLike | string | null | undefined,
): ReadonlyArray<IntegrityMetadataLike | string | null | undefined> => ): ReadonlyArray<IntegrityMetadataLike | string | null | undefined> => {
typeof integrity === "string" if (typeof integrity === "string") {
? integrity.split(SeparatorRegex) return integrity.split(SeparatorRegex);
: [integrity], }
);
this.#set = set return [integrity];
},
)
.map((integrity) => new IntegrityMetadata(integrity)) .map((integrity) => new IntegrityMetadata(integrity))
.filter((integrityMetadata) => integrityMetadata.toString() !== ""); .filter((integrityMetadata) => integrityMetadata.toString() !== "");
for (const integrityMetadata of this.#set) { this.#getPrioritizedHashAlgorithm = _getPrioritizedHashAlgorithm;
const [strongest = new IntegrityMetadata("")] = this.strongest;
const prioritizedHashAlgorithm = _getPrioritizedHashAlgorithm(
strongest.alg as HashAlgorithm,
integrityMetadata.alg as HashAlgorithm,
);
switch (prioritizedHashAlgorithm) {
case "":
this.strongest.push(integrityMetadata);
break;
case integrityMetadata.alg:
this.strongest = [integrityMetadata];
break;
}
}
} }
/** /**
* Enables iteration over the set of integrity metadata. * Enables iteration over the set of integrity metadata.
* @returns A generator that yields each IntegrityMetadata object. * @returns A generator that yields each IntegrityMetadata object.
* @example * @example
* ``` * ```js
* [...integrityMetadataSet] * [...integrityMetadataSet]
* ``` * ```
*/ */
*[Symbol.iterator](): Generator<IntegrityMetadata> { *[Symbol.iterator](): Generator<IntegrityMetadata> {
for (const integrityMetadata of this.#set) { for (const integrityMetadata of this.#set) {
yield integrityMetadata; yield new IntegrityMetadata(integrityMetadata);
} }
} }
@ -338,11 +319,44 @@ export class IntegrityMetadataSet {
return this.#set.length; return this.#set.length;
} }
/**
* The strongest (most secure) integrity metadata from the set.
* @see {@link https://www.w3.org/TR/SRI/#get-the-strongest-metadata-from-set}
*/
get strongest(): IntegrityMetadataSet {
let strongest = new IntegrityMetadataSet([]);
for (const integrityMetadata of this.#set) {
const [{ alg } = new IntegrityMetadata("")] = strongest;
const prioritizedHashAlgorithm = this.#getPrioritizedHashAlgorithm(
alg as HashAlgorithm,
integrityMetadata.alg as HashAlgorithm,
);
switch (prioritizedHashAlgorithm) {
case "":
strongest = new IntegrityMetadataSet([
...strongest,
integrityMetadata,
]);
break;
case integrityMetadata.alg:
strongest = new IntegrityMetadataSet(integrityMetadata);
break;
case alg:
break;
}
}
return strongest;
}
/** /**
* Returns an array of the strongest supported hash algorithms in the set. * Returns an array of the strongest supported hash algorithms in the set.
*/ */
get strongestHashAlgorithms(): ReadonlyArray<HashAlgorithm> { get strongestHashAlgorithms(): ReadonlyArray<HashAlgorithm> {
const strongestHashAlgorithms = this.strongest const strongestHashAlgorithms = [...this.strongest]
.map(({ alg }) => alg as HashAlgorithm) .map(({ alg }) => alg as HashAlgorithm)
.filter(Boolean); .filter(Boolean);
@ -354,17 +368,17 @@ export class IntegrityMetadataSet {
* @param integrity The integrity metadata to match. * @param integrity The integrity metadata to match.
* @returns `true` if the integrity metadata matches, `false` otherwise. * @returns `true` if the integrity metadata matches, `false` otherwise.
* @example * @example
* ``` * ```js
* integrityMetadataSet.match({ * integrityMetadataSet.match("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
* alg: "sha256",
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
* })
* ``` * ```
* *
* or * or
* *
* ``` * ```js
* integrityMetadataSet.match("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=") * integrityMetadataSet.match({
* alg: "sha256",
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
* })
* ``` * ```
*/ */
match(integrity: IntegrityMetadataLike | string | null | undefined): boolean { match(integrity: IntegrityMetadataLike | string | null | undefined): boolean {
@ -407,7 +421,7 @@ export class IntegrityMetadataSet {
* @param options Optional configuration options for the metadata set. * @param options Optional configuration options for the metadata set.
* @returns A promise that resolves to an `IntegrityMetadataSet` object. * @returns A promise that resolves to an `IntegrityMetadataSet` object.
* @example * @example
* ``` * ```js
* const res = new Response("Hello, world!"); * const res = new Response("Hello, world!");
* const data = await res.arrayBuffer(); * const data = await res.arrayBuffer();
* const set = await createIntegrityMetadataSet(["sha256", "sha384", "sha512"], data); * const set = await createIntegrityMetadataSet(["sha256", "sha384", "sha512"], data);

View file

@ -7,11 +7,16 @@ test("supports SHA-256", function () {
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
); );
assert.deepEqual(set.strongest[0], { assert.deepEqual(
alg: "sha256", [...set],
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", [
opt: [], {
}); alg: "sha256",
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
opt: [],
},
],
);
}); });
test("supports SHA-384", function () { test("supports SHA-384", function () {
@ -19,11 +24,16 @@ test("supports SHA-384", function () {
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
); );
assert.deepEqual(set.strongest[0], { assert.deepEqual(
alg: "sha384", [...set],
val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", [
opt: [], {
}); alg: "sha384",
val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
opt: [],
},
],
);
}); });
test("supports SHA-512", function () { test("supports SHA-512", function () {
@ -31,11 +41,16 @@ test("supports SHA-512", function () {
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
); );
assert.deepEqual(set.strongest[0], { assert.deepEqual(
alg: "sha512", [...set],
val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", [
opt: [], {
}); alg: "sha512",
val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
opt: [],
},
],
);
}); });
test("accepts options", function () { test("accepts options", function () {
@ -43,11 +58,16 @@ test("accepts options", function () {
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=?foo?bar", "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=?foo?bar",
); );
assert.deepEqual(set.strongest[0], { assert.deepEqual(
alg: "sha256", [...set],
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", [
opt: ["foo", "bar"], {
}); alg: "sha256",
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
opt: ["foo", "bar"],
},
],
);
}); });
test("accepts an IntegrityMetadata like object as input", function () { test("accepts an IntegrityMetadata like object as input", function () {
@ -57,10 +77,14 @@ test("accepts an IntegrityMetadata like object as input", function () {
}); });
assert.deepEqual( assert.deepEqual(
set, [...set],
new IntegrityMetadataSet( [
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", {
), alg: "sha256",
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
opt: [],
},
],
); );
}); });
@ -77,12 +101,14 @@ sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLs
]); ]);
assert.deepEqual( assert.deepEqual(
set, [...set],
new IntegrityMetadataSet([ [
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", ...new IntegrityMetadataSet([
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
]), "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
]),
],
); );
}); });
@ -93,11 +119,13 @@ test("multiple overlapping algorithms can be accepted", function () {
]); ]);
assert.deepEqual( assert.deepEqual(
set, [...set],
new IntegrityMetadataSet([ [
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", ...new IntegrityMetadataSet([
"sha256-uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=", "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
]), "sha256-uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=",
]),
],
); );
}); });
@ -107,10 +135,14 @@ test("trims leading and trailing whitespace", function () {
); );
assert.deepEqual( assert.deepEqual(
set, [...set],
new IntegrityMetadataSet( [
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", {
), alg: "sha256",
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
opt: [],
},
],
); );
}); });
@ -120,37 +152,39 @@ test("whitespace can be analyzed as entry separator", function () {
); );
assert.deepEqual( assert.deepEqual(
set, [...set],
new IntegrityMetadataSet([ [
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", ...new IntegrityMetadataSet([
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
]), "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
]),
],
); );
}); });
test("discards unsupported hash algorithm", function () { test("discards unsupported hash algorithm", function () {
const set = new IntegrityMetadataSet("sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk="); const set = new IntegrityMetadataSet("sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk=");
assert.deepEqual(set, new IntegrityMetadataSet([])); assert.deepEqual([...set], []);
}); });
test("discards null input", function () { test("discards null input", function () {
const set = new IntegrityMetadataSet(null); const set = new IntegrityMetadataSet(null);
assert.deepEqual(set, new IntegrityMetadataSet([])); assert.deepEqual([...set], []);
}); });
test("discards empty string input", function () { test("discards empty string input", function () {
const set = new IntegrityMetadataSet([]); const set = new IntegrityMetadataSet([]);
assert.deepEqual(set, new IntegrityMetadataSet([])); assert.deepEqual([...set], []);
}); });
test("discards invalid value", function () { test("discards invalid value", function () {
const set = new IntegrityMetadataSet("md5\0/..invalid-value"); const set = new IntegrityMetadataSet("md5\0/..invalid-value");
assert.deepEqual(set, new IntegrityMetadataSet([])); assert.deepEqual([...set], []);
}); });
test("discards invalid values in a list of multiple inputs", function () { test("discards invalid values in a list of multiple inputs", function () {
@ -158,5 +192,5 @@ test("discards invalid values in a list of multiple inputs", function () {
"sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk= md5\0/..invalid-value", "sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk= md5\0/..invalid-value",
); );
assert.deepEqual(set, new IntegrityMetadataSet([])); assert.deepEqual([...set], []);
}); });

View file

@ -1,19 +1,30 @@
import assert from "node:assert"; import assert from "node:assert";
import { test } from "node:test"; import { test } from "node:test";
import { IntegrityMetadata, IntegrityMetadataSet } from "../../src/index.ts"; import { IntegrityMetadataSet } from "../../src/index.ts";
test("pick the strongest metadata from set", function () { test("pick the strongest metadata from set", function () {
const integrityMetadataSet = new IntegrityMetadataSet(` const integrityMetadataSet = new IntegrityMetadataSet(`
sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM= sha256-gxZXfeA3KCK+ZyBybEt6liVPg+FWGf/KLVU6rufBujE=
sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r sha384-LDW1hUX1OX+VZsNmW+LELiky69a4xF+FfVsTlqZOhqPiPj5YYo20jP6C8H8uXMZf
sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ== sha512-aqnrVqlE3w/CWs51jb3FHCsFSBwfpecdXHaFFYZNkxfW2Z1qyJm4mA9iCPK11KeWwEa8rbMDq7l6IrnevQuOQw==
sha512-P8q/bH6NoZs5MnZKL9D/r/oEZOlyEAmSfuXuJchD2WeXnKbnfcO3fF0WvO6CiqZUGWsEREs9BWLrv1xr3NPOLg==
`); `);
assert.deepEqual(integrityMetadataSet.strongest, [ assert.deepEqual(
new IntegrityMetadata( [...integrityMetadataSet.strongest],
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", [
), {
]); alg: "sha512",
val: "aqnrVqlE3w/CWs51jb3FHCsFSBwfpecdXHaFFYZNkxfW2Z1qyJm4mA9iCPK11KeWwEa8rbMDq7l6IrnevQuOQw==",
opt: [],
},
{
alg: "sha512",
val: "P8q/bH6NoZs5MnZKL9D/r/oEZOlyEAmSfuXuJchD2WeXnKbnfcO3fF0WvO6CiqZUGWsEREs9BWLrv1xr3NPOLg==",
opt: [],
},
],
);
}); });
test("if there are no supported algorithms, return the empty set", function () { test("if there are no supported algorithms, return the empty set", function () {
@ -22,7 +33,7 @@ sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk=
md5-bNNVbesNpUvKBgtMOUeYOQ== md5-bNNVbesNpUvKBgtMOUeYOQ==
`); `);
assert.deepEqual(integrityMetadataSet.strongest, []); assert.deepEqual([...integrityMetadataSet.strongest], []);
}); });
test("custom getPrioritizedHashAlgorithm function can be used", function () { test("custom getPrioritizedHashAlgorithm function can be used", function () {
@ -39,9 +50,14 @@ sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLs
}, },
); );
assert.deepEqual(integrityMetadataSet.strongest, [ assert.deepEqual(
new IntegrityMetadata( [...integrityMetadataSet.strongest],
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", [
), {
]); alg: "sha384",
val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
opt: [],
},
],
);
}); });