From d3b59a319ee52af8457f2ed40caceff0ab27334e Mon Sep 17 00:00:00 2001 From: Puneet Dixit <236133619+puneetdixit200@users.noreply.github.com> Date: Thu, 21 May 2026 10:42:13 +0530 Subject: [PATCH 1/2] fix: redact auth token debug logs Assisted-by: Codex:gpt-5.5 --- models/signin.test.ts | 43 ++++++++++++++++++++++++++++++++++++++ models/signin.ts | 4 ++-- models/signup.test.ts | 48 +++++++++++++++++++++++++++++++++++++++++++ models/signup.ts | 11 ++++++---- 4 files changed, 100 insertions(+), 6 deletions(-) diff --git a/models/signin.test.ts b/models/signin.test.ts index dce663b11..a877b376c 100644 --- a/models/signin.test.ts +++ b/models/signin.test.ts @@ -1,5 +1,6 @@ import { assert } from "@std/assert/assert"; import { assertEquals } from "@std/assert/equals"; +import { configure, type LogRecord, reset } from "@logtape/logtape"; import { createSigninToken, deleteSigninToken, @@ -31,3 +32,45 @@ Deno.test({ assertEquals(deleted, undefined); }, }); + +Deno.test({ + name: "signin token debug log omits replay secrets", + sanitizeOps: false, + sanitizeResources: false, + async fn() { + const { kv } = createTestKv(); + const records: LogRecord[] = []; + const accountId = "019d9162-ffff-7fff-8fff-ffffffffffff"; + + await configure({ + reset: true, + sinks: { capture: (record) => records.push(record) }, + loggers: [ + { + category: ["hackerspub", "models", "signin"], + lowestLevel: "debug", + sinks: ["capture"], + }, + ], + }); + + try { + const token = await createSigninToken(kv, accountId); + const record = records.find((record) => + record.rawMessage === + "Created sign-in token for {accountId} (expires in {expires})" + ); + + assert(record != null); + assertEquals(record.properties.accountId, accountId); + assertEquals(record.properties.token, undefined); + assertEquals(record.properties.code, undefined); + + const serializedProperties = JSON.stringify(record.properties); + assertEquals(serializedProperties.includes(token.token), false); + assertEquals(serializedProperties.includes(token.code), false); + } finally { + await reset(); + } + }, +}); diff --git a/models/signin.ts b/models/signin.ts index 04e076005..c47baafbd 100644 --- a/models/signin.ts +++ b/models/signin.ts @@ -36,9 +36,9 @@ export async function createSigninToken( tokenData, EXPIRATION.total("millisecond"), ); - logger.debug("Created sign-in token (expires in {expires}): {token}", { + logger.debug("Created sign-in token for {accountId} (expires in {expires})", { expires: EXPIRATION, - token: tokenData, + accountId: tokenData.accountId, }); return tokenData; } diff --git a/models/signup.test.ts b/models/signup.test.ts index 14dfc6d66..182974190 100644 --- a/models/signup.test.ts +++ b/models/signup.test.ts @@ -1,5 +1,6 @@ import { assert } from "@std/assert/assert"; import { assertEquals } from "@std/assert/equals"; +import { configure, type LogRecord, reset } from "@logtape/logtape"; import { createAccount, createSignupToken, @@ -38,6 +39,53 @@ Deno.test({ }, }); +Deno.test({ + name: "signup token debug log omits replay secrets", + sanitizeOps: false, + sanitizeResources: false, + async fn() { + const { kv } = createTestKv(); + const records: LogRecord[] = []; + const email = "candidate@example.com"; + const inviterId = "019d9162-ffff-7fff-8fff-ffffffffffff"; + + await configure({ + reset: true, + sinks: { capture: (record) => records.push(record) }, + loggers: [ + { + category: ["hackerspub", "models", "signup"], + lowestLevel: "debug", + sinks: ["capture"], + }, + ], + }); + + try { + const token = await createSignupToken(kv, email, { inviterId }); + const record = records.find((record) => + record.rawMessage === + "Created sign-up token (expires in {expires}, invited: {invited})" + ); + + assert(record != null); + assertEquals(record.properties.invited, true); + assertEquals(record.properties.email, undefined); + assertEquals(record.properties.inviterId, undefined); + assertEquals(record.properties.token, undefined); + assertEquals(record.properties.code, undefined); + + const serializedProperties = JSON.stringify(record.properties); + assertEquals(serializedProperties.includes(email), false); + assertEquals(serializedProperties.includes(inviterId), false); + assertEquals(serializedProperties.includes(token.token), false); + assertEquals(serializedProperties.includes(token.code), false); + } finally { + await reset(); + } + }, +}); + Deno.test({ name: "createAccount() stores inviter and verified email", sanitizeOps: false, diff --git a/models/signup.ts b/models/signup.ts index 177519cae..4adb639e1 100644 --- a/models/signup.ts +++ b/models/signup.ts @@ -56,10 +56,13 @@ export async function createSignupToken( tokenData, expiration.total("millisecond"), ); - logger.debug("Created sign-up token (expires in {expires}): {token}", { - expires: EXPIRATION, - token: tokenData, - }); + logger.debug( + "Created sign-up token (expires in {expires}, invited: {invited})", + { + expires: expiration, + invited: tokenData.inviterId != null, + }, + ); return tokenData; } From f6c51d97edc32375eeb477d72a3d62622b19f0fe Mon Sep 17 00:00:00 2001 From: Puneet Dixit <236133619+puneetdixit200@users.noreply.github.com> Date: Thu, 21 May 2026 10:53:55 +0530 Subject: [PATCH 2/2] style: sort test imports --- models/signin.test.ts | 2 +- models/signup.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/models/signin.test.ts b/models/signin.test.ts index a877b376c..60aa1f27c 100644 --- a/models/signin.test.ts +++ b/models/signin.test.ts @@ -1,6 +1,6 @@ +import { configure, type LogRecord, reset } from "@logtape/logtape"; import { assert } from "@std/assert/assert"; import { assertEquals } from "@std/assert/equals"; -import { configure, type LogRecord, reset } from "@logtape/logtape"; import { createSigninToken, deleteSigninToken, diff --git a/models/signup.test.ts b/models/signup.test.ts index 182974190..e54ac0b5e 100644 --- a/models/signup.test.ts +++ b/models/signup.test.ts @@ -1,6 +1,6 @@ +import { configure, type LogRecord, reset } from "@logtape/logtape"; import { assert } from "@std/assert/assert"; import { assertEquals } from "@std/assert/equals"; -import { configure, type LogRecord, reset } from "@logtape/logtape"; import { createAccount, createSignupToken,