diff --git a/test/create-integrity-metadata-set.js b/test/create-integrity-metadata-set.js new file mode 100644 index 0000000..442e686 --- /dev/null +++ b/test/create-integrity-metadata-set.js @@ -0,0 +1,35 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { + createIntegrityMetadataSet, + IntegrityMetadata, + IntegrityMetadataSet, +} from "../dist/index.js"; + +test("instantiate a new IntegrityMetadataSet", async function () { + const res = new Response("Hello, world!"); + const data = await res.arrayBuffer(); + const set = await createIntegrityMetadataSet(["sha256"], data); + + assert(set instanceof IntegrityMetadataSet); +}); + +test("instantiate with the specified hash algorithms and ArrayBuffer", async function () { + const res = new Response("Hello, world!"); + const data = await res.arrayBuffer(); + const set = await createIntegrityMetadataSet(["sha384", "sha512"], data); + + assert.deepEqual( + [...set], + [ + new IntegrityMetadata({ + alg: "sha384", + val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", + }), + new IntegrityMetadata({ + alg: "sha512", + val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", + }), + ], + ); +}); diff --git a/test/get-prioritized-hash-algorithm.js b/test/get-prioritized-hash-algorithm.js index 8fa4545..8a5488c 100644 --- a/test/get-prioritized-hash-algorithm.js +++ b/test/get-prioritized-hash-algorithm.js @@ -22,6 +22,6 @@ test("if both strings are empty, return the empty string", function () { assert.strictEqual(getPrioritizedHashAlgorithm("", ""), ""); }); -test("if either is an empty string, it return the supported hash algorithm", function () { +test("if either is the empty string, it return the supported hash algorithm", function () { assert.strictEqual(getPrioritizedHashAlgorithm("sha256", ""), "sha256"); }); diff --git a/test/integrity-metadata-set/constructor.js b/test/integrity-metadata-set/constructor.js new file mode 100644 index 0000000..0530987 --- /dev/null +++ b/test/integrity-metadata-set/constructor.js @@ -0,0 +1,162 @@ +import assert from "node:assert"; +import { test } from "node:test"; +import { IntegrityMetadataSet } from "../../dist/index.js"; + +test("supports SHA-256", function () { + const set = new IntegrityMetadataSet( + "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + ); + + assert.deepEqual(set.strongest[0], { + alg: "sha256", + val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + opt: [], + }); +}); + +test("supports SHA-384", function () { + const set = new IntegrityMetadataSet( + "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", + ); + + assert.deepEqual(set.strongest[0], { + alg: "sha384", + val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", + opt: [], + }); +}); + +test("supports SHA-512", function () { + const set = new IntegrityMetadataSet( + "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", + ); + + assert.deepEqual(set.strongest[0], { + alg: "sha512", + val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", + opt: [], + }); +}); + +test("accepts options", function () { + const set = new IntegrityMetadataSet( + "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=?foo?bar", + ); + + assert.deepEqual(set.strongest[0], { + alg: "sha256", + val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + opt: ["foo", "bar"], + }); +}); + +test("accepts an IntegrityMetadata like object as input", function () { + const set = new IntegrityMetadataSet({ + alg: "sha256", + val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + }); + + assert.deepEqual( + set, + new IntegrityMetadataSet( + "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + ), + ); +}); + +test("multiple algorithms can be accepted", function () { + const set = new IntegrityMetadataSet([ + { + alg: "sha256", + val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + }, + ` +sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r +sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ== +`, + ]); + + assert.deepEqual( + set, + new IntegrityMetadataSet([ + "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", + "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", + ]), + ); +}); + +test("multiple overlapping algorithms can be accepted", function () { + const set = new IntegrityMetadataSet([ + "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + "sha256-uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=", + ]); + + assert.deepEqual( + set, + new IntegrityMetadataSet([ + "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + "sha256-uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=", + ]), + ); +}); + +test("trims leading and trailing whitespace", function () { + const set = new IntegrityMetadataSet( + "\t sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=\u0020\u00a0\u1680\u2000\u2001\u2002\u3000", + ); + + assert.deepEqual( + set, + new IntegrityMetadataSet( + "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + ), + ); +}); + +test("whitespace can be analyzed as entry separator", function () { + const set = new IntegrityMetadataSet( + "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=\t\u0020\u00a0\u1680sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r\u2000\u2001\u2002\u3000sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", + ); + + assert.deepEqual( + set, + new IntegrityMetadataSet([ + "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=", + "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", + "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", + ]), + ); +}); + +test("discards unsupported hash algorithm", function () { + const set = new IntegrityMetadataSet("sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk="); + + assert.deepEqual(set, new IntegrityMetadataSet()); +}); + +test("discards null input", function () { + const set = new IntegrityMetadataSet(null); + + assert.deepEqual(set, new IntegrityMetadataSet()); +}); + +test("discards empty string input", function () { + const set = new IntegrityMetadataSet(""); + + assert.deepEqual(set, new IntegrityMetadataSet()); +}); + +test("discards invalid value", function () { + const set = new IntegrityMetadataSet("md5\0/..invalid-value"); + + assert.deepEqual(set, new IntegrityMetadataSet()); +}); + +test("discards invalid values in a list of multiple inputs", function () { + const set = new IntegrityMetadataSet( + "sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk= md5\0/..invalid-value", + ); + + assert.deepEqual(set, new IntegrityMetadataSet()); +}); diff --git a/test/integrity-metadata-set/join.js b/test/integrity-metadata-set/join.js new file mode 100644 index 0000000..f757c89 --- /dev/null +++ b/test/integrity-metadata-set/join.js @@ -0,0 +1,32 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { IntegrityMetadataSet } from "../../dist/index.js"; + +test("join() can be used", function () { + const integrityMetadataSet = new IntegrityMetadataSet( + ` +sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r +sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ== +`, + ); + + assert.strictEqual( + integrityMetadataSet.join(), + "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", + ); +}); + +test("a separator can be specified", function () { + const integrityMetadataSet = new IntegrityMetadataSet( + ` +sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r +sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ== +`, + ); + + assert.strictEqual( + integrityMetadataSet.join("\n"), + `sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r +sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==`, + ); +}); diff --git a/test/integrity-metadata-set/strongest.js b/test/integrity-metadata-set/strongest.js new file mode 100644 index 0000000..db390a9 --- /dev/null +++ b/test/integrity-metadata-set/strongest.js @@ -0,0 +1,47 @@ +import assert from "node:assert"; +import { test } from "node:test"; +import { IntegrityMetadata, IntegrityMetadataSet } from "../../dist/index.js"; + +test("pick the strongest metadata from set", function () { + const integrityMetadataSet = new IntegrityMetadataSet(` +sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM= +sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r +sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ== +`); + + assert.deepEqual(integrityMetadataSet.strongest, [ + new IntegrityMetadata( + "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", + ), + ]); +}); + +test("if there are no supported algorithms, return the empty set", function () { + const integrityMetadataSet = new IntegrityMetadataSet(` +sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk= +md5-bNNVbesNpUvKBgtMOUeYOQ== +`); + + assert.deepEqual(integrityMetadataSet.strongest, []); +}); + +test("custom getPrioritizedHashAlgorithm function can be used", function () { + const integrityMetadataSet = new IntegrityMetadataSet( + ` +sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM= +sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r +sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ== +`, + { + getPrioritizedHashAlgorithm() { + return "sha384"; + }, + }, + ); + + assert.deepEqual(integrityMetadataSet.strongest, [ + new IntegrityMetadata( + "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r", + ), + ]); +}); diff --git a/test/integrity-metadata-set/to-json.js b/test/integrity-metadata-set/to-json.js new file mode 100644 index 0000000..f5a4045 --- /dev/null +++ b/test/integrity-metadata-set/to-json.js @@ -0,0 +1,17 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { IntegrityMetadataSet } from "../../dist/index.js"; + +test("toJSON() can be used", function () { + const integrityMetadataSet = new IntegrityMetadataSet( + ` +sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r +sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ== +`, + ); + + assert.strictEqual( + integrityMetadataSet.toJSON(), + "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", + ); +}); diff --git a/test/integrity-metadata-set/to-string.js b/test/integrity-metadata-set/to-string.js new file mode 100644 index 0000000..addeb32 --- /dev/null +++ b/test/integrity-metadata-set/to-string.js @@ -0,0 +1,17 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { IntegrityMetadataSet } from "../../dist/index.js"; + +test("toString() can be used", function () { + const integrityMetadataSet = new IntegrityMetadataSet( + ` +sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r +sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ== +`, + ); + + assert.strictEqual( + integrityMetadataSet.toString(), + "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==", + ); +});