lacewing 1.0.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +59 -0
- package/README.md +37 -0
- package/SECURITY.md +14 -13
- package/dist/src/extension.js +22 -0
- package/dist/src/extension.js.map +1 -0
- package/dist/src/key/import.js +2 -1
- package/dist/src/key/import.js.map +1 -1
- package/dist/src/lib/algorithms.js +9 -2
- package/dist/src/lib/algorithms.js.map +1 -1
- package/dist/src/lib/duration.js +3 -0
- package/dist/src/lib/duration.js.map +1 -1
- package/dist/types/src/extension.d.ts +22 -0
- package/dist/types/src/extension.d.ts.map +1 -0
- package/dist/types/src/key/import.d.ts.map +1 -1
- package/dist/types/src/lib/algorithms.d.ts +4 -0
- package/dist/types/src/lib/algorithms.d.ts.map +1 -1
- package/dist/types/src/lib/duration.d.ts.map +1 -1
- package/package.json +13 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [1.2.0] - 2026-09-26
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- Export parseJsonObject, the strict JSON reading tokens get
|
|
10
|
+
|
|
11
|
+
## [1.1.0] - 2026-09-26
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
- Corrected docs ignore
|
|
16
|
+
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
- Add Ed25519 under its RFC 9864 name and freeze registry entries
|
|
20
|
+
- Add the read-only lacewing/extension subpath
|
|
21
|
+
|
|
22
|
+
### Miscellaneous Tasks
|
|
23
|
+
|
|
24
|
+
- Bump tsx from 4.23.1 to 4.23.5
|
|
25
|
+
- Bump typescript-eslint from 8.65.0 to 8.66.0
|
|
26
|
+
- Bump tsx from 4.23.5 to 4.23.9
|
|
27
|
+
- Bump eslint from 10.8.0 to 10.8.1
|
|
28
|
+
- Bump tsx from 4.23.9 to 4.23.11
|
|
29
|
+
- Bump @types/node from 26.1.2 to 26.2.0
|
|
30
|
+
- Bump tsx from 4.23.11 to 4.23.12
|
|
31
|
+
- Bump typescript-eslint from 8.66.0 to 8.67.0
|
|
32
|
+
- Bump @commitlint/cli from 21.2.1 to 21.2.2
|
|
33
|
+
- Bump @commitlint/config-conventional
|
|
34
|
+
- Bump typescript-eslint from 8.67.0 to 8.68.0
|
|
35
|
+
- Bump @types/node from 26.2.0 to 26.3.0
|
|
36
|
+
- Bump eslint from 10.9.0 to 10.9.1
|
|
37
|
+
- Bump @types/node from 26.3.0 to 26.4.0
|
|
38
|
+
- Bump lefthook from 2.1.10 to 2.1.12
|
|
39
|
+
- Bump tsx from 4.23.12 to 4.23.13
|
|
40
|
+
- Bump typescript-eslint from 8.68.0 to 8.69.0
|
|
41
|
+
- Bump @types/node from 26.4.0 to 26.4.1
|
|
42
|
+
- Bump eslint from 10.9.1 to 10.10.0
|
|
43
|
+
- Bump typescript-eslint from 8.69.0 to 8.70.0
|
|
44
|
+
- Bump @types/node from 26.4.1 to 26.5.0
|
|
45
|
+
- Bump @types/node from 26.5.0 to 26.5.1
|
|
46
|
+
- Bump fast-check from 4.9.0 to 4.10.0
|
|
47
|
+
- Bump lefthook from 2.1.12 to 2.1.14
|
|
48
|
+
- Added long overdue document
|
|
49
|
+
- Bump fast-check from 4.10.0 to 4.10.1
|
|
50
|
+
- Bump git-cliff from 2.13.1 to 2.14.2
|
|
51
|
+
- Bump @types/node from 26.5.1 to 26.6.2
|
|
52
|
+
- Bump eslint from 10.10.0 to 10.11.0
|
|
53
|
+
- Bump @commitlint/config-conventional
|
|
54
|
+
- Bump @commitlint/cli from 21.2.2 to 21.2.3
|
|
55
|
+
- Bump fast-check from 4.10.1 to 4.10.2
|
|
56
|
+
- Bump tsx from 4.23.13 to 4.23.15
|
|
57
|
+
- Bump typescript-eslint from 8.70.0 to 8.70.1
|
|
58
|
+
- Adjusted dist dir with more strictness
|
|
59
|
+
|
|
60
|
+
### Testing
|
|
61
|
+
|
|
62
|
+
- Check the built package through its exports map
|
|
63
|
+
|
|
5
64
|
## [1.0.2] - 2026-08-01
|
|
6
65
|
|
|
7
66
|
### Bug Fixes
|
package/README.md
CHANGED
|
@@ -414,6 +414,43 @@ import { enableLegacyRSA } from "lacewing/legacy/rsa"; // RS256 + RS384 + RS512
|
|
|
414
414
|
|
|
415
415
|
Prefer `PS256`/`EdDSA` everywhere you control both sides.
|
|
416
416
|
|
|
417
|
+
### Ed25519 has two names
|
|
418
|
+
|
|
419
|
+
`EdDSA` and `Ed25519` are the same signature over the same key. `Ed25519` is
|
|
420
|
+
the fully-specified name from [RFC 9864](https://www.rfc-editor.org/rfc/rfc9864),
|
|
421
|
+
and some issuers and clients (oauth4webapi, for one) write only that one. Both
|
|
422
|
+
are in the registry, as two entries, and Lacewing does not treat them as
|
|
423
|
+
aliases:
|
|
424
|
+
|
|
425
|
+
- A profile accepts the names you list. `algorithms: ["EdDSA"]` refuses a
|
|
426
|
+
token whose header says `Ed25519`; list both if your issuer might send
|
|
427
|
+
either.
|
|
428
|
+
- A key is bound to the name it was imported under. A key imported for
|
|
429
|
+
`EdDSA` does not verify an `Ed25519` token, even with both allowlisted.
|
|
430
|
+
Import it once per name, or use a JWKS entry with no `alg`, which serves
|
|
431
|
+
both.
|
|
432
|
+
|
|
433
|
+
### Building on Lacewing: `lacewing/extension`
|
|
434
|
+
|
|
435
|
+
For packages that add a check on top of a Lacewing profile and must agree with
|
|
436
|
+
Lacewing about what a valid algorithm, duration or JWS segment is. It is
|
|
437
|
+
covered by semver like the root export, and it is read-only:
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
import { getAlgorithmProperties, parseDuration, parseJsonObject, readHeaderValue } from "lacewing/extension";
|
|
441
|
+
|
|
442
|
+
getAlgorithmProperties("ES256"); // { name: "ES256", kty: "EC", crv: "P-256", minKeyBits: 256 }, frozen
|
|
443
|
+
parseDuration("15m"); // 900, as DurationSeconds
|
|
444
|
+
parseJsonObject(text, "header"); // a plain object, or JWTInvalid; a member named twice is refused
|
|
445
|
+
readHeaderValue(request, "dpop", "verifyProof"); // same source rules as parseBearer
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Registry entries come back frozen, so nothing that imports them can lower a
|
|
449
|
+
key-size floor for the rest of the process. Adding to the registry stays
|
|
450
|
+
behind the `legacy/` imports, and header validation is not exported: it
|
|
451
|
+
refuses an embedded `jwk`, which is right for a token and wrong for a format
|
|
452
|
+
that carries its own key, so each format owns its own header rules.
|
|
453
|
+
|
|
417
454
|
## Why Lacewing exists
|
|
418
455
|
|
|
419
456
|
Lacewing is not trying to compete with general-purpose JWT libraries. It started as the hardened setup I kept rebuilding for my own projects, packaged so I'd stop rebuilding it.
|
package/SECURITY.md
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
# Security Policy
|
|
2
2
|
|
|
3
|
-
## Supported Versions
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
currently being supported with security updates.
|
|
4
|
+
## Security/Bugfix Versions
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
| 4.0.x | :white_check_mark: |
|
|
13
|
-
| < 4.0 | :x: |
|
|
6
|
+
Security and bug fixes are generally provided only for the last minor version.
|
|
7
|
+
Fixes are released either as part of the next minor version or as an on-demand patch version.
|
|
8
|
+
|
|
9
|
+
Security fixes are given priority and might be enough to cause a new version to be released.
|
|
14
10
|
|
|
15
11
|
## Reporting a Vulnerability
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
We encourage responsible disclosure of security vulnerabilities.
|
|
14
|
+
If you find something suspicious, we encourage and appreciate your report!
|
|
15
|
+
|
|
16
|
+
### Ways to report
|
|
17
|
+
|
|
18
|
+
In order for the vulnerability reports to reach maintainers as soon as possible, the preferred way is to use the "Report a vulnerability" button under the "Security" tab of the associated GitHub project.
|
|
19
|
+
This creates a private communication channel between the reporter and the maintainers.
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
declined, etc.
|
|
21
|
+
> This document was left empty for a long time as I didn't really want to expose my identity and most of my projects originate as projects from my personal instance of gitlabs. But I also understand that this is something people expect as a bare minimum from a dep that claims to be more security centric.
|
|
22
|
+
> Credits to [honeycomb](https://raw.githubusercontent.com/honeycombio/examples/refs/heads/main/SECURITY.md) for the security markdown file.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lacewing/extension`: the policy Lacewing already enforces, for packages
|
|
3
|
+
* that build on it and must not drift from it.
|
|
4
|
+
*
|
|
5
|
+
* Covered by semver like the root export. Everything here is read-only:
|
|
6
|
+
* registry entries are frozen, and nothing that adds to the registry
|
|
7
|
+
* (`registerLegacyAlgorithm`) or weakens a check is reachable from this
|
|
8
|
+
* path. Header validation (`validateHeader`) is deliberately absent too:
|
|
9
|
+
* it refuses an embedded `jwk`, which is right for a token and wrong for
|
|
10
|
+
* any format that carries its own key, so each format owns its header
|
|
11
|
+
* rules.
|
|
12
|
+
*
|
|
13
|
+
* `parseJsonObject` is the strict JSON reading a token's header and payload
|
|
14
|
+
* get: a plain object, and no member name twice at any depth. A format that
|
|
15
|
+
* reads JSON out of a JWS reads it the same way, so the two cannot disagree
|
|
16
|
+
* about which of two `alg` or `htu` members counts.
|
|
17
|
+
*/
|
|
18
|
+
export { getAlgorithmProperties } from "./lib/algorithms.js";
|
|
19
|
+
export { parseJsonObject } from "./lib/json.js";
|
|
20
|
+
export { parseDuration } from "./lib/duration.js";
|
|
21
|
+
export { readHeaderValue } from "./http/source.js";
|
|
22
|
+
//# sourceMappingURL=extension.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.js","sourceRoot":"","sources":["../../src/extension.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,sBAAsB,EAAsB,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAqB,MAAM,kBAAkB,CAAC"}
|
package/dist/src/key/import.js
CHANGED
|
@@ -12,7 +12,8 @@ import { validateHMACSecret } from "../lib/entropy.js";
|
|
|
12
12
|
import { decodeBase64url } from "../lib/base64url.js";
|
|
13
13
|
import { createLacewingKey, SetAlg, } from "../types.js";
|
|
14
14
|
function expectedWebCryptoName(alg, info) {
|
|
15
|
-
|
|
15
|
+
// Both `EdDSA` and `Ed25519` name the one OKP curve in the registry.
|
|
16
|
+
if (info.kty === "OKP")
|
|
16
17
|
return "Ed25519";
|
|
17
18
|
if (info.kty === "EC")
|
|
18
19
|
return "ECDSA";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../../src/key/import.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAC1D,OAAO,EACN,kBAAkB,EAClB,eAAe,EACf,eAAe,GACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,sBAAsB,EAAsB,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EACN,iBAAiB,EACjB,MAAM,GAGN,MAAM,aAAa,CAAC;AAKrB,SAAS,qBAAqB,CAAC,GAAW,EAAE,IAAmB;IAC9D,IAAI,GAAG,KAAK,
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../../src/key/import.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,11 +6,18 @@
|
|
|
6
6
|
* one-entry change here (cryptographic agility, §3.2.4). Legacy interop
|
|
7
7
|
* algorithms can only enter through `registerLegacyAlgorithm`, which is
|
|
8
8
|
* reachable exclusively via the explicit `legacy/` imports.
|
|
9
|
+
*
|
|
10
|
+
* Entries are frozen because `getAlgorithmProperties` is public through
|
|
11
|
+
* `lacewing/extension`: a mutable entry handed to another package would let
|
|
12
|
+
* it lower `minKeyBits` for every key Lacewing imports afterwards.
|
|
9
13
|
*/
|
|
10
14
|
import { AlgorithmNotAllowed } from "../util/errors.js";
|
|
11
15
|
import { SetAlg } from "../types.js";
|
|
12
16
|
const CORE_ALGORITHMS = [
|
|
13
17
|
{ name: "EdDSA", kty: "OKP", crv: "Ed25519", minKeyBits: 256 },
|
|
18
|
+
// RFC 9864's fully-specified name for the same Ed25519 signature. Some
|
|
19
|
+
// clients (oauth4webapi among them) only ever write this one.
|
|
20
|
+
{ name: "Ed25519", kty: "OKP", crv: "Ed25519", minKeyBits: 256 },
|
|
14
21
|
{ name: "ES256", kty: "EC", crv: "P-256", minKeyBits: 256 },
|
|
15
22
|
{ name: "ES384", kty: "EC", crv: "P-384", minKeyBits: 384 },
|
|
16
23
|
{ name: "ES512", kty: "EC", crv: "P-521", minKeyBits: 521 },
|
|
@@ -22,7 +29,7 @@ const CORE_ALGORITHMS = [
|
|
|
22
29
|
{ name: "HS384", kty: "oct", minKeyBits: 384 },
|
|
23
30
|
{ name: "HS512", kty: "oct", minKeyBits: 512 },
|
|
24
31
|
];
|
|
25
|
-
const registry = new Map(CORE_ALGORITHMS.map((info) => [info.name, info]));
|
|
32
|
+
const registry = new Map(CORE_ALGORITHMS.map((info) => [info.name, Object.freeze(info)]));
|
|
26
33
|
/** Type guard: is this exact string an allowed algorithm? Case sensitive. */
|
|
27
34
|
export function isValidAlgorithm(alg) {
|
|
28
35
|
return registry.has(alg);
|
|
@@ -54,7 +61,7 @@ export function registerLegacyAlgorithm(info) {
|
|
|
54
61
|
throw new AlgorithmNotAllowed("Algorithm 'none' is not allowed");
|
|
55
62
|
}
|
|
56
63
|
if (!registry.has(info.name)) {
|
|
57
|
-
registry.set(info.name, info);
|
|
64
|
+
registry.set(info.name, Object.freeze({ ...info }));
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
//# sourceMappingURL=algorithms.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"algorithms.js","sourceRoot":"","sources":["../../../src/lib/algorithms.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"algorithms.js","sourceRoot":"","sources":["../../../src/lib/algorithms.ts"],"names":[],"mappings":"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"}
|
package/dist/src/lib/duration.js
CHANGED
|
@@ -14,6 +14,9 @@ export function parseDuration(value) {
|
|
|
14
14
|
if (typeof value === "number") {
|
|
15
15
|
return toSeconds(value);
|
|
16
16
|
}
|
|
17
|
+
if (typeof value !== "string") {
|
|
18
|
+
throw new TypeError("Duration must be a number of seconds or a string like \"15m\"");
|
|
19
|
+
}
|
|
17
20
|
const match = DURATION.exec(value.trim());
|
|
18
21
|
if (match === null) {
|
|
19
22
|
throw new TypeError(`Invalid duration "${value}" - use seconds or "30s" / "15m" / "1h" / "7d"`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duration.js","sourceRoot":"","sources":["../../../src/lib/duration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAwB,MAAM,aAAa,CAAC;AAE9D,MAAM,QAAQ,GAAG,sBAAsB,CAAC;AAExC,MAAM,UAAU,GAA2B;IAC1C,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,KAAK;CACR,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,KAAsB;IACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1C,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAClB,qBAAqB,KAAK,gDAAgD,CAC1E,CAAC;IACH,CAAC;IACD,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;IAC/B,OAAO,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,GAAI,UAAU,CAAC,IAAc,CAAY,CAAC,CAAC;AAC3E,CAAC"}
|
|
1
|
+
{"version":3,"file":"duration.js","sourceRoot":"","sources":["../../../src/lib/duration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAwB,MAAM,aAAa,CAAC;AAE9D,MAAM,QAAQ,GAAG,sBAAsB,CAAC;AAExC,MAAM,UAAU,GAA2B;IAC1C,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,IAAI;IACP,CAAC,EAAE,KAAK;CACR,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,KAAsB;IACnD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,IAAI,SAAS,CAAC,+DAA+D,CAAC,CAAC;IACtF,CAAC;IACD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1C,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAClB,qBAAqB,KAAK,gDAAgD,CAC1E,CAAC;IACH,CAAC;IACD,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;IAC/B,OAAO,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,GAAI,UAAU,CAAC,IAAc,CAAY,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lacewing/extension`: the policy Lacewing already enforces, for packages
|
|
3
|
+
* that build on it and must not drift from it.
|
|
4
|
+
*
|
|
5
|
+
* Covered by semver like the root export. Everything here is read-only:
|
|
6
|
+
* registry entries are frozen, and nothing that adds to the registry
|
|
7
|
+
* (`registerLegacyAlgorithm`) or weakens a check is reachable from this
|
|
8
|
+
* path. Header validation (`validateHeader`) is deliberately absent too:
|
|
9
|
+
* it refuses an embedded `jwk`, which is right for a token and wrong for
|
|
10
|
+
* any format that carries its own key, so each format owns its header
|
|
11
|
+
* rules.
|
|
12
|
+
*
|
|
13
|
+
* `parseJsonObject` is the strict JSON reading a token's header and payload
|
|
14
|
+
* get: a plain object, and no member name twice at any depth. A format that
|
|
15
|
+
* reads JSON out of a JWS reads it the same way, so the two cannot disagree
|
|
16
|
+
* about which of two `alg` or `htu` members counts.
|
|
17
|
+
*/
|
|
18
|
+
export { getAlgorithmProperties, type AlgorithmInfo } from "./lib/algorithms.js";
|
|
19
|
+
export { parseJsonObject } from "./lib/json.js";
|
|
20
|
+
export { parseDuration } from "./lib/duration.js";
|
|
21
|
+
export { readHeaderValue, type HeaderSource } from "./http/source.js";
|
|
22
|
+
//# sourceMappingURL=extension.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extension.d.ts","sourceRoot":"","sources":["../../../src/extension.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,sBAAsB,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../../../src/key/import.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,EAGN,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"import.d.ts","sourceRoot":"","sources":["../../../../src/key/import.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAWH,OAAO,EAGN,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;AAwHtE;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,KAAK,CAAC,GAAG,SAAS,MAAM,EACvD,QAAQ,EAAE,WAAW,EACrB,SAAS,EAAE,GAAG,GACZ,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAoB3B"}
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
* one-entry change here (cryptographic agility, §3.2.4). Legacy interop
|
|
7
7
|
* algorithms can only enter through `registerLegacyAlgorithm`, which is
|
|
8
8
|
* reachable exclusively via the explicit `legacy/` imports.
|
|
9
|
+
*
|
|
10
|
+
* Entries are frozen because `getAlgorithmProperties` is public through
|
|
11
|
+
* `lacewing/extension`: a mutable entry handed to another package would let
|
|
12
|
+
* it lower `minKeyBits` for every key Lacewing imports afterwards.
|
|
9
13
|
*/
|
|
10
14
|
import { type ValidAlg } from "../types.js";
|
|
11
15
|
export interface AlgorithmInfo {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"algorithms.d.ts","sourceRoot":"","sources":["../../../../src/lib/algorithms.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"algorithms.d.ts","sourceRoot":"","sources":["../../../../src/lib/algorithms.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEpD,MAAM,WAAW,aAAa;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8CAA8C;IAC9C,QAAQ,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,GAAG,KAAK,GAAG,KAAK,CAAC;IAC3C,yCAAyC;IACzC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC5B;AAuBD,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAErD;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAOjE;AAED,8EAA8E;AAC9E,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAGhD;AAED,wEAAwE;AACxE,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAOjE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../../../../src/lib/duration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAW9D,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../../../../src/lib/duration.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAa,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAW9D,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,eAAe,CAerE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lacewing",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "An opinionated JWT library that makes RFC 8725 (JWT Best Current Practices) the default behavior, not an optional configuration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"types": "./dist/types/index.d.ts",
|
|
14
14
|
"default": "./dist/index.js"
|
|
15
15
|
},
|
|
16
|
+
"./extension": {
|
|
17
|
+
"types": "./dist/types/src/extension.d.ts",
|
|
18
|
+
"default": "./dist/src/extension.js"
|
|
19
|
+
},
|
|
16
20
|
"./legacy/rs256": {
|
|
17
21
|
"types": "./dist/types/src/legacy/rs256.d.ts",
|
|
18
22
|
"default": "./dist/src/legacy/rs256.js"
|
|
@@ -28,6 +32,7 @@
|
|
|
28
32
|
"lint": "eslint .",
|
|
29
33
|
"lint:fix": "eslint . --fix",
|
|
30
34
|
"test": "node --import tsx --test \"tests/**/*.test.ts\"",
|
|
35
|
+
"test:dist": "npm run build && node --test \"tests/dist/*.test.js\"",
|
|
31
36
|
"test:tap": "node --import tsx --test --test-reporter=tap \"tests/**/*.test.ts\"",
|
|
32
37
|
"compliance": "node --import tsx tools/run-compliance.ts",
|
|
33
38
|
"demo": "node --import tsx examples/demo.ts",
|
|
@@ -51,20 +56,20 @@
|
|
|
51
56
|
"url": "git+https://github.com/grMLEqomlkkU5Eeinz4brIrOVCUCkJuN/lacewing.git"
|
|
52
57
|
},
|
|
53
58
|
"devDependencies": {
|
|
54
|
-
"@commitlint/cli": "^21.2.
|
|
55
|
-
"@commitlint/config-conventional": "^21.2.
|
|
59
|
+
"@commitlint/cli": "^21.2.2",
|
|
60
|
+
"@commitlint/config-conventional": "^21.2.2",
|
|
56
61
|
"@eslint/js": "^10.0.1",
|
|
57
|
-
"@types/node": "^26.
|
|
58
|
-
"eslint": "^10.
|
|
62
|
+
"@types/node": "^26.2.0",
|
|
63
|
+
"eslint": "^10.9.0",
|
|
59
64
|
"fast-check": "^4.9.0",
|
|
60
65
|
"git-cliff": "^2.13.1",
|
|
61
66
|
"lefthook": "^2.1.10",
|
|
62
|
-
"tsx": "^4.23.
|
|
67
|
+
"tsx": "^4.23.12",
|
|
63
68
|
"typedoc": "^0.28.20",
|
|
64
69
|
"typescript": "^6.0.3",
|
|
65
|
-
"typescript-eslint": "^8.
|
|
70
|
+
"typescript-eslint": "^8.67.0"
|
|
66
71
|
},
|
|
67
72
|
"dependencies": {
|
|
68
|
-
"jose": "^6.2.
|
|
73
|
+
"jose": "^6.2.10"
|
|
69
74
|
}
|
|
70
75
|
}
|