From 4772de2f0a25bca823b75b9d3952f88b02ce4ee1 Mon Sep 17 00:00:00 2001 From: Dustin Healy <54083382+dustinhealy@users.noreply.github.com> Date: Sun, 7 Jun 2026 22:13:58 -0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=A9=B9=20fix:=20Redact=20req.body.text=20?= =?UTF-8?q?before=20block-mode=20denyRequest?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In block mode the messagePiiFilter middleware called denyRequest before mutating req.body.text, so the original credential still reached denyRequest. That helper builds a user message from req.body.text and emits a `created` SSE event for it; for an existing conversation with a parent message id it also persists the user message to MongoDB. The assistant turn was refused but the credential itself surfaced in the chat-history bubble and the database row. Moved the req.body.text mutation ahead of the block branch so every downstream code path sees the redacted text. denyRequest now builds its user message and any persisted row from the scrubbed string, the warn and silent paths are unchanged, and the post-handlers (moderateText, the agent run) continue to receive redacted input. --- api/server/middleware/messagePiiFilter.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/api/server/middleware/messagePiiFilter.js b/api/server/middleware/messagePiiFilter.js index 4747e9c8af..30cc11de4e 100644 --- a/api/server/middleware/messagePiiFilter.js +++ b/api/server/middleware/messagePiiFilter.js @@ -31,6 +31,13 @@ async function messagePiiFilter(req, res, next) { if (result.matches.length === 0) { return next(); } + // Mutate req.body.text FIRST so every code path below (block's + // denyRequest, warn/silent's downstream middleware + controller) + // sees the redacted text. denyRequest emits a `created` SSE event + // built from req.body.text and conditionally persists that user + // message; running it on the original would leak the credential + // even though the assistant turn is refused. + req.body.text = result.text; if (config.onMatch === 'block') { const labels = result.matches.map((m) => m.patternLabel).join(', '); logger.info( @@ -42,7 +49,6 @@ async function messagePiiFilter(req, res, next) { message: `Message blocked by PII filter: ${labels}. Edit and retry.`, }); } - req.body.text = result.text; if (config.onMatch === 'warn') { req._piiPreRedactMatches = result.matches; }