mirror of
https://github.com/kou029w/websri.git
synced 2025-01-31 14:28:14 +00:00
Compare commits
5 commits
c8e7319396
...
22450619c4
Author | SHA1 | Date | |
---|---|---|---|
|
22450619c4 | ||
83860ef7f8 | |||
93af3bce3c | |||
3d1a4acd2f | |||
52ff1f7e9a |
6 changed files with 111 additions and 66 deletions
|
@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [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 +30,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
- First release
|
- First release
|
||||||
|
|
||||||
|
[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.0...HEAD
|
||||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "websri",
|
"name": "websri",
|
||||||
"version": "0.1.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "websri",
|
"name": "websri",
|
||||||
"version": "0.1.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@release-it/keep-a-changelog": "5.0.0",
|
"@release-it/keep-a-changelog": "5.0.0",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "websri",
|
"name": "websri",
|
||||||
"version": "0.1.0",
|
"version": "1.0.0",
|
||||||
"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>",
|
||||||
|
|
82
src/index.ts
82
src/index.ts
|
@ -90,8 +90,11 @@ 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[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
||||||
|
@ -281,40 +279,23 @@ 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -327,7 +308,7 @@ export class 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([
|
||||||
|
...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.
|
* 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);
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,16 @@ test("supports SHA-256", function () {
|
||||||
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(set.strongest[0], {
|
assert.deepEqual(
|
||||||
|
[...set],
|
||||||
|
[
|
||||||
|
{
|
||||||
alg: "sha256",
|
alg: "sha256",
|
||||||
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
opt: [],
|
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(
|
||||||
|
[...set],
|
||||||
|
[
|
||||||
|
{
|
||||||
alg: "sha384",
|
alg: "sha384",
|
||||||
val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
||||||
opt: [],
|
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(
|
||||||
|
[...set],
|
||||||
|
[
|
||||||
|
{
|
||||||
alg: "sha512",
|
alg: "sha512",
|
||||||
val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
||||||
opt: [],
|
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(
|
||||||
|
[...set],
|
||||||
|
[
|
||||||
|
{
|
||||||
alg: "sha256",
|
alg: "sha256",
|
||||||
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
opt: ["foo", "bar"],
|
opt: ["foo", "bar"],
|
||||||
});
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("accepts an IntegrityMetadata like object as input", function () {
|
test("accepts an IntegrityMetadata like object as input", function () {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
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(`
|
||||||
|
@ -9,11 +9,12 @@ sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r
|
||||||
sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==
|
sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assert.deepEqual(integrityMetadataSet.strongest, [
|
assert.deepEqual(
|
||||||
new IntegrityMetadata(
|
integrityMetadataSet.strongest,
|
||||||
|
new IntegrityMetadataSet(
|
||||||
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
||||||
),
|
),
|
||||||
]);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
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 +23,10 @@ sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk=
|
||||||
md5-bNNVbesNpUvKBgtMOUeYOQ==
|
md5-bNNVbesNpUvKBgtMOUeYOQ==
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assert.deepEqual(integrityMetadataSet.strongest, []);
|
assert.deepEqual(
|
||||||
|
integrityMetadataSet.strongest,
|
||||||
|
new IntegrityMetadataSet([]),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("custom getPrioritizedHashAlgorithm function can be used", function () {
|
test("custom getPrioritizedHashAlgorithm function can be used", function () {
|
||||||
|
@ -39,9 +43,10 @@ sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLs
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(integrityMetadataSet.strongest, [
|
assert.deepEqual(
|
||||||
new IntegrityMetadata(
|
integrityMetadataSet.strongest,
|
||||||
|
new IntegrityMetadataSet(
|
||||||
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
||||||
),
|
),
|
||||||
]);
|
);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue