mirror of
https://github.com/kou029w/websri.git
synced 2025-04-05 21:26:25 +00:00
Compare commits
22 commits
Author | SHA1 | Date | |
---|---|---|---|
|
345017c52d | ||
|
529d6a3483 | ||
|
09960e348d | ||
|
2e9d3bc281 | ||
|
7e43c94bfb | ||
9a2e7ad45c | |||
d0415bbd04 | |||
|
22450619c4 | ||
83860ef7f8 | |||
93af3bce3c | |||
3d1a4acd2f | |||
52ff1f7e9a | |||
|
c8e7319396 | ||
|
5310a04b87 | ||
|
7d53588259 | ||
|
33044b912a | ||
f4c800793b | |||
|
f2854638df | ||
|
17fce57d8b | ||
|
5f6ceea225 | ||
|
5e369ade90 | ||
8f7b6bd105 |
8 changed files with 1285 additions and 1316 deletions
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
@ -16,10 +16,10 @@ jobs:
|
||||||
contents: write
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4
|
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version: lts/*
|
node-version: lts/*
|
||||||
cache: npm
|
cache: npm
|
||||||
|
|
8
.github/workflows/test.yml
vendored
8
.github/workflows/test.yml
vendored
|
@ -11,8 +11,8 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
- uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4
|
- uses: actions/setup-node@cdca7365b2dadb8aad0a33bc7601856ffabcc48e # v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: npm
|
cache: npm
|
||||||
|
@ -23,7 +23,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
- uses: denoland/setup-deno@v1
|
- uses: denoland/setup-deno@v1
|
||||||
with:
|
with:
|
||||||
deno-version: latest
|
deno-version: latest
|
||||||
|
@ -34,7 +34,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 15
|
timeout-minutes: 15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||||
- uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2
|
- uses: oven-sh/setup-bun@4bc047ad259df6fc24a6c9b0f9a0cb08cf17fbe5 # v2
|
||||||
with:
|
with:
|
||||||
bun-version: latest
|
bun-version: latest
|
||||||
|
|
13
CHANGELOG.md
13
CHANGELOG.md
|
@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.0.1] - 2025-02-19
|
||||||
|
|
||||||
|
- Fixed "Maximum call stack size exceeded" error when using `IntegrityMetadataSet#strongest`
|
||||||
|
|
||||||
|
## [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 +34,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
- First release
|
- First release
|
||||||
|
|
||||||
|
[1.0.1]: https://github.com/kou029w/websri/compare/v1.0.0...v1.0.1
|
||||||
|
[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.1...HEAD
|
||||||
|
|
2242
package-lock.json
generated
2242
package-lock.json
generated
File diff suppressed because it is too large
Load diff
10
package.json
10
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "websri",
|
"name": "websri",
|
||||||
"version": "0.1.0",
|
"version": "1.0.1",
|
||||||
"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>",
|
||||||
|
@ -45,10 +45,10 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@release-it/keep-a-changelog": "5.0.0",
|
"@release-it/keep-a-changelog": "5.0.0",
|
||||||
"@types/node": "22.7.4",
|
"@types/node": "22.13.17",
|
||||||
"pkgroll": "2.5.0",
|
"pkgroll": "2.6.0",
|
||||||
"release-it": "17.6.0",
|
"release-it": "17.10.0",
|
||||||
"tsx": "4.19.1",
|
"tsx": "4.19.3",
|
||||||
"typescript": "5.6.2"
|
"typescript": "5.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
144
src/index.ts
144
src/index.ts
|
@ -90,21 +90,24 @@ 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[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of `IntegrityMetadata` from a given object or string.
|
* Creates an instance of `IntegrityMetadata` from a given object or string.
|
||||||
* @param integrity The integrity metadata input, which can be a string or object.
|
* @param integrity The integrity metadata input, which can be a string or object.
|
||||||
* @example
|
* @example
|
||||||
* ```
|
* ```js
|
||||||
* new IntegrityMetadata("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
|
* new IntegrityMetadata("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* or
|
* or
|
||||||
*
|
*
|
||||||
* ```
|
* ```js
|
||||||
* new IntegrityMetadata({
|
* new IntegrityMetadata({
|
||||||
* alg: "sha256",
|
* alg: "sha256",
|
||||||
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
|
@ -135,17 +138,17 @@ export class IntegrityMetadata implements IntegrityMetadataLike {
|
||||||
* @param integrity The integrity metadata to compare with.
|
* @param integrity The integrity metadata to compare with.
|
||||||
* @returns `true` if the integrity metadata matches, `false` otherwise.
|
* @returns `true` if the integrity metadata matches, `false` otherwise.
|
||||||
* @example
|
* @example
|
||||||
* ```
|
* ```js
|
||||||
* integrityMetadata.match({
|
* integrityMetadata.match("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
|
||||||
* alg: "sha256",
|
|
||||||
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
|
||||||
* })
|
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* or
|
* or
|
||||||
*
|
*
|
||||||
* ```
|
* ```js
|
||||||
* integrityMetadata.match("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
|
* integrityMetadata.match({
|
||||||
|
* alg: "sha256",
|
||||||
|
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
|
* })
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
match(integrity: IntegrityMetadataLike | string | null | undefined): boolean {
|
match(integrity: IntegrityMetadataLike | string | null | undefined): boolean {
|
||||||
|
@ -177,7 +180,7 @@ export class IntegrityMetadata implements IntegrityMetadataLike {
|
||||||
* @param integrity The integrity metadata object to stringify.
|
* @param integrity The integrity metadata object to stringify.
|
||||||
* @returns The stringified integrity metadata.
|
* @returns The stringified integrity metadata.
|
||||||
* @example
|
* @example
|
||||||
* ```
|
* ```js
|
||||||
* IntegrityMetadata.stringify({
|
* IntegrityMetadata.stringify({
|
||||||
* alg: "sha256",
|
* alg: "sha256",
|
||||||
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
|
@ -199,7 +202,7 @@ export class IntegrityMetadata implements IntegrityMetadataLike {
|
||||||
* @param opt Optional additional attributes.
|
* @param opt Optional additional attributes.
|
||||||
* @returns A promise that resolves to an `IntegrityMetadata` object.
|
* @returns A promise that resolves to an `IntegrityMetadata` object.
|
||||||
* @example
|
* @example
|
||||||
* ```
|
* ```js
|
||||||
* const res = new Response("Hello, world!");
|
* const res = new Response("Hello, world!");
|
||||||
* const data = await res.arrayBuffer();
|
* const data = await res.arrayBuffer();
|
||||||
* const integrityMetadata = await createIntegrityMetadata("sha256", data);
|
* const integrityMetadata = await createIntegrityMetadata("sha256", data);
|
||||||
|
@ -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
|
||||||
|
@ -251,23 +249,23 @@ export class IntegrityMetadataSet {
|
||||||
* @param integrity The integrity metadata or an array of integrity metadata.
|
* @param integrity The integrity metadata or an array of integrity metadata.
|
||||||
* @param options Optional configuration options for hash algorithm prioritization.
|
* @param options Optional configuration options for hash algorithm prioritization.
|
||||||
* @example
|
* @example
|
||||||
* ```
|
* ```js
|
||||||
* new IntegrityMetadataSet(`
|
|
||||||
* sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=
|
|
||||||
* sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r
|
|
||||||
* sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==
|
|
||||||
* `)
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* or
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* new IntegrityMetadataSet([
|
* new IntegrityMetadataSet([
|
||||||
* "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
* "sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
* "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
* "sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
||||||
* "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
* "sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
||||||
* ])
|
* ])
|
||||||
* ```
|
* ```
|
||||||
|
*
|
||||||
|
* or
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* new IntegrityMetadataSet(`
|
||||||
|
* sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=
|
||||||
|
* sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r
|
||||||
|
* sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==
|
||||||
|
* `)
|
||||||
|
* ```
|
||||||
*/
|
*/
|
||||||
constructor(
|
constructor(
|
||||||
integrity:
|
integrity:
|
||||||
|
@ -281,53 +279,36 @@ 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables iteration over the set of integrity metadata.
|
* Enables iteration over the set of integrity metadata.
|
||||||
* @returns A generator that yields each IntegrityMetadata object.
|
* @returns A generator that yields each IntegrityMetadata object.
|
||||||
* @example
|
* @example
|
||||||
* ```
|
* ```js
|
||||||
* [...integrityMetadataSet]
|
* [...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([
|
||||||
|
...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);
|
||||||
|
|
||||||
|
@ -354,17 +368,17 @@ export class IntegrityMetadataSet {
|
||||||
* @param integrity The integrity metadata to match.
|
* @param integrity The integrity metadata to match.
|
||||||
* @returns `true` if the integrity metadata matches, `false` otherwise.
|
* @returns `true` if the integrity metadata matches, `false` otherwise.
|
||||||
* @example
|
* @example
|
||||||
* ```
|
* ```js
|
||||||
* integrityMetadataSet.match({
|
* integrityMetadataSet.match("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
|
||||||
* alg: "sha256",
|
|
||||||
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
|
||||||
* })
|
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* or
|
* or
|
||||||
*
|
*
|
||||||
* ```
|
* ```js
|
||||||
* integrityMetadataSet.match("sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=")
|
* integrityMetadataSet.match({
|
||||||
|
* alg: "sha256",
|
||||||
|
* val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
|
* })
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
match(integrity: IntegrityMetadataLike | string | null | undefined): boolean {
|
match(integrity: IntegrityMetadataLike | string | null | undefined): boolean {
|
||||||
|
@ -407,7 +421,7 @@ export class IntegrityMetadataSet {
|
||||||
* @param options Optional configuration options for the metadata set.
|
* @param options Optional configuration options for the metadata set.
|
||||||
* @returns A promise that resolves to an `IntegrityMetadataSet` object.
|
* @returns A promise that resolves to an `IntegrityMetadataSet` object.
|
||||||
* @example
|
* @example
|
||||||
* ```
|
* ```js
|
||||||
* const res = new Response("Hello, world!");
|
* const res = new Response("Hello, world!");
|
||||||
* const data = await res.arrayBuffer();
|
* const data = await res.arrayBuffer();
|
||||||
* const set = await createIntegrityMetadataSet(["sha256", "sha384", "sha512"], data);
|
* const set = await createIntegrityMetadataSet(["sha256", "sha384", "sha512"], data);
|
||||||
|
|
|
@ -7,11 +7,16 @@ test("supports SHA-256", function () {
|
||||||
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(set.strongest[0], {
|
assert.deepEqual(
|
||||||
alg: "sha256",
|
[...set],
|
||||||
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
[
|
||||||
opt: [],
|
{
|
||||||
});
|
alg: "sha256",
|
||||||
|
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
|
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(
|
||||||
alg: "sha384",
|
[...set],
|
||||||
val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
[
|
||||||
opt: [],
|
{
|
||||||
});
|
alg: "sha384",
|
||||||
|
val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
||||||
|
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(
|
||||||
alg: "sha512",
|
[...set],
|
||||||
val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
[
|
||||||
opt: [],
|
{
|
||||||
});
|
alg: "sha512",
|
||||||
|
val: "wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
||||||
|
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(
|
||||||
alg: "sha256",
|
[...set],
|
||||||
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 () {
|
||||||
|
@ -57,10 +77,14 @@ test("accepts an IntegrityMetadata like object as input", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
set,
|
[...set],
|
||||||
new IntegrityMetadataSet(
|
[
|
||||||
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
{
|
||||||
),
|
alg: "sha256",
|
||||||
|
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
|
opt: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -77,12 +101,14 @@ sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLs
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
set,
|
[...set],
|
||||||
new IntegrityMetadataSet([
|
[
|
||||||
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
...new IntegrityMetadataSet([
|
||||||
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
||||||
]),
|
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
||||||
|
]),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -93,11 +119,13 @@ test("multiple overlapping algorithms can be accepted", function () {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
set,
|
[...set],
|
||||||
new IntegrityMetadataSet([
|
[
|
||||||
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
...new IntegrityMetadataSet([
|
||||||
"sha256-uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=",
|
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
]),
|
"sha256-uU0nuZNNPgilLlLX2n2r+sSE7+N6U4DukIj3rOLvzek=",
|
||||||
|
]),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -107,10 +135,14 @@ test("trims leading and trailing whitespace", function () {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
set,
|
[...set],
|
||||||
new IntegrityMetadataSet(
|
[
|
||||||
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
{
|
||||||
),
|
alg: "sha256",
|
||||||
|
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
|
opt: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -120,37 +152,39 @@ test("whitespace can be analyzed as entry separator", function () {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
set,
|
[...set],
|
||||||
new IntegrityMetadataSet([
|
[
|
||||||
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
...new IntegrityMetadataSet([
|
||||||
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||||
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
||||||
]),
|
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
||||||
|
]),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("discards unsupported hash algorithm", function () {
|
test("discards unsupported hash algorithm", function () {
|
||||||
const set = new IntegrityMetadataSet("sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk=");
|
const set = new IntegrityMetadataSet("sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk=");
|
||||||
|
|
||||||
assert.deepEqual(set, new IntegrityMetadataSet([]));
|
assert.deepEqual([...set], []);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("discards null input", function () {
|
test("discards null input", function () {
|
||||||
const set = new IntegrityMetadataSet(null);
|
const set = new IntegrityMetadataSet(null);
|
||||||
|
|
||||||
assert.deepEqual(set, new IntegrityMetadataSet([]));
|
assert.deepEqual([...set], []);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("discards empty string input", function () {
|
test("discards empty string input", function () {
|
||||||
const set = new IntegrityMetadataSet([]);
|
const set = new IntegrityMetadataSet([]);
|
||||||
|
|
||||||
assert.deepEqual(set, new IntegrityMetadataSet([]));
|
assert.deepEqual([...set], []);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("discards invalid value", function () {
|
test("discards invalid value", function () {
|
||||||
const set = new IntegrityMetadataSet("md5\0/..invalid-value");
|
const set = new IntegrityMetadataSet("md5\0/..invalid-value");
|
||||||
|
|
||||||
assert.deepEqual(set, new IntegrityMetadataSet([]));
|
assert.deepEqual([...set], []);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("discards invalid values in a list of multiple inputs", function () {
|
test("discards invalid values in a list of multiple inputs", function () {
|
||||||
|
@ -158,5 +192,5 @@ test("discards invalid values in a list of multiple inputs", function () {
|
||||||
"sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk= md5\0/..invalid-value",
|
"sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk= md5\0/..invalid-value",
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(set, new IntegrityMetadataSet([]));
|
assert.deepEqual([...set], []);
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,19 +1,30 @@
|
||||||
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(`
|
||||||
sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=
|
sha256-gxZXfeA3KCK+ZyBybEt6liVPg+FWGf/KLVU6rufBujE=
|
||||||
sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r
|
sha384-LDW1hUX1OX+VZsNmW+LELiky69a4xF+FfVsTlqZOhqPiPj5YYo20jP6C8H8uXMZf
|
||||||
sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==
|
sha512-aqnrVqlE3w/CWs51jb3FHCsFSBwfpecdXHaFFYZNkxfW2Z1qyJm4mA9iCPK11KeWwEa8rbMDq7l6IrnevQuOQw==
|
||||||
|
sha512-P8q/bH6NoZs5MnZKL9D/r/oEZOlyEAmSfuXuJchD2WeXnKbnfcO3fF0WvO6CiqZUGWsEREs9BWLrv1xr3NPOLg==
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assert.deepEqual(integrityMetadataSet.strongest, [
|
assert.deepEqual(
|
||||||
new IntegrityMetadata(
|
[...integrityMetadataSet.strongest],
|
||||||
"sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLsmn1H256V/eUIQ==",
|
[
|
||||||
),
|
{
|
||||||
]);
|
alg: "sha512",
|
||||||
|
val: "aqnrVqlE3w/CWs51jb3FHCsFSBwfpecdXHaFFYZNkxfW2Z1qyJm4mA9iCPK11KeWwEa8rbMDq7l6IrnevQuOQw==",
|
||||||
|
opt: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
alg: "sha512",
|
||||||
|
val: "P8q/bH6NoZs5MnZKL9D/r/oEZOlyEAmSfuXuJchD2WeXnKbnfcO3fF0WvO6CiqZUGWsEREs9BWLrv1xr3NPOLg==",
|
||||||
|
opt: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
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 +33,7 @@ sha1-lDpwLQbzRZmu4fjajvn3KWAx1pk=
|
||||||
md5-bNNVbesNpUvKBgtMOUeYOQ==
|
md5-bNNVbesNpUvKBgtMOUeYOQ==
|
||||||
`);
|
`);
|
||||||
|
|
||||||
assert.deepEqual(integrityMetadataSet.strongest, []);
|
assert.deepEqual([...integrityMetadataSet.strongest], []);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("custom getPrioritizedHashAlgorithm function can be used", function () {
|
test("custom getPrioritizedHashAlgorithm function can be used", function () {
|
||||||
|
@ -39,9 +50,14 @@ sha512-wVJ82JPBJHc9gRkRlwyP5uhX1t9dySJr2KFgYUwM2WOk3eorlLt9NgIe+dhl1c6ilKgt1JoLs
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.deepEqual(integrityMetadataSet.strongest, [
|
assert.deepEqual(
|
||||||
new IntegrityMetadata(
|
[...integrityMetadataSet.strongest],
|
||||||
"sha384-VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
[
|
||||||
),
|
{
|
||||||
]);
|
alg: "sha384",
|
||||||
|
val: "VbxVaw0v4Pzlgrpf4Huq//A1ZTY4x6wNVJTCpkwL6hzFczHHwSpFzbyn9MNKCJ7r",
|
||||||
|
opt: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Reference in a new issue