From f4f7646d5ad69468064c20dc2788377636c17427 Mon Sep 17 00:00:00 2001 From: Justin Willhite <5132924+thejdubb02@users.noreply.github.com> Date: Wed, 26 Aug 2026 01:43:41 +0000 Subject: [PATCH 1/2] fix: respect HIDE_HISTORY on the results and converters pages The results and converters pages rendered
without the hideHistory prop, so the History nav link stayed visible on them even when HIDE_HISTORY was set. Every other page that renders the logged-in header already passes hideHistory={HIDE_HISTORY}; do the same here. Adds a regression test that renders the converters page with HIDE_HISTORY set and asserts the History link is absent (it fails without this change). Resolves #556 Signed-off-by: Justin Willhite <5132924+thejdubb02@users.noreply.github.com> --- src/pages/listConverters.tsx | 9 ++++++-- src/pages/results.tsx | 9 ++++++-- tests/pages/listConverters.test.ts | 37 ++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 tests/pages/listConverters.test.ts diff --git a/src/pages/listConverters.tsx b/src/pages/listConverters.tsx index 2e3cc772..1c5f8c42 100644 --- a/src/pages/listConverters.tsx +++ b/src/pages/listConverters.tsx @@ -2,7 +2,7 @@ import Elysia from "elysia"; import { BaseHtml } from "../components/base"; import { Header } from "../components/header"; import { getAllInputs, getAllTargets } from "../converters/main"; -import { ALLOW_UNAUTHENTICATED, WEBROOT } from "../helpers/env"; +import { ALLOW_UNAUTHENTICATED, HIDE_HISTORY, WEBROOT } from "../helpers/env"; import { userService } from "./user"; export const listConverters = new Elysia().use(userService).get( @@ -11,7 +11,12 @@ export const listConverters = new Elysia().use(userService).get( return ( <> -
+
<> -
+
Buffer.from(value).toString("base64url"); + const payload = `${b64url(JSON.stringify({ alg: "HS256", typ: "JWT" }))}.${b64url( + JSON.stringify({ id: "1", exp: Math.floor(Date.now() / 1000) + 3600 }), + )}`; + const signature = createHmac("sha256", JWT_SECRET).update(payload).digest("base64url"); + return `auth=${payload}.${signature}`; +} + +// Regression test for #556. +test("converters page hides the History link when HIDE_HISTORY is set", async () => { + const res = await listConverters.handle( + new Request("http://localhost/converters", { headers: { Cookie: sessionCookie() } }), + ); + expect(res.status).toBe(200); + + const html = await res.text(); + // Sanity: the authenticated header actually rendered. + expect(html).toContain('href="/account"'); + // The History link must be gone. + expect(html).not.toContain('href="/history"'); +}); From 6f7989b5ee78b061440986db14c503c9da0f2ef6 Mon Sep 17 00:00:00 2001 From: Justin Willhite <5132924+thejdubb02@users.noreply.github.com> Date: Wed, 26 Aug 2026 02:23:54 +0000 Subject: [PATCH 2/2] test: pin WEBROOT and add a results-page regression test Addresses review feedback on the header tests: - Pin WEBROOT="" so the History-link assertions stay deterministic in environments where WEBROOT is set. - Add a results-page render test (the scenario in #556), seeding a job for the authenticated user and asserting the History link is absent. It fails without the hideHistory fix on results.tsx. Signed-off-by: Justin Willhite <5132924+thejdubb02@users.noreply.github.com> --- tests/pages/listConverters.test.ts | 1 + tests/pages/results.test.ts | 43 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/tests/pages/listConverters.test.ts b/tests/pages/listConverters.test.ts index d141ba67..bc99a012 100644 --- a/tests/pages/listConverters.test.ts +++ b/tests/pages/listConverters.test.ts @@ -6,6 +6,7 @@ const JWT_SECRET = "test-secret"; process.env.DB_PATH = ":memory:"; process.env.JWT_SECRET = JWT_SECRET; process.env.HIDE_HISTORY = "true"; +process.env.WEBROOT = ""; import { createHmac } from "node:crypto"; import { expect, test } from "bun:test"; diff --git a/tests/pages/results.test.ts b/tests/pages/results.test.ts index 7673ad92..a44e96fd 100644 --- a/tests/pages/results.test.ts +++ b/tests/pages/results.test.ts @@ -1,6 +1,19 @@ +// The results page is behind auth and reads HIDE_HISTORY at module load, so the env is +// set and a session cookie minted before the page is imported below. buildDownloadUrl +// has no env dependency, so importing it statically above is safe. +const JWT_SECRET = "test-secret"; +process.env.DB_PATH = ":memory:"; +process.env.JWT_SECRET = JWT_SECRET; +process.env.HIDE_HISTORY = "true"; +process.env.WEBROOT = ""; + +import { createHmac } from "node:crypto"; import { expect, test } from "bun:test"; import { buildDownloadUrl } from "../../src/helpers/buildDownloadUrl"; +const { default: db } = await import("../../src/db/db"); +const { results } = await import("../../src/pages/results"); + test("encodes reserved characters in download filenames", () => { expect(buildDownloadUrl("", "1/2/", "clip #1?.gif")).toBe("/download/1/2/clip%20%231%3F.gif"); }); @@ -10,3 +23,33 @@ test("preserves output path segments while encoding the filename", () => { "/convertx/download/user/job/%E5%A0%B1%E5%91%8A%20100%25.pdf", ); }); + +// Minimal HS256 JWT so the authenticated route renders; @elysiajs/jwt verifies it. +function sessionCookie(): string { + const b64url = (value: string) => Buffer.from(value).toString("base64url"); + const payload = `${b64url(JSON.stringify({ alg: "HS256", typ: "JWT" }))}.${b64url( + JSON.stringify({ id: "1", exp: Math.floor(Date.now() / 1000) + 3600 }), + )}`; + const signature = createHmac("sha256", JWT_SECRET).update(payload).digest("base64url"); + return `auth=${payload}.${signature}`; +} + +// Regression test for #556 on the results page (the scenario in the report: the header +// shown right after a conversion). Previously this page omitted the hideHistory prop. +test("results page hides the History link when HIDE_HISTORY is set", async () => { + db.query("INSERT INTO users (id, email, password) VALUES (1, 'test@example.com', 'x')").run(); + db.query( + "INSERT INTO jobs (id, user_id, date_created, status, num_files) VALUES (1, 1, '2026-01-01', 'done', 0)", + ).run(); + + const res = await results.handle( + new Request("http://localhost/results/1", { headers: { Cookie: sessionCookie() } }), + ); + expect(res.status).toBe(200); + + const html = await res.text(); + // Sanity: the authenticated header actually rendered. + expect(html).toContain('href="/account"'); + // The History link must be gone. + expect(html).not.toContain('href="/history"'); +});