@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.
- package/README.md +5 -3
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorate.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorateMetadata.js +1 -1
- package/dist/_virtual/{_@oxc-project_runtime@0.151.0 → _@oxc-project_runtime@0.152.0}/helpers/esm/decorateParam.js +1 -1
- package/dist/index.js +2 -2
- package/dist/nodefony/command/certificatesCommand.js +0 -1
- package/dist/nodefony/config/config.js +4 -3
- package/dist/nodefony/service/certificates.js +74 -143
- package/dist/nodefony/service/http-kernel.js +2 -2
- package/dist/nodefony/service/servers/server-http.js +3 -3
- package/dist/nodefony/service/servers/server-https.js +3 -3
- package/dist/nodefony/service/servers/server-websocket-secure.js +3 -3
- package/dist/nodefony/service/servers/server-websocket.js +3 -3
- package/dist/nodefony/service/sessions/sessions-service.js +3 -3
- package/dist/nodefony/service/trace.js +2 -1
- package/dist/nodefony/service/upload/upload-service.js +3 -3
- package/dist/nodefony/service/x509.js +324 -0
- package/dist/nodefony/src/context/http/Request.js +17 -4
- package/dist/nodefony/src/context/http/Response.js +30 -6
- package/dist/nodefony/src/context/http/urlFastPath.js +35 -10
- package/dist/nodefony/src/servers/portBinder.js +57 -2
- package/dist/types/nodefony/config/config.d.ts +1 -0
- package/dist/types/nodefony/service/certificates.d.ts +32 -47
- package/dist/types/nodefony/service/x509.d.ts +86 -0
- package/dist/types/nodefony/src/servers/portBinder.d.ts +10 -46
- package/docs/cookies.md +10 -10
- package/docs/index.md +4 -2
- package/docs/observabilite.md +13 -13
- package/docs/rate-limit.md +19 -19
- package/docs/servers.md +50 -47
- package/docs/session.md +17 -17
- package/docs/upload.md +29 -29
- 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 `
|
|
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
|
-
→
|
|
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.
|
|
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.
|
|
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
|
}
|
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.
|
|
25
|
-
import __decorate from "./_virtual/_@oxc-project_runtime@0.
|
|
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
|
-
|
|
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
|
|
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-
|
|
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-
|
|
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.
|
|
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é
|
|
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é
|
|
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
|
|
334
|
-
|
|
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
|
|
352
|
-
|
|
353
|
-
|
|
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 =
|
|
324
|
+
const org = distinguishedNameField(cert.issuer, "O");
|
|
359
325
|
return org !== null && /mkcert/i.test(org);
|
|
360
326
|
}
|
|
361
|
-
return cert.
|
|
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(
|
|
368
|
-
|
|
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
|
-
|
|
456
|
-
|
|
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.
|
|
460
|
-
|
|
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.
|
|
464
|
-
|
|
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
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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
|
-
|
|
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).
|
|
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 =
|
|
551
|
-
info.serial = cert.serialNumber;
|
|
552
|
-
info.validFrom = cert.
|
|
553
|
-
info.validTo = cert.
|
|
554
|
-
info.signatureAlgorithm =
|
|
555
|
-
const 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
|
|
558
|
-
if (
|
|
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
|
-
/**
|
|
563
|
-
|
|
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":
|
|
571
|
-
case "sha384":
|
|
572
|
-
case "sha256": return
|
|
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
|
|
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.
|
|
16
|
-
import __decorate from "../../_virtual/_@oxc-project_runtime@0.
|
|
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.
|
|
3
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.
|
|
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.
|
|
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.
|
|
3
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.
|
|
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.
|
|
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.
|
|
3
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.
|
|
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.
|
|
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.
|
|
3
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.
|
|
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.
|
|
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.
|
|
3
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
2
|
-
import __decorate from "../../../_virtual/_@oxc-project_runtime@0.
|
|
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.
|
|
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";
|