mirror of
https://github.com/kou029w/websri.git
synced 2025-01-31 14:28:14 +00:00
Compare commits
No commits in common. "22450619c4bef0477456b6fce2b36de85b8b9fae" and "c8e73193969abb6d8ab5d2c1b4636d4a1f5a4441" have entirely different histories.
22450619c4
...
c8e7319396
6 changed files with 66 additions and 111 deletions
|
@ -7,11 +7,6 @@ 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.
|
||||||
|
@ -30,9 +25,8 @@ 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/v1.0.0...HEAD
|
[unreleased]: https://github.com/kou029w/websri/compare/v0.1.0...HEAD
|
||||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "websri",
|
"name": "websri",
|
||||||
"version": "1.0.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "websri",
|
"name": "websri",
|
||||||
"version": "1.0.0",
|
"version": "0.1.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": "1.0.0",
|
"version": "0.1.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,11 +90,8 @@ 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[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -241,7 +238,12 @@ 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
|
||||||
|
@ -279,23 +281,40 @@ export class IntegrityMetadataSet {
|
||||||
_getPrioritizedHashAlgorithm = getPrioritizedHashAlgorithm,
|
_getPrioritizedHashAlgorithm = getPrioritizedHashAlgorithm,
|
||||||
}: IntegrityMetadataSetOptions = {},
|
}: IntegrityMetadataSetOptions = {},
|
||||||
) {
|
) {
|
||||||
this.#set = [integrity]
|
const set: ReadonlyArray<
|
||||||
|
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> =>
|
||||||
if (typeof integrity === "string") {
|
typeof integrity === "string"
|
||||||
return integrity.split(SeparatorRegex);
|
? integrity.split(SeparatorRegex)
|
||||||
}
|
: [integrity],
|
||||||
|
);
|
||||||
|
|
||||||
return [integrity];
|
this.#set = set
|
||||||
},
|
|
||||||
)
|
|
||||||
.map((integrity) => new IntegrityMetadata(integrity))
|
.map((integrity) => new IntegrityMetadata(integrity))
|
||||||
.filter((integrityMetadata) => integrityMetadata.toString() !== "");
|
.filter((integrityMetadata) => integrityMetadata.toString() !== "");
|
||||||
|
|
||||||
this.#getPrioritizedHashAlgorithm = _getPrioritizedHashAlgorithm;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -308,7 +327,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 new IntegrityMetadata(integrityMetadata);
|
yield integrityMetadata;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,44 +338,11 @@ 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,16 +7,11 @@ test("supports SHA-256", function () {
|
||||||
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(set.strongest[0], {
|
||||||
[...set],
|
alg: "sha256",
|
||||||
[
|
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
{
|
opt: [],
|
||||||
alg: "sha256",
|
});
|
||||||
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
|
||||||
opt: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("supports SHA-384", function () {
|
test("supports SHA-384", function () {
|
||||||
|
@ -24,16 +19,11 @@ test("supports SHA-384", function () {
|
||||||
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(set.strongest[0], {
|
||||||
[...set],
|
alg: "sha384",
|
||||||
[
|
val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
||||||
{
|
opt: [],
|
||||||
alg: "sha384",
|
});
|
||||||
val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
|
||||||
opt: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("supports SHA-512", function () {
|
test("supports SHA-512", function () {
|
||||||
|
@ -41,16 +31,11 @@ test("supports SHA-512", function () {
|
||||||
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(set.strongest[0], {
|
||||||
[...set],
|
alg: "sha512",
|
||||||
[
|
val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
||||||
{
|
opt: [],
|
||||||
alg: "sha512",
|
});
|
||||||
val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
|
||||||
opt: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("accepts options", function () {
|
test("accepts options", function () {
|
||||||
|
@ -58,16 +43,11 @@ test("accepts options", function () {
|
||||||
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=?foo?bar",
|
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=?foo?bar",
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(set.strongest[0], {
|
||||||
[...set],
|
alg: "sha256",
|
||||||
[
|
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 () {
|
||||||
|
|
|
@ -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 { IntegrityMetadataSet } from "../../src/index.ts";
|
import { IntegrityMetadata, 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,12 +9,11 @@ sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r
|
||||||
sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==
|
sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(integrityMetadataSet.strongest, [
|
||||||
integrityMetadataSet.strongest,
|
new IntegrityMetadata(
|
||||||
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 () {
|
||||||
|
@ -23,10 +22,7 @@ sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk=
|
||||||
md5-bNNVbesNpUvKBgtMOUeYOQ==
|
md5-bNNVbesNpUvKBgtMOUeYOQ==
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(integrityMetadataSet.strongest, []);
|
||||||
integrityMetadataSet.strongest,
|
|
||||||
new IntegrityMetadataSet([]),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("custom getPrioritizedHashAlgorithm function can be used", function () {
|
test("custom getPrioritizedHashAlgorithm function can be used", function () {
|
||||||
|
@ -43,10 +39,9 @@ sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLs
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(integrityMetadataSet.strongest, [
|
||||||
integrityMetadataSet.strongest,
|
new IntegrityMetadata(
|
||||||
new IntegrityMetadataSet(
|
|
||||||
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
||||||
),
|
),
|
||||||
);
|
]);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue