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]
|
||||
|
||||
## [1.0.0] - 2025-01-06
|
||||
|
||||
- Change `strongest` property type from `Array<IntegrityMetadata>` to `IntegrityMetadataSet`
|
||||
- Improve documentation
|
||||
|
||||
## [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.
|
||||
|
@ -25,8 +30,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- 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.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.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",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "websri",
|
||||
"version": "0.1.0",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@release-it/keep-a-changelog": "5.0.0",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"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.",
|
||||
"license": "MIT",
|
||||
"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.
|
||||
*/
|
||||
export class IntegrityMetadata implements IntegrityMetadataLike {
|
||||
/** Hash algorithm */
|
||||
alg: PrioritizedHashAlgorithm;
|
||||
/** The base64-encoded hash value of the resource */
|
||||
val: string;
|
||||
/** Optional additional attributes */
|
||||
opt: string[];
|
||||
|
||||
/**
|
||||
|
@ -238,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
|
||||
|
@ -281,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -327,7 +308,7 @@ export class IntegrityMetadataSet {
|
|||
*/
|
||||
*[Symbol.iterator](): Generator<IntegrityMetadata> {
|
||||
for (const integrityMetadata of this.#set) {
|
||||
yield integrityMetadata;
|
||||
yield new IntegrityMetadata(integrityMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,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);
|
||||
|
||||
|
|
|
@ -7,11 +7,16 @@ test("supports SHA-256", function () {
|
|||
"sha256-MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||
);
|
||||
|
||||
assert.deepEqual(set.strongest[0], {
|
||||
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], {
|
||||
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], {
|
||||
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], {
|
||||
assert.deepEqual(
|
||||
[...set],
|
||||
[
|
||||
{
|
||||
alg: "sha256",
|
||||
val: "MV9b23bQeMQ7isAGTkoBZGErH853yGk0W/yUx1iU7dM=",
|
||||
opt: ["foo", "bar"],
|
||||
});
|
||||
},
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test("accepts an IntegrityMetadata like object as input", function () {
|
||||
|
|
|
@ -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",
|
||||
),
|
||||
]);
|
||||
);
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue