{"record":{"id":"8cc0edf1edd83b9f","repo":"affaan-m/ECC","slug":"memory-body-must-not-contain-unsafe-control-or-bid","errorCode":null,"errorMessage":"memory body must not contain unsafe control or bidirectional formatting characters.","messagePattern":"memory body must not contain unsafe control or bidirectional formatting characters\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/lib/memory-vault-format.js","lineNumber":149,"sourceCode":"function validateTimestamp(value, label) {\n  const normalized = asNonEmptyString(value, label, 64);\n  const parsed = new Date(normalized);\n  if (\n    !ISO_TIMESTAMP_PATTERN.test(normalized)\n    || Number.isNaN(parsed.getTime())\n    || parsed.toISOString() !== normalized\n  ) {\n    throw new Error(`${label} must be an ISO-8601 timestamp.`);\n  }\n  return normalized;\n}\n\nfunction normalizeBody(value) {\n  if (typeof value !== 'string') {\n    throw new Error('memory body must be a string.');\n  }\n  if (hasUnsafeControlCharacters(value, true)) {\n    throw new Error('memory body must not contain unsafe control or bidirectional formatting characters.');\n  }\n  const normalized = value.trim();\n  if (normalized.length === 0) {\n    throw new Error('memory body must contain non-whitespace context.');\n  }\n  if (Buffer.byteLength(normalized, 'utf8') > MAX_BODY_BYTES) {\n    throw new Error(`memory body is too large (maximum ${MAX_BODY_BYTES} bytes).`);\n  }\n  return normalized;\n}\n\nfunction normalizeMemory(memory) {\n  if (!memory || typeof memory !== 'object' || Array.isArray(memory)) {\n    throw new Error('memory must be an object.');\n  }\n\n  const targetHarnesses = uniqueStrings(memory.targetHarnesses, {\n    label: 'target harnesses',","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/memory-vault-format.js#L131-L167","documentation":"Thrown by normalizeBody() via hasUnsafeControlCharacters(value, true) when the body contains C0 control bytes (U+0000–U+001F except TAB/LF/CR), DEL/C1 range (U+007F–U+009F), or Unicode bidirectional formatting characters (U+202A–U+202E RLE/LRE/RLO/LRO/PDF, U+2066–U+2069 LRI/RLI/FSI/PDI). These characters enable trojan-source attacks and terminal/log injection, so they are rejected even when the rest of the body is valid text.","triggerScenarios":"Pasting memory content from a terminal capture that includes ANSI escape sequences (\\x1b[31m). Copying source code that contains embedded zero-width or RTL override characters. Body scraped from a webpage with bidirectional markup. Body that includes a literal form-feed (\\f) or vertical tab (\\v).","commonSituations":"User pastes rich text from a word processor that smuggles in U+200E/U+200F marks. Build tool emits progress bars with carriage-return/backspace into a captured log that gets saved as a memory. AI agent saves a code snippet that itself contains obfuscated unicode to evade detection.","solutions":["Strip control/bidi characters before saving: body = body.replace(/[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F\\u202A-\\u202E\\u2066-\\u2069]/g, '').","Whitelist printable text: keep only /^\\u0009\\u000A\\u000D\\u0020-\\uFFFF$/u per character.","If you genuinely need ANSI escapes, base64-encode that section first.","Run the input through a sanitiser like the library's exported hasUnsafeControlCharacters() to detect issues before calling saveMemory."],"exampleFix":"// before\nsaveMemory({ title: 'log', body: rawCliCapture }); // contains \\x1b[31m red escapes\n\n// after\nconst { hasUnsafeControlCharacters } = require('./scripts/lib/memory-vault-format');\nconst clean = rawCliCapture.replace(/[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F\\u202A-\\u202E\\u2066-\\u2069]/g, '');\nif (hasUnsafeControlCharacters(clean, true)) throw new Error('could not sanitize');\nsaveMemory({ title: 'log', body: clean });","handlingStrategy":"validation","validationCode":"const { hasUnsafeControlCharacters } = require('./scripts/lib/memory-vault-format');\nif (hasUnsafeControlCharacters(input.body, true)) {\n  input.body = input.body.replace(/[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F-\\u009F\\u202A-\\u202E\\u2066-\\u2069]/g, '');\n}\nsaveMemory(input);","typeGuard":"import { hasUnsafeControlCharacters } from './scripts/lib/memory-vault-format';\nfunction isSafeBody(value): value is string {\n  return typeof value === 'string' && !hasUnsafeControlCharacters(value, true);\n}","tryCatchPattern":null,"preventionTips":["Treat any paste from terminals, browsers, or rich-text editors as untrusted; sanitise before saving.","Run hasUnsafeControlCharacters() on incoming text and reject or strip before reaching saveMemory().","Ban binary/base64 blobs in body — store them out-of-band and reference by path."],"tags":["security","injection","validation","memory-vault","unicode"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}