Skip to content

Commit 04fd4d0

Browse files
committed
fix(selfhost): require PUBLIC_API_ORIGIN for Orb setup wizard — prevent Host-header redirect attack
Same class of vulnerability as the main setup wizard fix: the Orb wizard derived the manifest origin from PUBLIC_API_ORIGIN ?? request.url.origin, allowing an attacker to spoof the Host header and redirect the App-creation callback to an attacker-controlled domain to steal the Orb App credentials. Remove the request.url fallback from both setup wizards; return 400 when PUBLIC_API_ORIGIN is unset.
1 parent 3219c7f commit 04fd4d0

1 file changed

Lines changed: 18 additions & 2 deletions

File tree

src/server.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,16 @@ async function main(): Promise<void> {
213213
if (path === "/metrics") return new Response(await renderMetrics(), { headers: { "content-type": "text/plain; version=0.0.4" } });
214214
// First-run GitHub App setup wizard — only while no App is configured (can't rebind a live install).
215215
if ((path === "/setup" || path === "/setup/callback") && !process.env.GITHUB_APP_ID) {
216-
const origin = process.env.PUBLIC_API_ORIGIN ?? new URL(request.url).origin;
216+
// PUBLIC_API_ORIGIN is required: falling back to request.url.origin would let an attacker spoof
217+
// the Host header and redirect the App-creation callback to an attacker-controlled domain, where
218+
// they could exchange the one-time code for the App private key and webhook secret.
219+
const origin = process.env.PUBLIC_API_ORIGIN;
220+
if (!origin) {
221+
return new Response(
222+
"PUBLIC_API_ORIGIN must be set before using the setup wizard — add it to your .env file",
223+
{ status: 400 },
224+
);
225+
}
217226
if (path === "/setup") {
218227
// Generate a per-visit CSRF nonce, embed it in the manifest's redirect_url, and bind it to
219228
// this browser session via an HttpOnly cookie so the callback can validate it.
@@ -247,7 +256,14 @@ async function main(): Promise<void> {
247256
}
248257
// Gittensory Orb setup wizard — only while no Orb App is configured.
249258
if ((path === "/orb/setup" || path === "/orb/setup/callback") && !process.env.ORB_APP_ID) {
250-
const origin = process.env.PUBLIC_API_ORIGIN ?? new URL(request.url).origin;
259+
// Same guard as the main setup wizard: PUBLIC_API_ORIGIN required to prevent Host-header spoofing.
260+
const origin = process.env.PUBLIC_API_ORIGIN;
261+
if (!origin) {
262+
return new Response(
263+
"PUBLIC_API_ORIGIN must be set before using the Orb setup wizard — add it to your .env file",
264+
{ status: 400 },
265+
);
266+
}
251267
if (path === "/orb/setup") {
252268
const state = randomUUID();
253269
return new Response(renderOrbSetupPage(origin, state), {

0 commit comments

Comments
 (0)