@tokenoftrust/storefront-runner 2.4.32 → 2.4.33
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.
|
@@ -37,6 +37,19 @@ export function stampInlineTagNonces(html: string, nonce: string): string {
|
|
|
37
37
|
);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
/**
|
|
41
|
+
* True when a CSP header already names a live nonce: a render-chain pass completed
|
|
42
|
+
* this response, stamping its header and document as one pair. Any further pass must
|
|
43
|
+
* serve it verbatim — re-stamping replaces the header's nonce but not the document's,
|
|
44
|
+
* and the browser then blocks every inline style and script on the page.
|
|
45
|
+
*/
|
|
46
|
+
export function carriesLiveNonce(response: Response): boolean {
|
|
47
|
+
return CSP_HEADERS.some((header) => {
|
|
48
|
+
const value = response.headers.get(header);
|
|
49
|
+
return !!value && value.includes("'nonce-") && !value.includes(INTEGRATION_NONCE_PLACEHOLDER);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
40
53
|
/** A fresh, non-guessable per-response nonce. */
|
|
41
54
|
export function freshNonce(): string {
|
|
42
55
|
return crypto.randomUUID();
|
|
@@ -92,8 +92,7 @@ import {
|
|
|
92
92
|
deriveSiteSurface,
|
|
93
93
|
cspHeaderName,
|
|
94
94
|
} from "@/lib/integration/browserEnvironment";
|
|
95
|
-
import { freshNonce, stampResponseNonce } from "@/lib/integration/nonceStamp";
|
|
96
|
-
import { takePinnedRouteRewriteResponse } from "@/lib/pinnedRouteRewriteBoundary";
|
|
95
|
+
import { carriesLiveNonce, freshNonce, stampResponseNonce } from "@/lib/integration/nonceStamp";
|
|
97
96
|
import {
|
|
98
97
|
composeIntegrationDeclarations,
|
|
99
98
|
resolveIntegrationRenderState,
|
|
@@ -713,13 +712,11 @@ export async function runRenderChain(input: RenderChainInput): Promise<Response>
|
|
|
713
712
|
? await next(rewriteTarget)
|
|
714
713
|
: await next();
|
|
715
714
|
|
|
716
|
-
// A
|
|
717
|
-
//
|
|
718
|
-
//
|
|
719
|
-
//
|
|
720
|
-
|
|
721
|
-
const pinnedRewriteResponse = takePinnedRouteRewriteResponse(response);
|
|
722
|
-
if (pinnedRewriteResponse) return pinnedRewriteResponse;
|
|
715
|
+
// A nested pass of this middleware — an in-process `rewrite()`, or Astro re-running
|
|
716
|
+
// middleware for its error route — already decorated and nonce-stamped the
|
|
717
|
+
// response. Serve it verbatim: decorating again would inject preview helpers twice
|
|
718
|
+
// and replace the header's nonce while the document keeps the inner one.
|
|
719
|
+
if (carriesLiveNonce(response)) return response;
|
|
723
720
|
|
|
724
721
|
// On-publish auto-refresh: inject the live-reload client into SHARED preview
|
|
725
722
|
// (`test`) HTML pages ONLY — never `live` (keeps live clean + edge-cacheable).
|
|
@@ -14,7 +14,6 @@ import { breadcrumbLd } from "@/lib/jsonld";
|
|
|
14
14
|
import { truncate } from "@/lib/format";
|
|
15
15
|
import { rawHtmlPolicy, rawHtmlResponse, rawPageCanonicalUrl } from "@/lib/rawMarketingHtml";
|
|
16
16
|
import { renderRawComplianceTop, renderRawComplianceBottom } from "@/lib/compliance/rawComplianceNotices";
|
|
17
|
-
import { rewriteThroughRenderChain } from "@/lib/pinnedRouteRewriteBoundary";
|
|
18
17
|
import { styleGuideAliasRewrite } from "@/lib/styleGuidePaths";
|
|
19
18
|
|
|
20
19
|
const { tenant, canonicalBase, content, basePath, capabilities, features } = Astro.locals;
|
|
@@ -38,7 +37,7 @@ if (
|
|
|
38
37
|
// never reskin the live site.
|
|
39
38
|
const styleGuideTarget = styleGuideAliasRewrite(basePath, slug, Astro.url.search);
|
|
40
39
|
if (styleGuideTarget !== undefined) {
|
|
41
|
-
return
|
|
40
|
+
return Astro.rewrite(styleGuideTarget);
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
// Raw tenant-authored marketing subpage (vendor-injected HTML) — serve the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.33",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Private response marker for the in-process route rewrite boundary.
|
|
3
|
-
*
|
|
4
|
-
* `context.rewrite()` runs the rewritten request through middleware and returns
|
|
5
|
-
* a complete response: preview helpers, security headers, CSP, and the live
|
|
6
|
-
* nonce have already been applied. The outer render chain must pass those bytes
|
|
7
|
-
* through instead of decorating and nonce-stamping them a second time — a second
|
|
8
|
-
* stamp replaces the CSP header's nonce while the document keeps the inner one,
|
|
9
|
-
* so the browser blocks every inline style and script on the page.
|
|
10
|
-
*
|
|
11
|
-
* The marker exists only while the inner response unwinds through the outer
|
|
12
|
-
* middleware pass. `takePinnedRouteRewriteResponse` removes it before the
|
|
13
|
-
* response reaches a client.
|
|
14
|
-
*
|
|
15
|
-
* Every in-process rewrite goes through {@link rewriteThroughRenderChain};
|
|
16
|
-
* `pinnedRouteRewriteBoundary.test.ts` refuses a bare `.rewrite(` anywhere else.
|
|
17
|
-
*/
|
|
18
|
-
export const PINNED_ROUTE_REWRITE_COMPLETE_HEADER =
|
|
19
|
-
"x-tot-internal-pinned-route-rewrite-complete";
|
|
20
|
-
|
|
21
|
-
export function markPinnedRouteRewriteComplete(response: Response): Response {
|
|
22
|
-
response.headers.set(PINNED_ROUTE_REWRITE_COMPLETE_HEADER, "1");
|
|
23
|
-
return response;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function takePinnedRouteRewriteResponse(response: Response): Response | null {
|
|
27
|
-
if (response.headers.get(PINNED_ROUTE_REWRITE_COMPLETE_HEADER) !== "1") return null;
|
|
28
|
-
|
|
29
|
-
const headers = new Headers(response.headers);
|
|
30
|
-
headers.delete(PINNED_ROUTE_REWRITE_COMPLETE_HEADER);
|
|
31
|
-
return new Response(response.body, {
|
|
32
|
-
status: response.status,
|
|
33
|
-
statusText: response.statusText,
|
|
34
|
-
headers,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Rewrite in-process and mark the completed response so the outer middleware pass
|
|
40
|
-
* serves it verbatim. The response a rewrite hands back may be immutable, so it is
|
|
41
|
-
* copied before the marker (and any caller headers) are set.
|
|
42
|
-
*/
|
|
43
|
-
export async function rewriteThroughRenderChain(
|
|
44
|
-
context: { rewrite(path: string): Promise<Response> },
|
|
45
|
-
path: string,
|
|
46
|
-
): Promise<Response> {
|
|
47
|
-
const rendered = await context.rewrite(path);
|
|
48
|
-
return markPinnedRouteRewriteComplete(new Response(rendered.body, rendered));
|
|
49
|
-
}
|