From 93af3bce3c5e29df248a841e84970f04ccd388f3 Mon Sep 17 00:00:00 2001 From: Kohei Watanabe Date: Mon, 6 Jan 2025 13:41:24 +0900 Subject: [PATCH] change `strongest` property type from `Array` to `IntegrityMetadataSet` --- src/index.ts | 79 ++++++++++++---------- test/integrity-metadata-set/constructor.ts | 60 ++++++++++------ test/integrity-metadata-set/strongest.ts | 21 +++--- 3 files changed, 98 insertions(+), 62 deletions(-) diff --git a/src/index.ts b/src/index.ts index eaccad8..a41fdeb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -241,12 +241,7 @@ export type IntegrityMetadataSetOptions = { */ export class IntegrityMetadataSet { #set: ReadonlyArray; - - /** - * 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 = []; + #getPrioritizedHashAlgorithm = getPrioritizedHashAlgorithm; /** * Create an instance of `IntegrityMetadataSet` from integrity metadata or an array of integrity @@ -284,40 +279,23 @@ export class IntegrityMetadataSet { _getPrioritizedHashAlgorithm = getPrioritizedHashAlgorithm, }: IntegrityMetadataSetOptions = {}, ) { - const set: ReadonlyArray< - IntegrityMetadataLike | string | null | undefined - > = [integrity] + this.#set = [integrity] .flat() .flatMap( ( integrity: IntegrityMetadataLike | string | null | undefined, - ): ReadonlyArray => - typeof integrity === "string" - ? integrity.split(SeparatorRegex) - : [integrity], - ); + ): ReadonlyArray => { + if (typeof integrity === "string") { + return integrity.split(SeparatorRegex); + } - this.#set = set + return [integrity]; + }, + ) .map((integrity) => new IntegrityMetadata(integrity)) .filter((integrityMetadata) => integrityMetadata.toString() !== ""); - for (const integrityMetadata of this.#set) { - 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; - } - } + this.#getPrioritizedHashAlgorithm = _getPrioritizedHashAlgorithm; } /** @@ -330,7 +308,7 @@ export class IntegrityMetadataSet { */ *[Symbol.iterator](): Generator { for (const integrityMetadata of this.#set) { - yield integrityMetadata; + yield new IntegrityMetadata(integrityMetadata); } } @@ -341,11 +319,44 @@ export class IntegrityMetadataSet { 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([ + ...this.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. */ get strongestHashAlgorithms(): ReadonlyArray { - const strongestHashAlgorithms = this.strongest + const strongestHashAlgorithms = [...this.strongest] .map(({ alg }) => alg as HashAlgorithm) .filter(Boolean); diff --git a/test/integrity-metadata-set/constructor.ts b/test/integrity-metadata-set/constructor.ts index f8a3782..b425e83 100644 --- a/test/integrity-metadata-set/constructor.ts +++ b/test/integrity-metadata-set/constructor.ts @@ -7,11 +7,16 @@ test("supports SHA-256", function () { "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", ); - assert.deepEqual(set.strongest[0], { - alg: "sha256", - val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", - opt: [], - }); + assert.deepEqual( + [...set], + [ + { + alg: "sha256", + val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + opt: [], + }, + ], + ); }); test("supports SHA-384", function () { @@ -19,11 +24,16 @@ test("supports SHA-384", function () { "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", ); - assert.deepEqual(set.strongest[0], { - alg: "sha384", - val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", - opt: [], - }); + assert.deepEqual( + [...set], + [ + { + alg: "sha384", + val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", + opt: [], + }, + ], + ); }); test("supports SHA-512", function () { @@ -31,11 +41,16 @@ test("supports SHA-512", function () { "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", ); - assert.deepEqual(set.strongest[0], { - alg: "sha512", - val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", - opt: [], - }); + assert.deepEqual( + [...set], + [ + { + alg: "sha512", + val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", + opt: [], + }, + ], + ); }); test("accepts options", function () { @@ -43,11 +58,16 @@ test("accepts options", function () { "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=?foo?bar", ); - assert.deepEqual(set.strongest[0], { - alg: "sha256", - val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", - opt: ["foo", "bar"], - }); + assert.deepEqual( + [...set], + [ + { + alg: "sha256", + val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + opt: ["foo", "bar"], + }, + ], + ); }); test("accepts an IntegrityMetadata like object as input", function () { diff --git a/test/integrity-metadata-set/strongest.ts b/test/integrity-metadata-set/strongest.ts index 7fb0aad..1e084ea 100644 --- a/test/integrity-metadata-set/strongest.ts +++ b/test/integrity-metadata-set/strongest.ts @@ -1,6 +1,6 @@ import assert from "node:assert"; 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 () { const integrityMetadataSet = new IntegrityMetadataSet(` @@ -9,11 +9,12 @@ sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ== `); - assert.deepEqual(integrityMetadataSet.strongest, [ - new IntegrityMetadata( + assert.deepEqual( + integrityMetadataSet.strongest, + new IntegrityMetadataSet( "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", ), - ]); + ); }); test("if there are no supported algorithms, return the empty set", function () { @@ -22,7 +23,10 @@ sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk= md5-bNNVbesNpUvKBgtMOUeYOQ== `); - assert.deepEqual(integrityMetadataSet.strongest, []); + assert.deepEqual( + integrityMetadataSet.strongest, + new IntegrityMetadataSet([]), + ); }); test("custom getPrioritizedHashAlgorithm function can be used", function () { @@ -39,9 +43,10 @@ sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLs }, ); - assert.deepEqual(integrityMetadataSet.strongest, [ - new IntegrityMetadata( + assert.deepEqual( + integrityMetadataSet.strongest, + new IntegrityMetadataSet( "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", ), - ]); + ); });