@nodefony/http 10.0.0-beta.1 → 10.0.0-beta.2

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.
Files changed (33) hide show
  1. package/README.md +5 -3
  2. package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorate.js +1 -1
  3. package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorateMetadata.js +1 -1
  4. package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorateParam.js +1 -1
  5. package/dist/index.js +2 -2
  6. package/dist/nodefony/command/certificatesCommand.js +0 -1
  7. package/dist/nodefony/config/config.js +4 -3
  8. package/dist/nodefony/service/certificates.js +74 -143
  9. package/dist/nodefony/service/http-kernel.js +2 -2
  10. package/dist/nodefony/service/servers/server-http.js +3 -3
  11. package/dist/nodefony/service/servers/server-https.js +3 -3
  12. package/dist/nodefony/service/servers/server-websocket-secure.js +3 -3
  13. package/dist/nodefony/service/servers/server-websocket.js +3 -3
  14. package/dist/nodefony/service/sessions/sessions-service.js +3 -3
  15. package/dist/nodefony/service/trace.js +2 -1
  16. package/dist/nodefony/service/upload/upload-service.js +3 -3
  17. package/dist/nodefony/service/x509.js +324 -0
  18. package/dist/nodefony/src/context/http/Request.js +17 -4
  19. package/dist/nodefony/src/context/http/Response.js +30 -6
  20. package/dist/nodefony/src/context/http/urlFastPath.js +35 -10
  21. package/dist/nodefony/src/servers/portBinder.js +57 -2
  22. package/dist/types/nodefony/config/config.d.ts +1 -0
  23. package/dist/types/nodefony/service/certificates.d.ts +32 -47
  24. package/dist/types/nodefony/service/x509.d.ts +86 -0
  25. package/dist/types/nodefony/src/servers/portBinder.d.ts +10 -46
  26. package/docs/cookies.md +10 -10
  27. package/docs/index.md +4 -2
  28. package/docs/observabilite.md +13 -13
  29. package/docs/rate-limit.md +19 -19
  30. package/docs/servers.md +50 -47
  31. package/docs/session.md +17 -17
  32. package/docs/upload.md +29 -29
  33. package/package.json +7 -10
package/README.md CHANGED
@@ -6,7 +6,7 @@ pipeline de contexte/controller.
6
6
 
