mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 05:12:14 +00:00
The red-team corpus probed only user messages and model replies. Neither is the channel that reaches the prompt unguarded: a lab analyte, panel or unit name, or a medication label, none of which the account holder authors when the row was committed from an uploaded document. The free-text guard's file list was hardcoded and had rotted — three of its four entries were no longer reachable from the snapshot, and it covered one of the dozen files that actually emit free text, which is why the lab fields went unnoticed. Derive it from the import graph instead; it now covers 75 files, with a floor assertion so a resolver break cannot silently empty it. Deriving the list immediately surfaced a second leak: features.ts shipped a raw medication name and dose into the snapshot medications block. Also fix sanitizeForPrompt itself. It deleted control characters, which welded neighbouring tokens together and destroyed the word boundary its own injection patterns anchor on, so a payload preceded by a newline survived because of the newline. Replace with a space and collapse before matching.
38 lines
1.6 KiB
TypeScript
38 lines
1.6 KiB
TypeScript
/**
|
|
* Sanitize user-provided strings before embedding them in LLM prompts.
|
|
* Strips control characters, prompt-injection patterns, and limits length.
|
|
*
|
|
* v1.30.25 — control characters are replaced with a SPACE, not deleted, and
|
|
* the run is collapsed before the injection patterns are applied.
|
|
*
|
|
* Deleting them was self-defeating: every injection pattern below is anchored
|
|
* on a `\b` word boundary, and deletion WELDS the neighbouring tokens
|
|
* together. `"Ferritin\nSYSTEM: …"` collapsed to `"FerritinSYSTEM: …"`, where
|
|
* `\bsystem\s*:` no longer matches — so the payload survived precisely
|
|
* BECAUSE it was preceded by a newline, which is the most natural way to
|
|
* write it. Substituting a space preserves the boundary the patterns need.
|
|
* Order is load-bearing: normalise whitespace first, then strip patterns.
|
|
*/
|
|
export function sanitizeForPrompt(input: string, maxLength = 100): string {
|
|
return (
|
|
input
|
|
// Replace control characters with a space so adjacent tokens stay
|
|
// separated (see the note above — deletion defeats the `\b` anchors).
|
|
.replace(/[\x00-\x1f\x7f]/g, " ")
|
|
// Collapse the runs the substitution just created.
|
|
.replace(/\s+/g, " ")
|
|
// Remove common prompt injection patterns
|
|
.replace(/```/g, "")
|
|
.replace(/\bsystem\s*:/gi, "")
|
|
.replace(/\bassistant\s*:/gi, "")
|
|
.replace(/\buser\s*:/gi, "")
|
|
.replace(/\bignore\s+(previous|above|all)\b/gi, "")
|
|
.replace(/\bforget\s+(previous|above|all)\b/gi, "")
|
|
// Collapse again — pattern removal leaves gaps behind.
|
|
.replace(/\s+/g, " ")
|
|
// Limit length
|
|
.slice(0, maxLength)
|
|
.trim()
|
|
);
|
|
}
|