{"record":{"id":"8eab48b20b7e92a1","repo":"abhigyanpatwari/GitNexus","slug":"source-value-contains-control-or-hidden-bidire","errorCode":null,"errorMessage":"${source}: value contains control or hidden/bidirectional characters, which are not allowed.","messagePattern":"(.+?): value contains control or hidden/bidirectional characters, which are not allowed\\.","errorType":"validation","errorClass":"GitNexusRcError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/cli/analyze-config.ts","lineNumber":146,"sourceCode":" * Reject control characters and hidden / bidirectional Unicode in a string\n * value. These have no legitimate place in a branch name, registry name, or\n * device string, and would otherwise let a committed config smuggle invisible\n * controls into generated AGENTS.md / CLAUDE.md content.\n */\nconst isHiddenOrControl = (codePoint: number): boolean =>\n  codePoint < 0x20 ||\n  codePoint === 0x7f ||\n  (codePoint >= 0x200b && codePoint <= 0x200f) || // zero-width + LRM/RLM\n  (codePoint >= 0x202a && codePoint <= 0x202e) || // bidi embeddings/overrides\n  (codePoint >= 0x2060 && codePoint <= 0x2064) || // word-joiner + invisible math\n  (codePoint >= 0x2066 && codePoint <= 0x206f) || // bidi isolates + deprecated\n  codePoint === 0xfeff; // BOM / zero-width no-break space\n\nconst assertNoHiddenChars = (value: string, source: string): void => {\n  for (const ch of value) {\n    const cp = ch.codePointAt(0);\n    if (cp !== undefined && isHiddenOrControl(cp)) {\n      throw new GitNexusRcError(\n        `${source}: value contains control or hidden/bidirectional characters, which are not allowed.`,\n      );\n    }\n  }\n};\n\n/**\n * Validate a user-supplied branch name (from CLI or `.gitnexusrc`). Returns the\n * trimmed name or throws {@link GitNexusRcError}. Conservative but accepts the\n * shapes real branches use (`feature/foo-bar`, `release/1.2`, `develop`).\n */\nexport function validateBranchName(value: string, source: string): string {\n  const trimmed = value.trim();\n  if (!trimmed) {\n    throw new GitNexusRcError(`${source}: branch name must not be empty.`);\n  }\n  if (trimmed.length > BRANCH_MAX_LENGTH) {\n    throw new GitNexusRcError(`${source}: branch name is too long (max ${BRANCH_MAX_LENGTH}).`);","sourceCodeStart":128,"sourceCodeEnd":164,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/d540b00184d71a896261ee02670da9a92d59d8f7/gitnexus/src/cli/analyze-config.ts#L128-L164","documentation":"Thrown by assertNoHiddenChars() (reachable from validateBranchName, the 'string' config kind, and the 'string-array' kind) when a config value contains a control character (< 0x20), DEL (0x7f), zero-width characters, bidirectional overrides, or a BOM. These characters are invisible to reviewers but can alter how generated AGENTS.md/CLAUDE.md content is interpreted by an agent, so they are rejected at the single chokepoint before any file is written.","triggerScenarios":"A .gitnexusrc value containing a pasted zero-width space (U+200B), a bidi override (U+202E), a stray tab/newline inside a string, or a BOM (U+FEFF) carried over from a Windows editor copy-paste.","commonSituations":"Copying a branch or repo name from a web page or chat client that inserted a zero-width joiner; committing a config file edited in an editor that saved a BOM inside a string value; a malicious or accidental homoglyph attack on agent instructions.","solutions":["Re-type the value by hand instead of copy-pasting from a rich-text source.","Strip invisible characters before committing: run the value through a sanitizer that removes U+200B-200F, U+202A-202E, U+2060-206F, and U+FEFF.","Inspect the file with a hex viewer or `cat -A` to locate the offending byte, then remove it."],"exampleFix":"// before (.gitnexusrc, branch name has a trailing zero-width space)\n{ \"defaultBranch\": \"main\\u200b\" }\n\n// after\n{ \"defaultBranch\": \"main\" }","handlingStrategy":"validation","validationCode":"function stripHiddenChars(value: string): string {\n  return Array.from(value)\n    .filter((ch) => {\n      const cp = ch.codePointAt(0)!;\n      return !(cp < 0x20 || cp === 0x7f ||\n        (cp >= 0x200b && cp <= 0x200f) ||\n        (cp >= 0x202a && cp <= 0x202e) ||\n        (cp >= 0x2060 && cp <= 0x2064) ||\n        (cp >= 0x2066 && cp <= 0x206f) ||\n        cp === 0xfeff);\n    })\n    .join('');\n}","typeGuard":"function isFreeOfHiddenChars(value: string): boolean {\n  for (const ch of value) {\n    const cp = ch.codePointAt(0)!;\n    if (cp < 0x20 || cp === 0x7f ||\n        (cp >= 0x200b && cp <= 0x200f) ||\n        (cp >= 0x202a && cp <= 0x202e) ||\n        (cp >= 0x2060 && cp <= 0x2064) ||\n        (cp >= 0x2066 && cp <= 0x206f) ||\n        cp === 0xfeff) return false;\n  }\n  return true;\n}","tryCatchPattern":null,"preventionTips":["Avoid copy-pasting branch/repo names from chat clients or web pages that insert zero-width chars.","Sanitize editor BOM settings: save .gitnexusrc as UTF-8 without BOM.","Run `grep -P '[\\x{200b}-\\x{200f}\\x{202a}-\\x{202e}]' .gitnexusrc` before committing to catch invisible chars."],"tags":["validation","security","unicode","config","markdown-injection"],"backgroundTag":null,"analyzedSha":"d540b00184d71a896261ee02670da9a92d59d8f7","analyzedAt":"2026-08-12T19:50:25.132Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}