{"record":{"id":"9486e3f27b89d0cb","repo":"ruvnet/ruflo","slug":"path-contains-null-bytes","errorCode":null,"errorMessage":"Path contains null bytes","messagePattern":"Path contains null bytes","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/appliance/rvfa-format.ts","lineNumber":387,"sourceCode":"    const sorted = [...header.sections].sort((a, b) => a.offset - b.offset);\n    for (let i = 1; i < sorted.length; i++) {\n      const prev = sorted[i - 1];\n      const curr = sorted[i];\n      if (prev.offset + prev.size > curr.offset) {\n        throw new Error(\n          `Sections \"${prev.id}\" and \"${curr.id}\" overlap ` +\n            `(${prev.offset}+${prev.size} > ${curr.offset})`,\n        );\n      }\n    }\n\n    return new RvfaReader(buf, header);\n  }\n\n  /** Read an RVFA image from a file path. */\n  static async fromFile(path: string): Promise<RvfaReader> {\n    if (path.includes('\\0')) {\n      throw new Error('Path contains null bytes');\n    }\n    const data = await readFile(path);\n    return RvfaReader.fromBuffer(data);\n  }\n\n  /** Return the parsed header. */\n  getHeader(): RvfaHeader {\n    return this.header;\n  }\n\n  /** List all sections declared in the header. */\n  getSections(): RvfaSection[] {\n    return this.header.sections;\n  }\n\n  /**\n   * Extract and decompress a section by its id.\n   *","sourceCodeStart":369,"sourceCodeEnd":405,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/appliance/rvfa-format.ts#L369-L405","documentation":"RvfaReader.fromFile rejects any path containing a NUL byte ('\\0') before handing it to readFile. On POSIX systems an embedded NUL truncates the effective path at the C-string boundary, which is a classic path-injection primitive (e.g. 'safe-dir/../../etc/passwd\\0.png' being treated as 'safe-dir/../../etc/passwd'). Node's fs would throw its own opaque error; this guard fails fast with an explicit message.","triggerScenarios":"Calling await RvfaReader.fromFile(path) where path includes '\\0' — typically because the path came from untrusted user input, a URL-decoded string, or upstream data containing raw binary/control bytes.","commonSituations":"Paths assembled from CLI arguments, HTTP parameters, or archive entry names without sanitization; binary data accidentally concatenated into a path buffer; test harnesses feeding fuzzed filenames. Legitimate filesystem paths never contain NUL, so seeing this error always means tainted input.","solutions":["Reject or sanitize the path at the input boundary before it reaches fromFile — strip control characters, not just NUL","Trace where the string originated (URL query, env var, file listing) and validate/encode it there; decodeURIComponent on attacker-controlled input is a common source","Return a 400-style error to the caller rather than retrying — the input is malformed, not transient","Prefer allowlisting (e.g. /^[\\w.-]+\\.rvfa$/) over blocklisting for filenames you accept"],"exampleFix":"// before — raw external input straight to fromFile\nconst reader = await RvfaReader.fromFile(req.query.path);\n\n// after — validate the shape at the boundary\nconst m = String(req.query.path ?? '').match(/^[\\w./-]+\\.rvfa$/);\nif (!m) throw new Error('invalid image path');\nconst reader = await RvfaReader.fromFile(m[0]);","handlingStrategy":"validation","validationCode":"function isSafeImagePath(p: string): boolean {\n  return typeof p === 'string' && !p.includes('\\0') && /^[\\w./-]+\\.rvfa$/.test(p);\n}","typeGuard":"function isNullByteFreePath(p: unknown): p is string {\n  return typeof p === 'string' && !p.includes('\\0');\n}","tryCatchPattern":"try { reader = await RvfaReader.fromFile(path); }\ncatch (e) {\n  if (e instanceof Error && e.message === 'Path contains null bytes') {\n    // reject the request/input; never sanitize by stripping \\0 and continuing blindly\n  }\n  throw e;\n}","preventionTips":["Validate paths at the trust boundary with an allowlist pattern, not a NUL blocklist","Never build paths from raw URL-decoded or archive-entry strings","Log and reject (400) rather than cleaning tainted filenames"],"tags":["rvfa","path-injection","security","null-byte"],"backgroundTag":"null-byte-path-injection","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}