From e2e9ec9eb4545f73a8b8dae39a20f8f663b67fc3 Mon Sep 17 00:00:00 2001 From: Nico Date: Mon, 16 Feb 2026 01:34:09 -0800 Subject: [PATCH] fix: redact invite codes in logs using last4 policy --- backend/controllers/households.controller.js | 14 +++++++++++-- backend/utils/redaction.js | 20 +++++++++++++++++++ .../src/components/manage/ManageHousehold.jsx | 14 +++++++++++-- 3 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 backend/utils/redaction.js diff --git a/backend/controllers/households.controller.js b/backend/controllers/households.controller.js index 9c3076a..22c08dd 100644 --- a/backend/controllers/households.controller.js +++ b/backend/controllers/households.controller.js @@ -1,5 +1,6 @@ const householdModel = require("../models/household.model"); const { sendError } = require("../utils/http"); +const { inviteCodeLast4, safeErrorMessage } = require("../utils/redaction"); // Get all households user belongs to exports.getUserHouseholds = async (req, res) => { @@ -107,13 +108,18 @@ exports.refreshInviteCode = async (req, res) => { household }); } catch (error) { - console.error("Refresh invite code error:", error); + console.error( + `Refresh invite code error request_id=${req.request_id} invite_last4=${inviteCodeLast4( + req.body?.inviteCode + )} message=${safeErrorMessage(error)}` + ); sendError(res, 500, "Failed to refresh invite code"); } }; // Join household via invite code exports.joinHousehold = async (req, res) => { + const inviteLast4 = inviteCodeLast4(req.params.inviteCode); try { const { inviteCode } = req.params; if (!inviteCode) return sendError(res, 400, "Invite code is required"); @@ -138,7 +144,11 @@ exports.joinHousehold = async (req, res) => { household: { id: result.id, name: result.name } }); } catch (error) { - console.error("Join household error:", error); + console.error( + `Join household error request_id=${req.request_id} invite_last4=${inviteLast4} message=${safeErrorMessage( + error + )}` + ); sendError(res, 500, "Failed to join household"); } }; diff --git a/backend/utils/redaction.js b/backend/utils/redaction.js new file mode 100644 index 0000000..ee0dd6a --- /dev/null +++ b/backend/utils/redaction.js @@ -0,0 +1,20 @@ +function inviteCodeLast4(inviteCode) { + if (!inviteCode || typeof inviteCode !== "string") return "none"; + const trimmed = inviteCode.trim(); + if (!trimmed) return "none"; + return trimmed.slice(-4); +} + +function safeErrorMessage(error) { + if (!error) return "unknown_error"; + if (typeof error === "string") return error; + if (typeof error.message === "string" && error.message.trim()) { + return error.message; + } + return "unknown_error"; +} + +module.exports = { + inviteCodeLast4, + safeErrorMessage, +}; diff --git a/frontend/src/components/manage/ManageHousehold.jsx b/frontend/src/components/manage/ManageHousehold.jsx index a593658..aaeb2db 100644 --- a/frontend/src/components/manage/ManageHousehold.jsx +++ b/frontend/src/components/manage/ManageHousehold.jsx @@ -64,9 +64,19 @@ export default function ManageHousehold() { try { const response = await refreshInviteCode(activeHousehold.id); await refreshHouseholds(); - alert(`New invite code: ${response.data.inviteCode}`); + const refreshedInviteCode = response.data?.household?.invite_code; + if (refreshedInviteCode) { + alert(`New invite code: ${refreshedInviteCode}`); + } else { + alert("Invite code refreshed successfully"); + } } catch (error) { - console.error("Failed to refresh invite code:", error); + console.error( + "Failed to refresh invite code:", + error?.response?.data?.error?.message || + error?.response?.data?.message || + error?.message + ); alert("Failed to refresh invite code"); } };