* Core sanitization function without mention filtering * @param {string} content - The content to sanitize * @param {number} [maxLength] - Maximum length of content (default: 524288) * @param {number} [maxBotMentions] - Max bot trigger references before filtering (default: MAX_BOT_TRIGGER_REFEREN
(content, maxLength, maxBotMentions)
| 1248 | * @returns {string} The sanitized content |
| 1249 | */ |
| 1250 | function sanitizeContentCore(content, maxLength, maxBotMentions) { |
| 1251 | if (!content || typeof content !== "string") { |
| 1252 | return ""; |
| 1253 | } |
| 1254 | |
| 1255 | // Build list of allowed domains from environment and GitHub context |
| 1256 | const allowedDomains = buildAllowedDomains(); |
| 1257 | |
| 1258 | // Build list of allowed GitHub references from environment |
| 1259 | const allowedGitHubRefs = buildAllowedGitHubReferences(); |
| 1260 | |
| 1261 | let sanitized = content; |
| 1262 | |
| 1263 | // Apply Unicode hardening first to normalize text representation |
| 1264 | // This prevents Unicode-based attacks and ensures consistent processing |
| 1265 | sanitized = hardenUnicodeText(sanitized); |
| 1266 | |
| 1267 | // Remove ANSI escape sequences and control characters early |
| 1268 | // This must happen before mention neutralization to avoid creating bare mentions |
| 1269 | // when control characters are removed between @ and username |
| 1270 | sanitized = sanitized.replace(/\x1b\[[0-9;]*[mGKH]/g, ""); |
| 1271 | // Remove control characters except newlines (\n), tabs (\t), and carriage returns (\r) |
| 1272 | sanitized = sanitized.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, ""); |
| 1273 | |
| 1274 | // Neutralize commands at the start of text (e.g., /bot-name) |
| 1275 | sanitized = neutralizeCommands(sanitized); |
| 1276 | |
| 1277 | // Remove XML comments before mention neutralization to prevent bypass: if removeXmlComments |
| 1278 | // ran after neutralizeAllMentions, a comment like <!-- @user payload --> would first become |
| 1279 | // <!-- `@user` payload --> and applyFnOutsideInlineCode would split at the backtick boundary, |
| 1280 | // preventing the full <!--...--> pattern from being matched. |
| 1281 | sanitized = applyToNonCodeRegions(sanitized, removeXmlComments); |
| 1282 | |
| 1283 | // Remove markdown link titles — a steganographic injection channel analogous to HTML comments. |
| 1284 | // Quoted title text ([text](url "TITLE") and [ref]: url "TITLE") is invisible in GitHub's |
| 1285 | // rendered markdown (shown only as hover-tooltips) but reaches the AI model verbatim. |
| 1286 | // Must run before mention neutralization for the same ordering reason as removeXmlComments. |
| 1287 | sanitized = applyToNonCodeRegions(sanitized, neutralizeMarkdownLinkTitles); |
| 1288 | |
| 1289 | // Neutralize ALL @mentions (no filtering in core version) |
| 1290 | sanitized = neutralizeAllMentions(sanitized); |
| 1291 | |
| 1292 | // Convert XML tags to parentheses format – skip code blocks and inline code so that |
| 1293 | // type parameters (e.g. VBuffer<float32>) and code containing angle brackets are preserved |
| 1294 | sanitized = applyToNonCodeRegions(sanitized, convertXmlTags); |
| 1295 | |
| 1296 | // URI filtering - replace non-https protocols with "(redacted)" |
| 1297 | sanitized = applyURLSanitizationPolicy(sanitized, allowedDomains); |
| 1298 | |
| 1299 | // Apply truncation limits |
| 1300 | sanitized = applyTruncation(sanitized, maxLength); |
| 1301 | |
| 1302 | // Neutralize GitHub references if restrictions are configured |
| 1303 | sanitized = neutralizeGitHubReferences(sanitized, allowedGitHubRefs); |
| 1304 | |
| 1305 | // Neutralize common bot trigger phrases |
| 1306 | sanitized = neutralizeBotTriggers(sanitized, maxBotMentions); |
| 1307 |
no test coverage detected