7
7
  > Docs IA (dans le dépôt) : [`CLAUDE.md`](https://github.com/nodefony/nodefony-core/blob/main/src/packages/@nodefony/http/CLAUDE.md) · [`MEMORY.md`](https://github.com/nodefony/nodefony-core/blob/main/src/packages/@nodefony/http/MEMORY.md) · [`docs/`](./docs).
8
8
  > Règle dure : ce module ne peut PAS importer `@nodefony/framework` (cycle) — accès au resolver
9
- > via `(context as any)?.resolver`.
9
+ > via `context.resolver`, typé par le contrat `IRouteResolver` que ce module définit.
10
10
 
11
11
  ## Serveurs
12
12
 
@@ -26,11 +26,13 @@ serveur → http-kernel.handle() → rate-limit IP → createContext()
26
26
  → Router.resolve() (match avant le parse du body)
27
27
  → Firewall.applySecurityHeaders() → fallback statique si aucune route
28
28
  → parse du body
29
- → handleFrontController() (Resolver.resolve → controller + initialize())
29
+ → prepareFrontController() (route + zone armées, AUCUNE instanciation)
30
30
  → Firewall.enforceCsrf() → startSession() → Firewall.handleSecurity()
31
- → action du controller → Response.send()
31
+ → @IsGranted → instanciation DI + initialize() → action du controller → Response.send()
32
32
  ```
33
33
 
34
+ En WebSocket, le contrôleur est instancié au handshake (`handleFrontController()`), avant le pare-feu.
35
+
34
36
  Chaque contexte (HTTP + WS) porte un `requestId` (UUID v4, ou `X-Request-Id` client),
35
37
  réinjecté dans la réponse et corrélé dans les logs via AsyncLocalStorage.
36
38
 
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorate.js
1
+ //#region \0@oxc-project+runtime@0.152.0/helpers/esm/decorate.js
2
2
  function __decorate(decorators, target, key, desc) {
3
3
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
4
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorateMetadata.js
1
+ //#region \0@oxc-project+runtime@0.152.0/helpers/esm/decorateMetadata.js
2
2
  function __decorateMetadata(k, v) {
3
3
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
4
4
  }
@@ -1,4 +1,4 @@
1
- //#region \0@oxc-project+runtime@0.151.0/helpers/esm/decorateParam.js
1
+ //#region \0@oxc-project+runtime@0.152.0/helpers/esm/decorateParam.js
2
2
  function __decorateParam(paramIndex, decorator) {
3
3
  return function(target, key) {
4
4
  decorator(target, key, paramIndex);
package/dist/index.js CHANGED
@@ -21,8 +21,8 @@ import DefaultErrorRenderer, { schemaMismatchOf } from "./nodefony/service/error
21
21
  import DefaultRequestLogger from "./nodefony/service/request-logger.js";
22
22
  import JsonAuditLogger from "./nodefony/service/audit-logger.js";
23
23
  import PrettyRequestLogger from "./nodefony/service/pretty-request-logger.js";
24
- import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.js";
25
- import __decorate from "./_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.js";
24
+ import __decorateMetadata from "./_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js";
25
+ import __decorate from "./_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorate.js";
26
26
  import http_kernel_default from "./nodefony/service/http-kernel.js";
27
27
  import server_http_default from "./nodefony/service/servers/server-http.js";
28
28
  import server_https_default from "./nodefony/service/servers/server-https.js";
@@ -26,7 +26,6 @@ var Certificates = class extends Command {
26
26
  this.log("Service 'certificates' introuvable (module @nodefony/http non chargé).", "ERROR");
27
27
  return this;
28
28
  }
29
- await service.loadForge();
30
29
  await service.generateServerCertificates(Boolean(opts.force));
31
30
  const info = await service.describe();
32
31
  if (opts.json) process.stdout.write(`${JSON.stringify(info, void 0, 2)}\n`);
@@ -159,8 +159,9 @@ const http3Schema = z.looseObject({});
159
159
  const selfSignedAttrSchema = z.looseObject({
160
160
  name: z.string().optional(),
161
161
  value: z.string().optional(),
162
- shortName: z.string().optional()
163
- }).describe("Champ de sujet/issuer node-forge (CertificateField).");
162
+ shortName: z.string().optional(),
163
+ type: z.string().optional()
164
+ }).describe("Attribut du sujet/émetteur : `name` (commonName…), `shortName` (CN…) ou `type` (OID), et `value`.");
164
165
  const selfSignedSchema = z.strictObject({
165
166
  size: z.number().int().positive().default(2048).describe("Taille de la clé RSA (bits). 2048 minimum, 4096 recommandé en prod."),
166
167
  hash: z.enum([
@@ -174,7 +175,7 @@ const selfSignedSchema = z.strictObject({
174
175
  kernelDerived: true,
175
176
  description: "Attributs du certificat (commonName, organizationName…). Vide (défaut) = dérivés du kernel par le builder (commonName ← domain)."
176
177
  })
177
- }).describe("Génération du certificat AUTO-SIGNÉ (node-forge, JavaScript pur — aucun binaire externe n'est invoqué). Ces réglages ne valent QUE pour la stratégie `selfsigned` : sous `mkcert` (défaut en développement) ils sont ignorés — mkcert ne reçoit que les noms d'hôtes — et sous `strategy: \"explicit\"` le certificat est fourni, donc rien n'est généré.");
178
+ }).describe("Génération du certificat AUTO-SIGNÉ (node:crypto — aucun binaire externe ni dépendance tierce). Ces réglages ne valent QUE pour la stratégie `selfsigned` : sous `mkcert` (défaut en développement) ils sont ignorés — mkcert ne reçoit que les noms d'hôtes — et sous `strategy: \"explicit\"` le certificat est fourni, donc rien n'est généré.");
178
179
  const sanSchema = z.strictObject({
179
180
  dns: z.array(z.string()).default([]).meta({
180
181
  kernelDerived: true,
@@ -1,12 +1,14 @@
1
1
  import { asServersConfig } from "../src/servers/kernelServers.js";
2
+ import { SHA1_WITH_RSA_OID, createSelfSignedCertificate, distinguishedNameField, parseSubjectAltName, signatureAlgorithmName, signatureAlgorithmOid } from "./x509.js";
2
3
  import { Service, extend } from "nodefony";
3
- import { randomBytes } from "node:crypto";
4
+ import { X509Certificate, generateKeyPair, randomBytes } from "node:crypto";
4
5
  import fsp from "node:fs/promises";
5
6
  import path, { resolve } from "node:path";
6
7
  import { promisify } from "node:util";
7
8
  import { execFile } from "node:child_process";
8
9
  //#region nodefony/service/certificates.ts
9
10
  const execFileAsync = promisify(execFile);
11
+ const generateKeyPairAsync = promisify(generateKeyPair);
10
12
  /**
11
13
  * Vrai si `host` est une IP littérale (IPv4 `n.n.n.n` ou IPv6 — contient `:`).
12
14
  * Une IP doit aller en SAN `iPAddress`, jamais en `dNSName` (RFC 6125).
@@ -32,20 +34,11 @@ const defaultOptions = {
32
34
  }
33
35
  };
34
36
  /**
35
- * Valeur texte d'un champ de certificat node-forge (`getField` rend `any`),
36
- * ou `null` si le champ est absent.
37
- */
38
- function certFieldValue(field) {
39
- if (field === null || typeof field !== "object") return null;
40
- const value = field.value;
41
- return typeof value === "string" ? value : String(value);
42
- }
43
- /**
44
37
  * Service de fourniture du certificat TLS du serveur HTTPS/WSS.
45
38
  *
46
39
  * Trois stratégies : `explicit` (certificat fourni en config — le cas de
47
40
  * PRODUCTION), `mkcert` (CA locale trustée, confort de dev) et `selfsigned`
48
- * (auto-signé node-forge, secours). La génération est réservée au
41
+ * (auto-signé, secours). La génération est réservée au
49
42
  * DÉVELOPPEMENT : en production sans certificat fourni, le service crie un
50
43
  * avertissement (Nodefony n'est pas une autorité de certification).
51
44
  *
@@ -58,7 +51,6 @@ var Certificate = class Certificate extends Service {
58
51
  module;
59
52
  files = [];
60
53
  keysPair = null;
61
- certForge = null;
62
54
  ca = "";
63
55
  key = "";
64
56
  cert = "";
@@ -68,13 +60,6 @@ var Certificate = class Certificate extends Service {
68
60
  intermediateCertPem = "";
69
61
  /** CAROOT résolu de mkcert (null tant que non détecté / indisponible). */
70
62
  mkcertCaRoot = null;
71
- /**
72
- * Backend crypto node-forge, chargé PARESSEUSEMENT. La génération de
73
- * certificat est un confort de DÉVELOPPEMENT : en production avec un
74
- * certificat fourni (`strategy: "explicit"`), cette grosse dépendance n'est
75
- * jamais importée (règle perf-mémoire — pas de dep chargée « au cas où »).
76
- */
77
- forge = null;
78
63
  path = resolve(".", "nodefony", "config", "certificates");
79
64
  serverPath = resolve(this.path, "server");
80
65
  caPath = resolve(this.path, "ca", "nodefony-root-ca.crt.pem");
@@ -95,20 +80,6 @@ var Certificate = class Certificate extends Service {
95
80
  return this.kernel?.environment === "development";
96
81
  }
97
82
  /**
98
- * Charge node-forge à la demande (idempotent) — uniquement sur le chemin de
99
- * GÉNÉRATION (selfsigned / mkcert / inspection). Le chemin `explicit` (prod)
100
- * ne l'appelle jamais.
101
- */
102
- async loadForge() {
103
- this.forge ??= (await import("node-forge")).default;
104
- return this.forge;
105
- }
106
- /** Accès au backend forge déjà chargé (lève si `loadForge` n'a pas été appelé). */
107
- get forgeLib() {
108
- if (!this.forge) throw new Error("node-forge non chargé — appeler loadForge() avant toute génération.");
109
- return this.forge;
110
- }
111
- /**
112
83
  * Fabrique-t-on un certificat au démarrage ?
113
84
  *
114
85
  * 🔴 Seulement si un serveur TLS est ACTIF. Sans cette question, le hook
@@ -199,7 +170,7 @@ var Certificate = class Certificate extends Service {
199
170
  /**
200
171
  * Génère (ou recharge) le certificat serveur selon la stratégie résolue :
201
172
  * `explicit` (fourni en config / prod), `mkcert` (dev, CA trustée) ou
202
- * `selfsigned` (auto-signé node-forge, secours). Régénère automatiquement si
173
+ * `selfsigned` (auto-signé, secours). Régénère automatiquement si
203
174
  * le certificat présent sur disque n'est pas adéquat pour la stratégie active
204
175
  * (expiré, SHA-1, SAN incomplet).
205
176
  *
@@ -210,17 +181,13 @@ var Certificate = class Certificate extends Service {
210
181
  await this.ensureDirectoriesExist();
211
182
  const strategy = await this.resolveStrategy();
212
183
  if (strategy === "explicit") return this.loadExplicitCert();
213
- await this.loadForge();
214
184
  if (await this.checkCertificates() && !force && await this.isCertAdequate(strategy)) return this.readCerticates();
215
185
  if (strategy === "mkcert") await this.generateWithMkcert();
216
186
  else {
217
- this.keysPair = this.generateKeys();
218
- this.certForge = this.createCertificate();
219
- this.setExtension();
220
- this.sign();
187
+ this.keysPair = await this.generateKeys();
221
188
  this.key = this.generatePrivateKeyPem();
222
189
  this.publicKeyPem = this.generatePublickeyPem();
223
- const certPem = this.generateCertPem();
190
+ const certPem = await this.createCertificate();
224
191
  this.cert = certPem;
225
192
  this.ca = certPem;
226
193
  this.fullchainPem = this.createFullChain();
@@ -236,7 +203,7 @@ var Certificate = class Certificate extends Service {
236
203
  * erreur si absents.
237
204
  * - `mkcert` : dev + binaire mkcert + CA locale présents. Forcé hors dev →
238
205
  * retombe sur `selfsigned` avec avertissement.
239
- * - `selfsigned` : auto-signé node-forge (fallback). En PRODUCTION, crie un
206
+ * - `selfsigned` : auto-signé (fallback). En PRODUCTION, crie un
240
207
  * avertissement : la génération n'est pas le rôle d'un serveur de prod.
241
208
  */
242
209
  async resolveStrategy() {
@@ -253,7 +220,7 @@ var Certificate = class Certificate extends Service {
253
220
  this.mkcertCaRoot = caRoot;
254
221
  return "mkcert";
255
222
  }
256
- this.log((requested === "mkcert" ? "strategy='mkcert' mais mkcert introuvable — " : "mkcert introuvable — ") + "fallback certificat auto-signé node-forge (non trusté). `brew install mkcert nss && mkcert -install` pour un HTTPS dev sans erreur (HMR cross-origin/WSS).", "WARNING");
223
+ this.log((requested === "mkcert" ? "strategy='mkcert' mais mkcert introuvable — " : "mkcert introuvable — ") + "fallback certificat auto-signé (non trusté). `brew install mkcert nss && mkcert -install` pour un HTTPS dev sans erreur (HMR cross-origin/WSS).", "WARNING");
257
224
  } else if (requested === "mkcert") this.log("strategy='mkcert' ignoré hors development → certificat auto-signé.", "WARNING");
258
225
  }
259
226
  if (!this.isDev()) this.log("Aucun certificat TLS fourni en PRODUCTION : génération d'un auto-signé NON trusté (secours). Nodefony n'est PAS une autorité de certification de production — fournissez un vrai certificat (Let's Encrypt, ingress k8s, reverse-proxy edge) via certificates.{ key, cert, ca } (strategy='explicit').", "WARNING");
@@ -330,8 +297,10 @@ var Certificate = class Certificate extends Service {
330
297
  await this.restrictPrivateKey();
331
298
  const rootCaPem = await fsp.readFile(path.join(caRoot, "rootCA.pem"), "utf8");
332
299
  const certPem = await fsp.readFile(this.certPath, "utf8");
333
- const { pki } = this.forgeLib;
334
- const publicKeyPem = pki.publicKeyToPem(pki.certificateFromPem(certPem).publicKey);
300
+ const publicKeyPem = new X509Certificate(certPem).publicKey.export({
301
+ type: "spki",
302
+ format: "pem"
303
+ });
335
304
  await fsp.writeFile(this.publicKeyPath, publicKeyPem, "utf8");
336
305
  await fsp.writeFile(this.fullchainPath, `${certPem}${rootCaPem}`, "utf8");
337
306
  await fsp.writeFile(this.caPath, rootCaPem, "utf8");
@@ -348,25 +317,21 @@ var Certificate = class Certificate extends Service {
348
317
  */
349
318
  async isCertAdequate(strategy) {
350
319
  try {
351
- const { pki } = this.forgeLib;
352
- const certPem = await fsp.readFile(this.certPath, "utf8");
353
- const cert = pki.certificateFromPem(certPem);
354
- if (cert.validity.notAfter.getTime() <= Date.now()) return false;
355
- const ext = cert.getExtension("subjectAltName");
356
- if (!ext || !this.sanCovers(ext.altNames ?? [])) return false;
320
+ const cert = new X509Certificate(await fsp.readFile(this.certPath));
321
+ if (cert.validToDate.getTime() <= Date.now()) return false;
322
+ if (!this.sanCovers(parseSubjectAltName(cert.subjectAltName).dns)) return false;
357
323
  if (strategy === "mkcert") {
358
- const org = certFieldValue(cert.issuer.getField("O"));
324
+ const org = distinguishedNameField(cert.issuer, "O");
359
325
  return org !== null && /mkcert/i.test(org);
360
326
  }
361
- return cert.signatureOid !== pki.oids.sha1WithRSAEncryption;
327
+ return signatureAlgorithmOid(cert.raw) !== SHA1_WITH_RSA_OID;
362
328
  } catch {
363
329
  return false;
364
330
  }
365
331
  }
366
332
  /** Le SAN présent couvre-t-il tous les noms DNS requis (RFC 6125) ? */
367
- sanCovers(present) {
368
- const presentDns = new Set(present.filter((p) => p.type === 2 && p.value).map((p) => p.value));
369
- return this.sanDnsNames().every((name) => presentDns.has(name));
333
+ sanCovers(presentDns) {
334
+ return this.sanDnsNames().every((name) => presentDns.includes(name));
370
335
  }
371
336
  /** Charge un certificat fourni en config (chemin fichier ou Buffer). */
372
337
  async loadExplicitCert() {
@@ -452,86 +417,58 @@ var Certificate = class Certificate extends Service {
452
417
  this.log(err, "DEBUG");
453
418
  }
454
419
  }
455
- generateKeys() {
456
- return this.forgeLib.pki.rsa.generateKeyPair(this.certOptions.selfSigned.size);
420
+ /** Génère la paire RSA (taille configurée) hors de la boucle d'événements. */
421
+ async generateKeys() {
422
+ return generateKeyPairAsync("rsa", { modulusLength: this.certOptions.selfSigned.size });
457
423
  }
424
+ /** Clé privée en PEM PKCS#1 (`BEGIN RSA PRIVATE KEY`). */
458
425
  generatePrivateKeyPem() {
459
- if (this.keysPair) return Buffer.from(this.forgeLib.pki.privateKeyToPem(this.keysPair.privateKey));
460
- throw new Error(`pki.rsa.KeyPair not found`);
426
+ if (this.keysPair) return Buffer.from(this.keysPair.privateKey.export({
427
+ type: "pkcs1",
428
+ format: "pem"
429
+ }));
430
+ throw new Error(`KeyPair not found`);
461
431
  }
432
+ /** Clé publique en PEM SPKI (`BEGIN PUBLIC KEY`). */
462
433
  generatePublickeyPem() {
463
- if (this.keysPair) return Buffer.from(this.forgeLib.pki.publicKeyToPem(this.keysPair.publicKey));
464
- throw new Error(`pki.rsa.KeyPair not found`);
434
+ if (this.keysPair) return Buffer.from(this.keysPair.publicKey.export({
435
+ type: "spki",
436
+ format: "pem"
437
+ }));
438
+ throw new Error(`KeyPair not found`);
465
439
  }
466
- generateCertPem() {
467
- if (this.certForge) return Buffer.from(this.forgeLib.pki.certificateToPem(this.certForge));
468
- throw new Error(`pki.Certificate not found`);
469
- }
470
- createCertificate() {
440
+ /**
441
+ * Fabrique et signe le certificat auto-signé avec la paire courante.
442
+ *
443
+ * Série aléatoire unique (RFC 5280 §4.1.2.2), `notBefore` reculé (décalage
444
+ * d'horloge du client), SAN DNS + IP (config explicite sinon dérivé), signé
445
+ * avec le hachage configuré — jamais SHA-1 (collision SHAttered 2017,
446
+ * CA/Browser Forum depuis 2016).
447
+ *
448
+ * @returns le certificat en PEM
449
+ * @throws Si la paire de clés n'a pas été générée
450
+ */
451
+ async createCertificate() {
471
452
  if (!this.keysPair) throw new Error(`KeyPair not found`);
472
453
  const o = this.certOptions.selfSigned;
473
- const cert = this.forgeLib.pki.createCertificate();
474
- cert.publicKey = this.keysPair.publicKey;
475
- cert.serialNumber = Certificate.generateSerialHex();
476
- const backdateMs = o.backdateMinutes * 6e4;
477
- const start = Date.now() - backdateMs;
478
- cert.validity.notBefore = new Date(start);
479
- cert.validity.notAfter = new Date(start + o.validityDays * 864e5);
480
- cert.setSubject(o.attrs);
481
- cert.setIssuer(o.attrs);
482
- return cert;
483
- }
484
- /** subjectAltName de l'auto-signé : DNS + IP (config explicite sinon dérivé). */
485
- altNames() {
486
- const out = [];
487
- for (const dns of this.sanDnsNames()) out.push({
488
- type: 2,
489
- value: dns
454
+ const start = Date.now() - o.backdateMinutes * 6e4;
455
+ const pem = await createSelfSignedCertificate({
456
+ privateKey: this.keysPair.privateKey,
457
+ publicKey: this.keysPair.publicKey,
458
+ serialHex: Certificate.generateSerialHex(),
459
+ notBefore: new Date(start),
460
+ notAfter: new Date(start + o.validityDays * 864e5),
461
+ attributes: o.attrs,
462
+ dns: this.sanDnsNames(),
463
+ ip: this.sanIps(),
464
+ hash: this.allowedHash(o.hash)
490
465
  });
491
- for (const ip of this.sanIps()) out.push({
492
- type: 7,
493
- ip
494
- });
495
- return out;
496
- }
497
- setExtension() {
498
- if (!this.certForge) throw new Error(`pki.Certificate not found`);
499
- this.certForge.setExtensions([
500
- {
501
- name: "basicConstraints",
502
- cA: false
503
- },
504
- {
505
- name: "keyUsage",
506
- digitalSignature: true,
507
- keyEncipherment: true
508
- },
509
- {
510
- name: "extKeyUsage",
511
- serverAuth: true,
512
- clientAuth: true
513
- },
514
- {
515
- name: "subjectAltName",
516
- altNames: this.altNames()
517
- },
518
- { name: "subjectKeyIdentifier" }
519
- ]);
520
- }
521
- /**
522
- * Signe le certificat avec le hachage configuré (SHA-256 par défaut).
523
- * SHA-1 est INTERDIT (collision SHAttered 2017, CA/Browser Forum depuis 2016) :
524
- * `node-forge` signe en SHA-1 par défaut si on ne passe pas de digest → on en
525
- * passe toujours un.
526
- */
527
- sign() {
528
- if (!this.certForge || !this.keysPair) throw new Error(`pki.rsa.KeyPair or pki.Certificate not found`);
529
- this.certForge.sign(this.keysPair.privateKey, this.digestFor(this.certOptions.selfSigned.hash));
466
+ return Buffer.from(pem);
530
467
  }
531
468
  /**
532
469
  * Résumé introspectable du certificat serveur courant — réutilisé par la
533
470
  * commande CLI `certificates` et un futur endpoint d'admin Studio (parité
534
- * CLI ↔ Web). Parse le certificat chargé (charge node-forge à la demande).
471
+ * CLI ↔ Web).
535
472
  */
536
473
  async describe() {
537
474
  const info = {
@@ -545,35 +482,29 @@ var Certificate = class Certificate extends Service {
545
482
  info.caPath = this.caPath;
546
483
  } catch {}
547
484
  if (!this.cert) return info;
548
- const { pki } = await this.loadForge();
549
485
  try {
550
- const cert = pki.certificateFromPem(this.cert.toString());
551
- info.serial = cert.serialNumber;
552
- info.validFrom = cert.validity.notBefore.toISOString();
553
- info.validTo = cert.validity.notAfter.toISOString();
554
- info.signatureAlgorithm = this.oidName(cert.signatureOid, pki);
555
- const cn = certFieldValue(cert.subject.getField("CN"));
486
+ const cert = new X509Certificate(this.cert);
487
+ info.serial = cert.serialNumber.toLowerCase();
488
+ info.validFrom = cert.validFromDate.toISOString();
489
+ info.validTo = cert.validToDate.toISOString();
490
+ info.signatureAlgorithm = signatureAlgorithmName(signatureAlgorithmOid(cert.raw));
491
+ const cn = cert.subject ? distinguishedNameField(cert.subject, "CN") : null;
556
492
  if (cn !== null) info.commonName = cn;
557
- const ext = cert.getExtension("subjectAltName");
558
- if (ext?.altNames) info.san = ext.altNames.map((a) => a.type === 7 ? a.ip ?? "" : a.value ?? "");
493
+ const san = parseSubjectAltName(cert.subjectAltName);
494
+ if (san.dns.length > 0 || san.ip.length > 0) info.san = [...san.dns, ...san.ip];
559
495
  } catch {}
560
496
  return info;
561
497
  }
562
- /** Nom lisible de l'OID d'algorithme de signature. */
563
- oidName(oid, pki) {
564
- return pki.oids[oid] ?? oid;
565
- }
566
- /** Digest node-forge correspondant au hachage configuré (jamais SHA-1). */
567
- digestFor(hash) {
568
- const { md } = this.forgeLib;
498
+ /** Hachage configuré, ramené à SHA-256 s'il sort du contrat (jamais SHA-1). */
499
+ allowedHash(hash) {
569
500
  switch (hash) {
570
- case "sha512": return md.sha512.create();
571
- case "sha384": return md.sha384.create();
572
- case "sha256": return md.sha256.create();
501
+ case "sha512":
502
+ case "sha384":
503
+ case "sha256": return hash;
573
504
  default: {
574
505
  const refused = hash;
575
506
  this.log(`Hachage '${refused}' refusé (SHA-1 interdit) → SHA-256.`, "WARNING");
576
- return md.sha256.create();
507
+ return "sha256";
577
508
  }
578
509
  }
579
510
  }
@@ -12,8 +12,8 @@ import JsonAuditLogger from "./audit-logger.js";
12
12
  import PrettyRequestLogger from "./pretty-request-logger.js";
13
13
  import { resolveTraceparent } from "./trace.js";
14
14
  import { asServersConfig, configuredServerPort } from "../src/servers/kernelServers.js";
15
- import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.js";
16
- import __decorate from "../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.js";
15
+ import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js";
16
+ import __decorate from "../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorate.js";
17
17
  import { GcScheduler, Module, RequestContext, Service, finallyMaybe, injectable, isPromise, logColor, nodefonyError, servedUrl, thenMaybe, writeRuntimeState } from "nodefony";
18
18
  import cluster from "node:cluster";
19
19
  import http from "node:http";
@@ -1,11 +1,11 @@
1
1
  import { asServersConfig, configuredServerPort } from "../../src/servers/kernelServers.js";
2
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.js";
3
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.js";
2
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js";
3
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorate.js";
4
4
  import http_kernel_default from "../http-kernel.js";
5
5
  import { handleClientError } from "./clientError.js";
6
6
  import { createDrainTerminator } from "./serverShutdown.js";
7
7
  import { bindWithFallback, buildBindPlan } from "../../src/servers/portBinder.js";
8
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateParam.js";
8
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateParam.js";
9
9
  import { Module, Service, extend, inject, nodefonyError } from "nodefony";
10
10
  import http from "node:http";
11
11
  //#region nodefony/service/servers/server-http.ts
@@ -1,11 +1,11 @@
1
1
  import { asServersConfig, configuredServerPort } from "../../src/servers/kernelServers.js";
2
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.js";
3
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.js";
2
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js";
3
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorate.js";
4
4
  import http_kernel_default from "../http-kernel.js";
5
5
  import { handleClientError } from "./clientError.js";
6
6
  import { createDrainTerminator } from "./serverShutdown.js";
7
7
  import { bindWithFallback, buildBindPlan } from "../../src/servers/portBinder.js";
8
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateParam.js";
8
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateParam.js";
9
9
  import { Module, Service, extend, inject, nodefonyError } from "nodefony";
10
10
  import http2 from "node:http2";
11
11
  import https from "node:https";
@@ -1,8 +1,8 @@
1
1
  import { asServersConfig, configuredServerPort } from "../../src/servers/kernelServers.js";
2
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.js";
3
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.js";
2
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js";
3
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorate.js";
4
4
  import http_kernel_default from "../http-kernel.js";
5
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateParam.js";
5
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateParam.js";
6
6
  import { startHeartbeat, trackPong } from "./wsHeartbeat.js";
7
7
  import { Module, Service, inject } from "nodefony";
8
8
  import Ws, { WebSocketServer } from "ws";
@@ -1,8 +1,8 @@
1
1
  import { asServersConfig, configuredServerPort } from "../../src/servers/kernelServers.js";
2
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.js";
3
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.js";
2
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js";
3
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorate.js";
4
4
  import http_kernel_default from "../http-kernel.js";
5
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateParam.js";
5
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateParam.js";
6
6
  import { startHeartbeat, trackPong } from "./wsHeartbeat.js";
7
7
  import { Module, Service, inject } from "nodefony";
8
8
  import Ws, { WebSocketServer } from "ws";
@@ -1,8 +1,8 @@
1
1
  import { SESSION_FACETS } from "../../src/session/storage/sessionFilters.js";
2
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.js";
3
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.js";
2
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js";
3
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorate.js";
4
4
  import http_kernel_default from "../http-kernel.js";
5
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateParam.js";
5
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateParam.js";
6
6
  import Session from "../../src/session/session.js";
7
7
  import MemorySessionStorage from "../../src/session/storage/MemorySessionStorage.js";
8
8
  import RevocationGuardStorage from "../../src/session/storage/RevocationGuardStorage.js";
@@ -66,7 +66,8 @@ function resolveTraceparent(header) {
66
66
  const newSpanId = randomHex(8);
67
67
  return `${parsed.version}-${parsed.traceId}-${newSpanId}-${parsed.flags}`;
68
68
  }
69
- return `00-${randomHex(16)}-${randomHex(8)}-01`;
69
+ const hex = randomHex(24);
70
+ return `00-${hex.slice(0, 32)}-${hex.slice(32)}-01`;
70
71
  }
71
72
  //#endregion
72
73
  export { parseTraceparent, resolveTraceparent };
@@ -1,7 +1,7 @@
1
- import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateMetadata.js";
2
- import __decorate from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorate.js";
1
+ import __decorateMetadata from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateMetadata.js";
2
+ import __decorate from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorate.js";
3
3
  import http_kernel_default from "../http-kernel.js";
4
- import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.151.0/helpers/esm/decorateParam.js";
4
+ import __decorateParam from "../../../_virtual/_@oxc-project_runtime@0.152.0/helpers/esm/decorateParam.js";
5
5
  import { Cli, FileClass, Module, Service, inject } from "nodefony";
6
6
  import fs from "node:fs";
7
7
  import fsp from "node:fs/promises";