@taujs/server 0.3.6 → 0.3.7
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/dist/{SSRServer-DPZped7n.d.ts → SSRServer-CbXIDaoA.d.ts} +5 -16
- package/dist/build.d.ts +2 -1
- package/dist/build.js +39 -38
- package/dist/config.d.ts +2 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +39 -38
- package/dist/security/csp.d.ts +12 -4
- package/dist/security/csp.js +134 -26
- package/package.json +1 -1
|
@@ -1,18 +1,7 @@
|
|
|
1
1
|
import { ServerResponse } from 'node:http';
|
|
2
|
-
import {
|
|
2
|
+
import { FastifyPluginAsync, FastifyPluginCallback } from 'fastify';
|
|
3
3
|
import { PluginOption } from 'vite';
|
|
4
|
-
|
|
5
|
-
type CSPDirectives = Record<string, string[]>;
|
|
6
|
-
interface CSPOptions {
|
|
7
|
-
directives?: CSPDirectives;
|
|
8
|
-
exposeNonce?: (req: FastifyRequest, nonce: string) => void;
|
|
9
|
-
generateCSP?: (directives: CSPDirectives, nonce: string) => string;
|
|
10
|
-
}
|
|
11
|
-
declare const defaultGenerateCSP: (directives: CSPDirectives, nonce: string) => string;
|
|
12
|
-
declare const generateNonce: () => string;
|
|
13
|
-
declare const createCSPHook: (options?: CSPOptions) => (req: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) => void;
|
|
14
|
-
declare const getRequestNonce: (req: FastifyRequest) => string | undefined;
|
|
15
|
-
declare const applyCSP: (security: SSRServerOptions["security"], reply: FastifyReply) => string | undefined;
|
|
4
|
+
import { CSPDirectives } from './security/csp.js';
|
|
16
5
|
|
|
17
6
|
declare const TEMPLATE: {
|
|
18
7
|
readonly defaultEntryClient: "entry-client";
|
|
@@ -66,7 +55,7 @@ type SSRServerOptions = {
|
|
|
66
55
|
security?: {
|
|
67
56
|
csp?: {
|
|
68
57
|
directives?: CSPDirectives;
|
|
69
|
-
generateCSP?: (directives: CSPDirectives,
|
|
58
|
+
generateCSP?: (directives: CSPDirectives, cspNonce: string) => string;
|
|
70
59
|
};
|
|
71
60
|
};
|
|
72
61
|
registerStaticAssets?: false | {
|
|
@@ -101,7 +90,7 @@ type RenderSSR = (initialDataResolved: Record<string, unknown>, location: string
|
|
|
101
90
|
headContent: string;
|
|
102
91
|
appHtml: string;
|
|
103
92
|
}>;
|
|
104
|
-
type RenderStream = (serverResponse: ServerResponse, callbacks: RenderCallbacks, initialDataPromise: Promise<Record<string, unknown>>, location: string, bootstrapModules?: string, meta?: Record<string, unknown
|
|
93
|
+
type RenderStream = (serverResponse: ServerResponse, callbacks: RenderCallbacks, initialDataPromise: Promise<Record<string, unknown>>, location: string, bootstrapModules?: string, meta?: Record<string, unknown>, cspNonce?: string) => void;
|
|
105
94
|
type RenderModule = {
|
|
106
95
|
renderSSR: RenderSSR;
|
|
107
96
|
renderStream: RenderStream;
|
|
@@ -150,4 +139,4 @@ interface InitialRouteParams extends Record<string, unknown> {
|
|
|
150
139
|
type RouteParams = InitialRouteParams & Record<string, unknown>;
|
|
151
140
|
type RoutePathsAndAttributes<Params = {}> = Omit<Route<Params>, 'element'>;
|
|
152
141
|
|
|
153
|
-
export { type BaseMiddleware as B, type Config as C, type DataResult as D, type GenericPlugin as G, type InitialRouteParams as I, type ManifestEntry as M, type NamedService as N, type ProcessedConfig as P, type Route as R, SSRServer as S, TEMPLATE as T, type RouteParams as a, type RouteAttributes as b, createMaps as c, type SSRServerOptions as d, type ServiceMethod as e, type ServiceRegistry as f, type RenderCallbacks as g, type SSRManifest as h, type Manifest as i, type RenderSSR as j, type RenderStream as k, type RenderModule as l, type ServiceCall as m, type DataHandler as n, type RoutePathsAndAttributes as o, processConfigs as p
|
|
142
|
+
export { type BaseMiddleware as B, type Config as C, type DataResult as D, type GenericPlugin as G, type InitialRouteParams as I, type ManifestEntry as M, type NamedService as N, type ProcessedConfig as P, type Route as R, SSRServer as S, TEMPLATE as T, type RouteParams as a, type RouteAttributes as b, createMaps as c, type SSRServerOptions as d, type ServiceMethod as e, type ServiceRegistry as f, type RenderCallbacks as g, type SSRManifest as h, type Manifest as i, type RenderSSR as j, type RenderStream as k, type RenderModule as l, type ServiceCall as m, type DataHandler as n, type RoutePathsAndAttributes as o, processConfigs as p };
|
package/dist/build.d.ts
CHANGED
package/dist/build.js
CHANGED
|
@@ -193,7 +193,7 @@ import { build } from "vite";
|
|
|
193
193
|
import { nodePolyfills } from "vite-plugin-node-polyfills";
|
|
194
194
|
|
|
195
195
|
// src/SSRServer.ts
|
|
196
|
-
var
|
|
196
|
+
var import_fastify_plugin2 = __toESM(require_plugin(), 1);
|
|
197
197
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
198
198
|
import { readFile } from "fs/promises";
|
|
199
199
|
import path2 from "path";
|
|
@@ -266,6 +266,7 @@ function createAuthHook(routes, isDebug) {
|
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
// src/security/csp.ts
|
|
269
|
+
var import_fastify_plugin = __toESM(require_plugin(), 1);
|
|
269
270
|
import crypto from "crypto";
|
|
270
271
|
|
|
271
272
|
// src/utils/Utils.ts
|
|
@@ -421,28 +422,22 @@ var defaultGenerateCSP = (directives, nonce) => {
|
|
|
421
422
|
return Object.entries(merged).map(([key, values]) => `${key} ${values.join(" ")}`).join("; ");
|
|
422
423
|
};
|
|
423
424
|
var generateNonce = () => crypto.randomBytes(16).toString("base64");
|
|
424
|
-
var
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
425
|
+
var cspPlugin = (0, import_fastify_plugin.default)(
|
|
426
|
+
async (fastify, opts) => {
|
|
427
|
+
fastify.addHook("onRequest", (req, reply, done) => {
|
|
428
|
+
const nonce = generateNonce();
|
|
429
|
+
req.cspNonce = nonce;
|
|
430
|
+
const directives = opts.directives ?? DEV_CSP_DIRECTIVES;
|
|
431
|
+
const generate = opts.generateCSP ?? defaultGenerateCSP;
|
|
432
|
+
const cspHeader = generate(directives, nonce);
|
|
433
|
+
reply.header("Content-Security-Policy", cspHeader);
|
|
434
|
+
done();
|
|
435
|
+
});
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
name: "taujs-csp-plugin"
|
|
434
439
|
}
|
|
435
|
-
|
|
436
|
-
};
|
|
437
|
-
var applyCSP = (security, reply) => {
|
|
438
|
-
const nonce = generateNonce();
|
|
439
|
-
const directives = security?.csp?.directives ?? DEV_CSP_DIRECTIVES;
|
|
440
|
-
const generate = security?.csp?.generateCSP ?? defaultGenerateCSP;
|
|
441
|
-
const header = generate(directives, nonce);
|
|
442
|
-
reply.header("Content-Security-Policy", header);
|
|
443
|
-
reply.request.nonce = nonce;
|
|
444
|
-
return nonce;
|
|
445
|
-
};
|
|
440
|
+
);
|
|
446
441
|
|
|
447
442
|
// src/security/verifyMiddleware.ts
|
|
448
443
|
var isAuthRequired = (r) => r.attr?.middleware?.auth?.required === true;
|
|
@@ -489,7 +484,7 @@ var processConfigs = (configs, baseClientRoot, templateDefaults) => {
|
|
|
489
484
|
};
|
|
490
485
|
});
|
|
491
486
|
};
|
|
492
|
-
var SSRServer = (0,
|
|
487
|
+
var SSRServer = (0, import_fastify_plugin2.default)(
|
|
493
488
|
async (app, opts) => {
|
|
494
489
|
const logger = createLogger(opts.isDebug ?? false);
|
|
495
490
|
const { alias, configs, routes, serviceRegistry, isDebug, clientRoot: baseClientRoot } = opts;
|
|
@@ -548,13 +543,10 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
548
543
|
...options ?? {}
|
|
549
544
|
});
|
|
550
545
|
}
|
|
551
|
-
app.
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
generateCSP: opts.security?.csp?.generateCSP
|
|
556
|
-
})
|
|
557
|
-
);
|
|
546
|
+
app.register(cspPlugin, {
|
|
547
|
+
directives: opts.security?.csp?.directives,
|
|
548
|
+
generateCSP: opts.security?.csp?.generateCSP
|
|
549
|
+
});
|
|
558
550
|
app.addHook("onRequest", createAuthHook(routes));
|
|
559
551
|
if (isDevelopment) {
|
|
560
552
|
const { createServer } = await import("vite");
|
|
@@ -613,7 +605,7 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
613
605
|
if (/\.\w+$/.test(req.raw.url ?? "")) return reply.callNotFound();
|
|
614
606
|
const url = req.url ? new URL(req.url, `http://${req.headers.host}`).pathname : "/";
|
|
615
607
|
const matchedRoute = matchRoute(url, routes);
|
|
616
|
-
const
|
|
608
|
+
const cspNonce = req.cspNonce;
|
|
617
609
|
if (!matchedRoute) {
|
|
618
610
|
reply.callNotFound();
|
|
619
611
|
return;
|
|
@@ -655,18 +647,23 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
655
647
|
if (renderType === RENDERTYPE.ssr) {
|
|
656
648
|
const { renderSSR } = renderModule;
|
|
657
649
|
const initialDataResolved = await initialDataPromise;
|
|
658
|
-
const initialDataScript = `<script nonce="${
|
|
650
|
+
const initialDataScript = `<script nonce="${cspNonce}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`;
|
|
659
651
|
const { headContent, appHtml } = await renderSSR(initialDataResolved, req.url, attr?.meta);
|
|
660
652
|
let aggregateHeadContent = headContent;
|
|
661
653
|
if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
|
|
662
654
|
if (manifest && cssLink) aggregateHeadContent += cssLink;
|
|
663
655
|
const shouldHydrate = attr?.hydrate !== false;
|
|
664
|
-
const bootstrapScriptTag = shouldHydrate ? `<script nonce="${
|
|
656
|
+
const bootstrapScriptTag = shouldHydrate ? `<script nonce="${cspNonce}" type="module" src="${bootstrapModule}" defer></script>` : "";
|
|
665
657
|
const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}${bootstrapScriptTag}`);
|
|
666
658
|
return reply.status(200).header("Content-Type", "text/html").send(fullHtml);
|
|
667
659
|
} else {
|
|
668
660
|
const { renderStream } = renderModule;
|
|
669
|
-
|
|
661
|
+
const cspNonce2 = req.cspNonce;
|
|
662
|
+
const cspHeader = reply.getHeader("Content-Security-Policy");
|
|
663
|
+
reply.raw.writeHead(200, {
|
|
664
|
+
"Content-Security-Policy": cspHeader,
|
|
665
|
+
"Content-Type": "text/html"
|
|
666
|
+
});
|
|
670
667
|
renderStream(
|
|
671
668
|
reply.raw,
|
|
672
669
|
{
|
|
@@ -677,7 +674,9 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
677
674
|
reply.raw.write(`${beforeHead}${aggregateHeadContent}${afterHead}`);
|
|
678
675
|
},
|
|
679
676
|
onFinish: async (initialDataResolved) => {
|
|
680
|
-
reply.raw.write(
|
|
677
|
+
reply.raw.write(
|
|
678
|
+
`<script nonce="${cspNonce2}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`
|
|
679
|
+
);
|
|
681
680
|
reply.raw.write(afterBody);
|
|
682
681
|
reply.raw.end();
|
|
683
682
|
},
|
|
@@ -689,7 +688,8 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
689
688
|
initialDataPromise,
|
|
690
689
|
req.url,
|
|
691
690
|
bootstrapModule,
|
|
692
|
-
attr?.meta
|
|
691
|
+
attr?.meta,
|
|
692
|
+
cspNonce2
|
|
693
693
|
);
|
|
694
694
|
}
|
|
695
695
|
} catch (error) {
|
|
@@ -704,13 +704,14 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
704
704
|
const defaultConfig = processedConfigs[0];
|
|
705
705
|
if (!defaultConfig) throw new Error("No default configuration found.");
|
|
706
706
|
const { clientRoot } = defaultConfig;
|
|
707
|
-
const
|
|
707
|
+
const cspNonce = req.cspNonce;
|
|
708
708
|
let template = ensureNonNull(templates.get(clientRoot), `Template not found for clientRoot: ${clientRoot}`);
|
|
709
709
|
const cssLink = cssLinks.get(clientRoot);
|
|
710
710
|
const bootstrapModule = bootstrapModules.get(clientRoot);
|
|
711
711
|
template = template.replace(SSRTAG.ssrHead, "").replace(SSRTAG.ssrHtml, "");
|
|
712
712
|
if (!isDevelopment && cssLink) template = template.replace("</head>", `${cssLink}</head>`);
|
|
713
|
-
if (bootstrapModule)
|
|
713
|
+
if (bootstrapModule)
|
|
714
|
+
template = template.replace("</body>", `<script nonce="${cspNonce}" type="module" src="${bootstrapModule}" defer></script></body>`);
|
|
714
715
|
reply.status(200).type("text/html").send(template);
|
|
715
716
|
} catch (error) {
|
|
716
717
|
console.error("Failed to serve clientHtmlTemplate:", error);
|
package/dist/config.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { PluginOption } from 'vite';
|
|
2
|
-
import { R as Route, a as RouteParams, b as RouteAttributes } from './SSRServer-
|
|
2
|
+
import { R as Route, a as RouteParams, b as RouteAttributes } from './SSRServer-CbXIDaoA.js';
|
|
3
3
|
import 'node:http';
|
|
4
4
|
import 'fastify';
|
|
5
|
+
import './security/csp.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* taujs [ τjs ] Orchestration System
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { FastifyReply } from 'fastify';
|
|
2
|
-
export { B as BaseMiddleware, C as Config, n as DataHandler, D as DataResult, G as GenericPlugin, I as InitialRouteParams, i as Manifest, M as ManifestEntry, N as NamedService, P as ProcessedConfig, g as RenderCallbacks, l as RenderModule, j as RenderSSR, k as RenderStream, R as Route, b as RouteAttributes, a as RouteParams, o as RoutePathsAndAttributes, h as SSRManifest, S as SSRServer, d as SSRServerOptions, m as ServiceCall, e as ServiceMethod, f as ServiceRegistry, T as TEMPLATE, c as createMaps, p as processConfigs } from './SSRServer-
|
|
2
|
+
export { B as BaseMiddleware, C as Config, n as DataHandler, D as DataResult, G as GenericPlugin, I as InitialRouteParams, i as Manifest, M as ManifestEntry, N as NamedService, P as ProcessedConfig, g as RenderCallbacks, l as RenderModule, j as RenderSSR, k as RenderStream, R as Route, b as RouteAttributes, a as RouteParams, o as RoutePathsAndAttributes, h as SSRManifest, S as SSRServer, d as SSRServerOptions, m as ServiceCall, e as ServiceMethod, f as ServiceRegistry, T as TEMPLATE, c as createMaps, p as processConfigs } from './SSRServer-CbXIDaoA.js';
|
|
3
3
|
import 'node:http';
|
|
4
4
|
import 'vite';
|
|
5
|
+
import './security/csp.js';
|
|
5
6
|
|
|
6
7
|
declare module 'fastify' {
|
|
7
8
|
interface FastifyRequest {
|
|
8
|
-
|
|
9
|
+
cspNonce?: string;
|
|
9
10
|
}
|
|
10
11
|
interface FastifyInstance {
|
|
11
12
|
/**
|
package/dist/index.js
CHANGED
|
@@ -191,7 +191,7 @@ var require_picocolors = __commonJS({
|
|
|
191
191
|
import "fastify";
|
|
192
192
|
|
|
193
193
|
// src/SSRServer.ts
|
|
194
|
-
var
|
|
194
|
+
var import_fastify_plugin2 = __toESM(require_plugin(), 1);
|
|
195
195
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
196
196
|
import { readFile } from "fs/promises";
|
|
197
197
|
import path2 from "path";
|
|
@@ -264,6 +264,7 @@ function createAuthHook(routes, isDebug) {
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
// src/security/csp.ts
|
|
267
|
+
var import_fastify_plugin = __toESM(require_plugin(), 1);
|
|
267
268
|
import crypto from "crypto";
|
|
268
269
|
|
|
269
270
|
// src/utils/Utils.ts
|
|
@@ -419,28 +420,22 @@ var defaultGenerateCSP = (directives, nonce) => {
|
|
|
419
420
|
return Object.entries(merged).map(([key, values]) => `${key} ${values.join(" ")}`).join("; ");
|
|
420
421
|
};
|
|
421
422
|
var generateNonce = () => crypto.randomBytes(16).toString("base64");
|
|
422
|
-
var
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
423
|
+
var cspPlugin = (0, import_fastify_plugin.default)(
|
|
424
|
+
async (fastify, opts) => {
|
|
425
|
+
fastify.addHook("onRequest", (req, reply, done) => {
|
|
426
|
+
const nonce = generateNonce();
|
|
427
|
+
req.cspNonce = nonce;
|
|
428
|
+
const directives = opts.directives ?? DEV_CSP_DIRECTIVES;
|
|
429
|
+
const generate = opts.generateCSP ?? defaultGenerateCSP;
|
|
430
|
+
const cspHeader = generate(directives, nonce);
|
|
431
|
+
reply.header("Content-Security-Policy", cspHeader);
|
|
432
|
+
done();
|
|
433
|
+
});
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: "taujs-csp-plugin"
|
|
432
437
|
}
|
|
433
|
-
|
|
434
|
-
};
|
|
435
|
-
var applyCSP = (security, reply) => {
|
|
436
|
-
const nonce = generateNonce();
|
|
437
|
-
const directives = security?.csp?.directives ?? DEV_CSP_DIRECTIVES;
|
|
438
|
-
const generate = security?.csp?.generateCSP ?? defaultGenerateCSP;
|
|
439
|
-
const header = generate(directives, nonce);
|
|
440
|
-
reply.header("Content-Security-Policy", header);
|
|
441
|
-
reply.request.nonce = nonce;
|
|
442
|
-
return nonce;
|
|
443
|
-
};
|
|
438
|
+
);
|
|
444
439
|
|
|
445
440
|
// src/security/verifyMiddleware.ts
|
|
446
441
|
var isAuthRequired = (r) => r.attr?.middleware?.auth?.required === true;
|
|
@@ -487,7 +482,7 @@ var processConfigs = (configs, baseClientRoot, templateDefaults) => {
|
|
|
487
482
|
};
|
|
488
483
|
});
|
|
489
484
|
};
|
|
490
|
-
var SSRServer = (0,
|
|
485
|
+
var SSRServer = (0, import_fastify_plugin2.default)(
|
|
491
486
|
async (app, opts) => {
|
|
492
487
|
const logger = createLogger(opts.isDebug ?? false);
|
|
493
488
|
const { alias, configs, routes, serviceRegistry, isDebug, clientRoot: baseClientRoot } = opts;
|
|
@@ -546,13 +541,10 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
546
541
|
...options ?? {}
|
|
547
542
|
});
|
|
548
543
|
}
|
|
549
|
-
app.
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
generateCSP: opts.security?.csp?.generateCSP
|
|
554
|
-
})
|
|
555
|
-
);
|
|
544
|
+
app.register(cspPlugin, {
|
|
545
|
+
directives: opts.security?.csp?.directives,
|
|
546
|
+
generateCSP: opts.security?.csp?.generateCSP
|
|
547
|
+
});
|
|
556
548
|
app.addHook("onRequest", createAuthHook(routes));
|
|
557
549
|
if (isDevelopment) {
|
|
558
550
|
const { createServer } = await import("vite");
|
|
@@ -611,7 +603,7 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
611
603
|
if (/\.\w+$/.test(req.raw.url ?? "")) return reply.callNotFound();
|
|
612
604
|
const url = req.url ? new URL(req.url, `http://${req.headers.host}`).pathname : "/";
|
|
613
605
|
const matchedRoute = matchRoute(url, routes);
|
|
614
|
-
const
|
|
606
|
+
const cspNonce = req.cspNonce;
|
|
615
607
|
if (!matchedRoute) {
|
|
616
608
|
reply.callNotFound();
|
|
617
609
|
return;
|
|
@@ -653,18 +645,23 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
653
645
|
if (renderType === RENDERTYPE.ssr) {
|
|
654
646
|
const { renderSSR } = renderModule;
|
|
655
647
|
const initialDataResolved = await initialDataPromise;
|
|
656
|
-
const initialDataScript = `<script nonce="${
|
|
648
|
+
const initialDataScript = `<script nonce="${cspNonce}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`;
|
|
657
649
|
const { headContent, appHtml } = await renderSSR(initialDataResolved, req.url, attr?.meta);
|
|
658
650
|
let aggregateHeadContent = headContent;
|
|
659
651
|
if (ssrManifest && preloadLink) aggregateHeadContent += preloadLink;
|
|
660
652
|
if (manifest && cssLink) aggregateHeadContent += cssLink;
|
|
661
653
|
const shouldHydrate = attr?.hydrate !== false;
|
|
662
|
-
const bootstrapScriptTag = shouldHydrate ? `<script nonce="${
|
|
654
|
+
const bootstrapScriptTag = shouldHydrate ? `<script nonce="${cspNonce}" type="module" src="${bootstrapModule}" defer></script>` : "";
|
|
663
655
|
const fullHtml = template.replace(SSRTAG.ssrHead, aggregateHeadContent).replace(SSRTAG.ssrHtml, `${appHtml}${initialDataScript}${bootstrapScriptTag}`);
|
|
664
656
|
return reply.status(200).header("Content-Type", "text/html").send(fullHtml);
|
|
665
657
|
} else {
|
|
666
658
|
const { renderStream } = renderModule;
|
|
667
|
-
|
|
659
|
+
const cspNonce2 = req.cspNonce;
|
|
660
|
+
const cspHeader = reply.getHeader("Content-Security-Policy");
|
|
661
|
+
reply.raw.writeHead(200, {
|
|
662
|
+
"Content-Security-Policy": cspHeader,
|
|
663
|
+
"Content-Type": "text/html"
|
|
664
|
+
});
|
|
668
665
|
renderStream(
|
|
669
666
|
reply.raw,
|
|
670
667
|
{
|
|
@@ -675,7 +672,9 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
675
672
|
reply.raw.write(`${beforeHead}${aggregateHeadContent}${afterHead}`);
|
|
676
673
|
},
|
|
677
674
|
onFinish: async (initialDataResolved) => {
|
|
678
|
-
reply.raw.write(
|
|
675
|
+
reply.raw.write(
|
|
676
|
+
`<script nonce="${cspNonce2}">window.__INITIAL_DATA__ = ${JSON.stringify(initialDataResolved).replace(/</g, "\\u003c")}</script>`
|
|
677
|
+
);
|
|
679
678
|
reply.raw.write(afterBody);
|
|
680
679
|
reply.raw.end();
|
|
681
680
|
},
|
|
@@ -687,7 +686,8 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
687
686
|
initialDataPromise,
|
|
688
687
|
req.url,
|
|
689
688
|
bootstrapModule,
|
|
690
|
-
attr?.meta
|
|
689
|
+
attr?.meta,
|
|
690
|
+
cspNonce2
|
|
691
691
|
);
|
|
692
692
|
}
|
|
693
693
|
} catch (error) {
|
|
@@ -702,13 +702,14 @@ var SSRServer = (0, import_fastify_plugin.default)(
|
|
|
702
702
|
const defaultConfig = processedConfigs[0];
|
|
703
703
|
if (!defaultConfig) throw new Error("No default configuration found.");
|
|
704
704
|
const { clientRoot } = defaultConfig;
|
|
705
|
-
const
|
|
705
|
+
const cspNonce = req.cspNonce;
|
|
706
706
|
let template = ensureNonNull(templates.get(clientRoot), `Template not found for clientRoot: ${clientRoot}`);
|
|
707
707
|
const cssLink = cssLinks.get(clientRoot);
|
|
708
708
|
const bootstrapModule = bootstrapModules.get(clientRoot);
|
|
709
709
|
template = template.replace(SSRTAG.ssrHead, "").replace(SSRTAG.ssrHtml, "");
|
|
710
710
|
if (!isDevelopment && cssLink) template = template.replace("</head>", `${cssLink}</head>`);
|
|
711
|
-
if (bootstrapModule)
|
|
711
|
+
if (bootstrapModule)
|
|
712
|
+
template = template.replace("</body>", `<script nonce="${cspNonce}" type="module" src="${bootstrapModule}" defer></script></body>`);
|
|
712
713
|
reply.status(200).type("text/html").send(template);
|
|
713
714
|
} catch (error) {
|
|
714
715
|
console.error("Failed to serve clientHtmlTemplate:", error);
|
package/dist/security/csp.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import 'fastify';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { FastifyPluginAsync } from 'fastify';
|
|
2
|
+
|
|
3
|
+
interface CSPPluginOptions {
|
|
4
|
+
directives?: CSPDirectives;
|
|
5
|
+
generateCSP?: (directives: CSPDirectives, nonce: string) => string;
|
|
6
|
+
}
|
|
7
|
+
type CSPDirectives = Record<string, string[]>;
|
|
8
|
+
declare const defaultGenerateCSP: (directives: CSPDirectives, nonce: string) => string;
|
|
9
|
+
declare const generateNonce: () => string;
|
|
10
|
+
declare const cspPlugin: FastifyPluginAsync<CSPPluginOptions>;
|
|
11
|
+
|
|
12
|
+
export { type CSPDirectives, type CSPPluginOptions, cspPlugin, defaultGenerateCSP, generateNonce };
|
package/dist/security/csp.js
CHANGED
|
@@ -1,4 +1,121 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
8
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
19
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
20
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
21
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
22
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
23
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
|
+
mod
|
|
25
|
+
));
|
|
26
|
+
|
|
27
|
+
// node_modules/fastify-plugin/lib/getPluginName.js
|
|
28
|
+
var require_getPluginName = __commonJS({
|
|
29
|
+
"node_modules/fastify-plugin/lib/getPluginName.js"(exports, module) {
|
|
30
|
+
"use strict";
|
|
31
|
+
var fpStackTracePattern = /at\s{1}(?:.*\.)?plugin\s{1}.*\n\s*(.*)/;
|
|
32
|
+
var fileNamePattern = /(\w*(\.\w*)*)\..*/;
|
|
33
|
+
module.exports = function getPluginName(fn) {
|
|
34
|
+
if (fn.name.length > 0) return fn.name;
|
|
35
|
+
const stackTraceLimit = Error.stackTraceLimit;
|
|
36
|
+
Error.stackTraceLimit = 10;
|
|
37
|
+
try {
|
|
38
|
+
throw new Error("anonymous function");
|
|
39
|
+
} catch (e) {
|
|
40
|
+
Error.stackTraceLimit = stackTraceLimit;
|
|
41
|
+
return extractPluginName(e.stack);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
function extractPluginName(stack) {
|
|
45
|
+
const m = stack.match(fpStackTracePattern);
|
|
46
|
+
return m ? m[1].split(/[/\\]/).slice(-1)[0].match(fileNamePattern)[1] : "anonymous";
|
|
47
|
+
}
|
|
48
|
+
module.exports.extractPluginName = extractPluginName;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// node_modules/fastify-plugin/lib/toCamelCase.js
|
|
53
|
+
var require_toCamelCase = __commonJS({
|
|
54
|
+
"node_modules/fastify-plugin/lib/toCamelCase.js"(exports, module) {
|
|
55
|
+
"use strict";
|
|
56
|
+
module.exports = function toCamelCase(name) {
|
|
57
|
+
if (name[0] === "@") {
|
|
58
|
+
name = name.slice(1).replace("/", "-");
|
|
59
|
+
}
|
|
60
|
+
return name.replace(/-(.)/g, function(match2, g1) {
|
|
61
|
+
return g1.toUpperCase();
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// node_modules/fastify-plugin/plugin.js
|
|
68
|
+
var require_plugin = __commonJS({
|
|
69
|
+
"node_modules/fastify-plugin/plugin.js"(exports, module) {
|
|
70
|
+
"use strict";
|
|
71
|
+
var getPluginName = require_getPluginName();
|
|
72
|
+
var toCamelCase = require_toCamelCase();
|
|
73
|
+
var count = 0;
|
|
74
|
+
function plugin(fn, options = {}) {
|
|
75
|
+
let autoName = false;
|
|
76
|
+
if (fn.default !== void 0) {
|
|
77
|
+
fn = fn.default;
|
|
78
|
+
}
|
|
79
|
+
if (typeof fn !== "function") {
|
|
80
|
+
throw new TypeError(
|
|
81
|
+
`fastify-plugin expects a function, instead got a '${typeof fn}'`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
if (typeof options === "string") {
|
|
85
|
+
options = {
|
|
86
|
+
fastify: options
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (typeof options !== "object" || Array.isArray(options) || options === null) {
|
|
90
|
+
throw new TypeError("The options object should be an object");
|
|
91
|
+
}
|
|
92
|
+
if (options.fastify !== void 0 && typeof options.fastify !== "string") {
|
|
93
|
+
throw new TypeError(`fastify-plugin expects a version string, instead got '${typeof options.fastify}'`);
|
|
94
|
+
}
|
|
95
|
+
if (!options.name) {
|
|
96
|
+
autoName = true;
|
|
97
|
+
options.name = getPluginName(fn) + "-auto-" + count++;
|
|
98
|
+
}
|
|
99
|
+
fn[Symbol.for("skip-override")] = options.encapsulate !== true;
|
|
100
|
+
fn[Symbol.for("fastify.display-name")] = options.name;
|
|
101
|
+
fn[Symbol.for("plugin-meta")] = options;
|
|
102
|
+
if (!fn.default) {
|
|
103
|
+
fn.default = fn;
|
|
104
|
+
}
|
|
105
|
+
const camelCase = toCamelCase(options.name);
|
|
106
|
+
if (!autoName && !fn[camelCase]) {
|
|
107
|
+
fn[camelCase] = fn;
|
|
108
|
+
}
|
|
109
|
+
return fn;
|
|
110
|
+
}
|
|
111
|
+
module.exports = plugin;
|
|
112
|
+
module.exports.default = plugin;
|
|
113
|
+
module.exports.fastifyPlugin = plugin;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
1
117
|
// src/security/csp.ts
|
|
118
|
+
var import_fastify_plugin = __toESM(require_plugin(), 1);
|
|
2
119
|
import crypto from "crypto";
|
|
3
120
|
|
|
4
121
|
// src/constants.ts
|
|
@@ -35,33 +152,24 @@ var defaultGenerateCSP = (directives, nonce) => {
|
|
|
35
152
|
return Object.entries(merged).map(([key, values]) => `${key} ${values.join(" ")}`).join("; ");
|
|
36
153
|
};
|
|
37
154
|
var generateNonce = () => crypto.randomBytes(16).toString("base64");
|
|
38
|
-
var
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
155
|
+
var cspPlugin = (0, import_fastify_plugin.default)(
|
|
156
|
+
async (fastify, opts) => {
|
|
157
|
+
fastify.addHook("onRequest", (req, reply, done) => {
|
|
158
|
+
const nonce = generateNonce();
|
|
159
|
+
req.cspNonce = nonce;
|
|
160
|
+
const directives = opts.directives ?? DEV_CSP_DIRECTIVES;
|
|
161
|
+
const generate = opts.generateCSP ?? defaultGenerateCSP;
|
|
162
|
+
const cspHeader = generate(directives, nonce);
|
|
163
|
+
reply.header("Content-Security-Policy", cspHeader);
|
|
164
|
+
done();
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "taujs-csp-plugin"
|
|
48
169
|
}
|
|
49
|
-
|
|
50
|
-
};
|
|
51
|
-
var getRequestNonce = (req) => req.nonce;
|
|
52
|
-
var applyCSP = (security, reply) => {
|
|
53
|
-
const nonce = generateNonce();
|
|
54
|
-
const directives = security?.csp?.directives ?? DEV_CSP_DIRECTIVES;
|
|
55
|
-
const generate = security?.csp?.generateCSP ?? defaultGenerateCSP;
|
|
56
|
-
const header = generate(directives, nonce);
|
|
57
|
-
reply.header("Content-Security-Policy", header);
|
|
58
|
-
reply.request.nonce = nonce;
|
|
59
|
-
return nonce;
|
|
60
|
-
};
|
|
170
|
+
);
|
|
61
171
|
export {
|
|
62
|
-
|
|
63
|
-
createCSPHook,
|
|
172
|
+
cspPlugin,
|
|
64
173
|
defaultGenerateCSP,
|
|
65
|
-
generateNonce
|
|
66
|
-
getRequestNonce
|
|
174
|
+
generateNonce
|
|
67
175
|
};
|