From 26a7024af1636a55ef3176f7b1e84e6bdfe0e954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?N=C3=A9stor?= Date: Fri, 2 Oct 2026 13:45:34 +0200 Subject: [PATCH] fix(deps): upgrade selfsigned to v5 to drop node-forge --- README.md | 4 +- package.json | 2 +- pnpm-lock.yaml | 224 ++++++++++++++++++++++++++++++++++++++++++++++--- src/index.ts | 9 +- src/util.ts | 30 ++++--- 5 files changed, 244 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 2be1d6f..daefa2f 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,9 @@ pluginBasicSsl({ Options passing to `selfsigned`, see [selfsigned - Options](https://github.com/jfromaniello/selfsigned?tab=readme-ov-file#options) for details. -- **Type:** `SelfsignedOptions` +In addition, the plugin supports a `days` option to set the validity period of the certificate. It is converted to `notAfterDate` and ignored if `notAfterDate` is set. + +- **Type:** `SelfsignedOptions & { days?: number }` - **Default:** ```ts diff --git a/package.json b/package.json index c17e612..481f7fe 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "test": "rs test" }, "dependencies": { - "selfsigned": "^3.0.1" + "selfsigned": "^5.5.0" }, "devDependencies": { "@rsbuild/core": "2.2.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1867c87..c355ff2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -167,8 +167,8 @@ importers: .: dependencies: selfsigned: - specifier: ^3.0.1 - version: 3.0.1 + specifier: ^5.5.0 + version: 5.5.0 devDependencies: '@rsbuild/core': specifier: 2.2.6 @@ -268,6 +268,57 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@noble/hashes@1.8.0': + resolution: {integrity: sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==} + engines: {node: ^14.21.3 || >=16} + + '@peculiar/asn1-cms@2.10.0': + resolution: {integrity: sha512-CkX0H4NCIOMHOU3rh2xXZywinYZ/EnJlaOlJiGI0e5L4XjFqHf4iH30LMbXWChVppdA9ljbanjtzQFOhDNfTWg==} + engines: {node: '>=14'} + + '@peculiar/asn1-csr@2.10.0': + resolution: {integrity: sha512-jTPTr/9rxKM+niQLMF3jiAMb0lWHUhUHIuSOhdGCIfpB4FAQYf9SoiXOgxP8bS/68IB+bj+1OHvVnDVCQfVUZw==} + engines: {node: '>=14'} + + '@peculiar/asn1-ecc@2.10.0': + resolution: {integrity: sha512-GFd3iOjFrWX+QWH2R2dO5QSJyyRGv9CIBKtRlGlPNCvb2RvmCbBDexe91MJgV76c69d998Xsa+CvuQ98seoiPg==} + engines: {node: '>=14'} + + '@peculiar/asn1-pfx@2.10.0': + resolution: {integrity: sha512-1y3QK9ZH1IPleAMmRoJlPS10eGkAWnULETW8zDFNUK1ffqpG7zmDY+kYBMYQgwlcQZ2iitLm2z2EBP0xzRXpaA==} + engines: {node: '>=14'} + + '@peculiar/asn1-pkcs8@2.10.0': + resolution: {integrity: sha512-Ri+BZT9bnwqlHWmhs7lvWjvJRxKev2hA2+4EbZajgeYWCbR8hyv0znF4DhsFouOhMj2nSDV/iGJ6DMQtwITnCw==} + engines: {node: '>=14'} + + '@peculiar/asn1-pkcs9@2.10.0': + resolution: {integrity: sha512-XIXsbDQFezYk6fudczkuvawkRD4GNpISGCqfYhdkJlvcGF/RFknU+0DDJagOaJqBf+PdPzk4J9oMqDGcvfR9HQ==} + engines: {node: '>=14'} + + '@peculiar/asn1-rsa@2.10.0': + resolution: {integrity: sha512-4Jvmwlh3gZAhNZ4/u7JSyLuce9hofOFZ4o3PYKAccCImGnyaT5CMym/ATgvAvqpOYFNW531oPZvJlEBhJy9VfA==} + engines: {node: '>=14'} + + '@peculiar/asn1-schema@2.10.0': + resolution: {integrity: sha512-GhokD41lV4gQrrLYm3wCkHfBOnJrnhDMgt4XeMW8gzfE1UdJqIuSwsE+ggf82XBjUXRJylcm+KIGQFa4utIVLw==} + engines: {node: '>=14'} + + '@peculiar/asn1-x509-attr@2.10.0': + resolution: {integrity: sha512-/85GtKOKmgvuSJNlaFfwGWNdRSZZ+hpF02NyM01XiCdzpaZONiBltDyfluFPvFx966CR+ZHNSG1jniwpy07oGg==} + engines: {node: '>=14'} + + '@peculiar/asn1-x509@2.10.0': + resolution: {integrity: sha512-ucNVg8+ANveTpMN3fy9lA2alryONdXc2A4cEG2hMniWbvQt+YOZoe8BI80YYNO8FBcuDY1qbDQ4uQGQVraHxGA==} + engines: {node: '>=14'} + + '@peculiar/utils@2.0.3': + resolution: {integrity: sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==} + + '@peculiar/x509@1.14.3': + resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==} + engines: {node: '>=20.0.0'} + '@rsbuild/core@2.2.6': resolution: {integrity: sha512-+BoNmacdQ2j9ysUunVUm2CTK7Aff70l4P/m29R5AWjd2vCU+X7bo6u3geMMJV0+j/JUHzoccC1ERIEhzqnD3Xg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -746,18 +797,26 @@ packages: '@yuku-toolchain/types@0.9.5': resolution: {integrity: sha512-KiuLNNgX9uNealaWAR+G3/cMXnRk9x4TY2EkYe/KIag+UPdwiA0RRf1hr1WAxzTP8KGzCTkqUdLPvqh32sEO3w==} + asn1js@3.0.10: + resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==} + engines: {node: '>=12.0.0'} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - node-forge@1.3.1: - resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} - engines: {node: '>= 6.13.0'} + bytestreamjs@2.0.1: + resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} + engines: {node: '>=6.0.0'} picomatch@4.0.5: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} + pkijs@3.4.1: + resolution: {integrity: sha512-Oo/NZcSWccq8KyoG7gLE9fnltgHns+pNCjCAp/WmjsUySi+sX7y4z4Xqu4fVb42CDHzRPl33fjzT15V1wvcyhA==} + engines: {node: '>=16.0.0'} + playwright-core@1.63.0: resolution: {integrity: sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==} engines: {node: '>=20'} @@ -773,6 +832,16 @@ packages: engines: {node: '>=14'} hasBin: true + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.2.0: + resolution: {integrity: sha512-BbubeCEyTuQjVMakvJQ/Sxbc93F2pwmbsxONT/ZRrwU7Ua38d8unYTwXpTVLAKJ4BDuH9IGztCjQcd/N/39Dvg==} + engines: {node: '>=16.0.0'} + + reflect-metadata@0.2.2: + resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} + rsbuild-plugin-dts@1.0.0: resolution: {integrity: sha512-FVoTsiTfSc0LPeSjrVhpvFOBRig0YXJq3Hp+bVxzh9/bzFvODzLRWOEux8q1Phe1oVCIIVSc2+Nb7kDcxIhosw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -796,17 +865,24 @@ packages: '@rspress/core': optional: true - selfsigned@3.0.1: - resolution: {integrity: sha512-6U6w6kSLrM9Zxo0D7mC7QdGS6ZZytMWBnj/vhF9p+dAHx6CwGezuRcO4VclTbrrI7mg7SD6zNiqXUuBHOVopNQ==} - engines: {node: '>=10'} + selfsigned@5.5.0: + resolution: {integrity: sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==} + engines: {node: '>=18'} tinypool@2.1.2: resolution: {integrity: sha512-9YodfrxS9g9IbFr/KOjE5bAeJ0p61n3bW6mqvy0jtoeKd1kTW1Cxm0oulm6KX2lyM9Gl6WIe8nEbY7LWv5ZJww==} engines: {node: ^20.0.0 || >=22.0.0} + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsyringe@4.10.0: + resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} + engines: {node: '>= 6.0.0'} + typescript@7.0.2: resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} engines: {node: '>=16.20.0'} @@ -885,6 +961,102 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true + '@noble/hashes@1.8.0': {} + + '@peculiar/asn1-cms@2.10.0': + dependencies: + '@peculiar/asn1-schema': 2.10.0 + '@peculiar/asn1-x509': 2.10.0 + '@peculiar/asn1-x509-attr': 2.10.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-csr@2.10.0': + dependencies: + '@peculiar/asn1-schema': 2.10.0 + '@peculiar/asn1-x509': 2.10.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.10.0': + dependencies: + '@peculiar/asn1-schema': 2.10.0 + '@peculiar/asn1-x509': 2.10.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pfx@2.10.0': + dependencies: + '@peculiar/asn1-cms': 2.10.0 + '@peculiar/asn1-pkcs8': 2.10.0 + '@peculiar/asn1-rsa': 2.10.0 + '@peculiar/asn1-schema': 2.10.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs8@2.10.0': + dependencies: + '@peculiar/asn1-schema': 2.10.0 + '@peculiar/asn1-x509': 2.10.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs9@2.10.0': + dependencies: + '@peculiar/asn1-cms': 2.10.0 + '@peculiar/asn1-pfx': 2.10.0 + '@peculiar/asn1-pkcs8': 2.10.0 + '@peculiar/asn1-schema': 2.10.0 + '@peculiar/asn1-x509': 2.10.0 + '@peculiar/asn1-x509-attr': 2.10.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.10.0': + dependencies: + '@peculiar/asn1-schema': 2.10.0 + '@peculiar/asn1-x509': 2.10.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.10.0': + dependencies: + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509-attr@2.10.0': + dependencies: + '@peculiar/asn1-schema': 2.10.0 + '@peculiar/asn1-x509': 2.10.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.10.0': + dependencies: + '@peculiar/asn1-schema': 2.10.0 + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/utils@2.0.3': + dependencies: + tslib: 2.8.1 + + '@peculiar/x509@1.14.3': + dependencies: + '@peculiar/asn1-cms': 2.10.0 + '@peculiar/asn1-csr': 2.10.0 + '@peculiar/asn1-ecc': 2.10.0 + '@peculiar/asn1-pkcs9': 2.10.0 + '@peculiar/asn1-rsa': 2.10.0 + '@peculiar/asn1-schema': 2.10.0 + '@peculiar/asn1-x509': 2.10.0 + pvtsutils: 1.3.6 + reflect-metadata: 0.2.2 + tslib: 2.8.1 + tsyringe: 4.10.0 + '@rsbuild/core@2.2.6': dependencies: '@rspack/core': 2.2.4(@swc/helpers@0.5.23) @@ -1178,12 +1350,27 @@ snapshots: '@yuku-toolchain/types@0.9.5': {} + asn1js@3.0.10: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.2.0 + tslib: 2.8.1 + assertion-error@2.0.1: {} - node-forge@1.3.1: {} + bytestreamjs@2.0.1: {} picomatch@4.0.5: {} + pkijs@3.4.1: + dependencies: + '@noble/hashes': 1.8.0 + asn1js: 3.0.10 + bytestreamjs: 2.0.1 + pvtsutils: 1.3.6 + pvutils: 1.2.0 + tslib: 2.8.1 + playwright-core@1.63.0: {} playwright@1.63.0: @@ -1192,6 +1379,14 @@ snapshots: prettier@3.9.6: {} + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.2.0: {} + + reflect-metadata@0.2.2: {} + rsbuild-plugin-dts@1.0.0(@rsbuild/core@2.2.6)(typescript@7.0.2): dependencies: '@ast-grep/napi': 0.45.2 @@ -1231,14 +1426,21 @@ snapshots: - jsdom - typescript - selfsigned@3.0.1: + selfsigned@5.5.0: dependencies: - node-forge: 1.3.1 + '@peculiar/x509': 1.14.3 + pkijs: 3.4.1 tinypool@2.1.2: {} + tslib@1.14.1: {} + tslib@2.8.1: {} + tsyringe@4.10.0: + dependencies: + tslib: 1.14.1 + typescript@7.0.2: optionalDependencies: '@typescript/typescript-aix-ppc64': 7.0.2 diff --git a/src/index.ts b/src/index.ts index dc17f97..32d3264 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,7 +22,14 @@ export type PluginBasicSslOptions = { /** * Options passing to `selfsigned`. */ - selfsignedOptions?: SelfsignedOptions; + selfsignedOptions?: SelfsignedOptions & { + /** + * Validity period of the certificate in days. + * Ignored if `notAfterDate` is set. + * @default 30 + */ + days?: number; + }; }; export const pluginBasicSsl = ( diff --git a/src/util.ts b/src/util.ts index 4152ab7..8ae7f5e 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,3 +1,4 @@ +import { X509Certificate } from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -18,6 +19,15 @@ async function ensureDir(dir: string) { } } +function isCertValid(content: string) { + try { + const { validTo } = new X509Certificate(content); + return new Date(validTo).getTime() > Date.now(); + } catch { + return false; + } +} + export const resolveHttpsConfig = async ( config: HttpsConfig, options: PluginBasicSslOptions, @@ -36,22 +46,20 @@ export const resolveHttpsConfig = async ( options.filename ?? 'fake-cert.pem', ); + const { days = 30, ...restOptions } = options.selfsignedOptions ?? {}; const selfsignedOptions = { - days: 30, keySize: 2048, - ...options.selfsignedOptions, + notAfterDate: new Date(Date.now() + days * 24 * 60 * 60 * 1000), + ...restOptions, }; if (fs.existsSync(certPath)) { - const stats = await fs.promises.stat(certPath); - const timeDiff = Date.now() - stats.mtimeMs; - const daysDiff = timeDiff / (1000 * 60 * 60 * 24); + const content = await fs.promises.readFile(certPath, { + encoding: 'utf-8', + }); - // Default validity period is 30 days - if (daysDiff < selfsignedOptions.days) { - const content = await fs.promises.readFile(certPath, { - encoding: 'utf-8', - }); + // Reuse the cached certificate until it expires + if (isCertValid(content)) { return { key: content, cert: content, @@ -59,7 +67,7 @@ export const resolveHttpsConfig = async ( } } - const pem = selfsigned.generate( + const pem = await selfsigned.generate( options.selfsignedAttrs ?? [{ name: 'commonName', value: 'localhost' }], selfsignedOptions, );