diff options
| author | Joe Mou <dev@mou.fo> | 2026-08-12 01:27:25 -0400 |
|---|---|---|
| committer | Joe Mou <dev@mou.fo> | 2026-08-12 01:28:04 -0400 |
| commit | 679a2b71c3e73dc1b8cf11806b3f3df49018b535 (patch) | |
| tree | abd5824f7834c595867893634016a4e050c30067 | |
| parent | 21310d0366c0f21e73c1f4f78d38370edfb59445 (diff) | |
Use an HTTP redirect for same-origin requests
The meta refresh page renders as a broken image when the request came
from an <img> on one of our own pages. Such requests are already past
the redirect-loop hazard the refresh works around, so send them a real
redirect instead.
| -rw-r--r-- | cgithub/src/app.test.ts | 20 | ||||
| -rw-r--r-- | cgithub/src/app.ts | 10 |
2 files changed, 29 insertions, 1 deletions
diff --git a/cgithub/src/app.test.ts b/cgithub/src/app.test.ts index 5474f8d..59ed352 100644 --- a/cgithub/src/app.test.ts +++ b/cgithub/src/app.test.ts @@ -110,6 +110,26 @@ describe("redirects to GitHub", () => { assert.doesNotMatch(body, /<script/); }); + // Same-origin requests are mostly subresources, which render an HTML page as + // a broken image rather than following its refresh. + it("should redirect over HTTP when the referer is one of our own pages", async () => { + const res = await app.request("http://cgithub.example/a/b/c?x=1", { + headers: { Referer: "http://cgithub.example/a/b" }, + }); + + assert.strictEqual(res.status, 302); + assert.strictEqual(res.headers.get("location"), "https://github.com/a/b/c?x=1"); + }); + + it("should use a meta refresh when the referer is another site", async () => { + const res = await app.request("http://cgithub.example/a/b/c?x=1", { + headers: { Referer: "https://github.example/a/b" }, + }); + + assert.strictEqual(res.status, 200); + assert.strictEqual(res.headers.get("location"), null); + }); + it("should redirect unhandled search types", async () => { const res = await app.request( "http://cgithub.example/actions/deploy-pages/search?q=x&type=code", diff --git a/cgithub/src/app.ts b/cgithub/src/app.ts index e27db59..9781a98 100644 --- a/cgithub/src/app.ts +++ b/cgithub/src/app.ts @@ -29,8 +29,16 @@ export function createApp(eta: Eta) { const app = new Hono(); // Use a meta refresh to avoid redirect loops in certain situations; we become - // the initiator origin even if we are the target of a redirection. + // the initiator origin even if we are the target of a redirection. Requests + // coming from one of our own pages are already past that hazard, and are + // often subresources (an <img> in a rendered README) that can't do anything + // with an HTML page, so those get a real HTTP redirect. function redirectToGitHub(c: Context, location: string) { + const referer = c.req.header("Referer"); + if (referer && URL.parse(referer)?.origin === new URL(c.req.url).origin) { + return c.redirect(location); + } + c.header("Referrer-Policy", "no-referrer"); return c.html(eta.render("redirect.eta", { location })); } |
