change strongest property type from Array<IntegrityMetadata> to IntegrityMetadataSet

This commit is contained in:
Nebel 2025-01-06 13:41:24 +09:00
parent 3d1a4acd2f
commit 93af3bce3c
Signed by: nebel
GPG key ID: 79807D08C6EF6460
3 changed files with 98 additions and 62 deletions

View file

@ -241,12 +241,7 @@ export type IntegrityMetadataSetOptions = {
*/
export class IntegrityMetadataSet {
#set: ReadonlyArray<IntegrityMetadata>;
/**
* 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> = [];
#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<IntegrityMetadataLike | string | null | undefined> =>
typeof integrity === "string"
? integrity.split(SeparatorRegex)
: [integrity],
);
): ReadonlyArray<IntegrityMetadataLike | string | null | undefined> => {
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<IntegrityMetadata> {
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<HashAlgorithm> {
const strongestHashAlgorithms = this.strongest
const strongestHashAlgorithms = [...this.strongest]
.map(({ alg }) => alg as HashAlgorithm)
.filter(Boolean);

View file

@ -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 () {

View file

@ -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",
),
]);
);
});