Skip to content

Commit 547e997

Browse files
authored
Merge pull request #163 from JoviDeCroock/codex/propose-fix-for-node-isg-vulnerability
2 parents 9b089c6 + 50fe5b6 commit 547e997

5 files changed

Lines changed: 20 additions & 12 deletions

File tree

.changeset/fair-mails-jump.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@pracht/adapter-node": patch
3+
---
4+
5+
Harden ISG stale background regeneration so it no longer reuses request context that can carry per-user data into shared cached HTML.

docs/ADAPTERS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,8 @@ that manifest.
145145
pages and writes updated HTML to disk. Route-state requests (`x-pracht-route-state-request`
146146
and `?_data=1`) bypass the cached HTML path so client navigation still reaches
147147
`handlePrachtRequest()`. Background regeneration uses a clean HTML request
148-
instead of replaying the triggering user's cookies/authorization headers.
148+
instead of replaying the triggering user's cookies/authorization headers, and
149+
skips app `createContext()` to avoid per-request context in shared ISG output.
149150
Static and ISG files are streamed, and static responses support `ETag` /
150151
`Last-Modified` conditional revalidation.
151152
- **Vite manifest**: reads `.vite/manifest.json` to inject correct `<script>` and

packages/adapter-node/src/node-handler.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,9 +264,11 @@ async function serveISGEntry<TContext>(
264264
}
265265

266266
if (isStale) {
267-
regenerateISGPage(options, pathname, htmlPath, contextArgs).catch((err) => {
268-
console.error(`ISG regeneration failed for ${pathname}:`, err);
269-
});
267+
regenerateISGPage(options, pathname, htmlPath, { request: contextArgs.request }).catch(
268+
(err) => {
269+
console.error(`ISG regeneration failed for ${pathname}:`, err);
270+
},
271+
);
270272
}
271273

272274
return true;

packages/adapter-node/src/node-isg.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,21 @@ import { dirname } from "node:path";
33
import { handlePrachtRequest } from "@pracht/core/server";
44
import type { NodeAdapterContextArgs, NodeAdapterOptions } from "./node-handler.ts";
55

6+
export interface ISGRegenerationContextArgs {
7+
request?: NodeAdapterContextArgs["request"];
8+
}
9+
610
export async function regenerateISGPage<TContext>(
711
options: NodeAdapterOptions<TContext>,
812
pathname: string,
913
htmlPath: string,
10-
contextArgs?: NodeAdapterContextArgs,
14+
contextArgs?: ISGRegenerationContextArgs,
1115
): Promise<void> {
1216
const request = createISGRegenerationRequest(pathname, contextArgs?.request);
13-
const context =
14-
options.createContext && contextArgs
15-
? await options.createContext({ ...contextArgs, request })
16-
: undefined;
1717

1818
const response = await handlePrachtRequest({
1919
app: options.app,
20-
context,
20+
context: undefined,
2121
registry: options.registry,
2222
request,
2323
clientEntryUrl: options.clientEntryUrl,

packages/adapter-node/test/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ describe("createNodeRequestHandler", () => {
190190
]);
191191
});
192192

193-
it("reuses createContext during stale ISG regeneration with a clean request", async () => {
193+
it("does not reuse createContext during stale ISG regeneration", async () => {
194194
const staticDir = makeTempDir();
195195
const htmlDir = join(staticDir, "isg");
196196
const htmlPath = join(htmlDir, "index.html");
@@ -252,7 +252,7 @@ describe("createNodeRequestHandler", () => {
252252

253253
await waitFor(() => readFileSync(htmlPath, "utf-8").includes("missing"));
254254

255-
expect(createContextCalls).toEqual(["missing"]);
255+
expect(createContextCalls).toEqual([]);
256256
expect(readFileSync(htmlPath, "utf-8")).toContain("missing");
257257
});
258258
});

0 commit comments

Comments
 (0)