{"record":{"id":"ce131978ee95f895","repo":"ruvnet/ruflo","slug":"section-sec-id-has-negative-offset-or-size","errorCode":null,"errorMessage":"Section \"${sec.id}\" has negative offset or size","messagePattern":"Section \"(.+?)\" has negative offset or size","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/appliance/rvfa-format.ts","lineNumber":358,"sourceCode":"    // Parse header JSON\n    const headerSlice = buf.subarray(PREAMBLE_SIZE, PREAMBLE_SIZE + headerLen);\n    let parsed: unknown;\n    try {\n      parsed = JSON.parse(headerSlice.toString('utf-8'));\n    } catch {\n      throw new Error('Failed to parse RVFA header JSON');\n    }\n\n    if (!validateHeader(parsed)) {\n      throw new Error('RVFA header failed validation');\n    }\n    const header = parsed as RvfaHeader;\n\n    // Bounds-check every section offset\n    const totalSize = buf.length;\n    for (const sec of header.sections) {\n      if (sec.offset < 0 || sec.size < 0) {\n        throw new Error(`Section \"${sec.id}\" has negative offset or size`);\n      }\n      if (sec.offset + sec.size > totalSize - SHA256_SIZE) {\n        throw new Error(\n          `Section \"${sec.id}\" extends beyond buffer ` +\n            `(offset=${sec.offset}, size=${sec.size}, bufLen=${totalSize})`,\n        );\n      }\n    }\n\n    // Check for overlapping sections\n    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})`,","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/appliance/rvfa-format.ts#L340-L376","documentation":"During fromBuffer's per-section bounds pass, a section in the parsed header declared a negative offset or negative size. Offsets/sizes are unsigned quantities by contract; validateHeader only checks they are numbers, so this loop is the explicit sanity gate before any subarray call. A negative value means the header was deliberately malformed (integer-overflow/underflow style payload) or randomly corrupted.","triggerScenarios":"RvfaReader.fromBuffer on a hostile or corrupted image where any header.sections[] entry has offset < 0 or size < 0 — e.g. a fuzzed .rvfa downloaded from an untrusted source, or a header crafted to make sec.offset + sec.size overflow/wrap past the buffer-end check that follows.","commonSituations":"Security testing / fuzzing feeds of appliance images; corrupted headers after bit-rot; untrusted mirrors serving modified images. Note the subsequent check (offset + size > totalSize - 32) can be bypassed with large unsigned values that wrap, so treat this error as a tamper signal, not a fluke.","solutions":["Do not retry or 'fix' the image — a negative offset/size means the header is malformed or malicious; discard the file and re-fetch from a trusted source","Verify the image out-of-band: the 32-byte footer hash and detached Ed25519 signature (rvfa-signing.ts) should be checked before parsing untrusted images","If it's your own writer, audit addSection/build for anything that could compute a negative offset (e.g. subtracting a larger padding from a smaller base)","Add a pre-parse rejection for untrusted inputs using the signing verification APIs before RvfaReader.fromBuffer"],"exampleFix":"// before — parse untrusted bytes directly\nconst reader = await RvfaReader.fromFile(untrustedPath);\n\n// after — verify signature first, then parse\nconst ok = await verifyFile(untrustedPath, trustedPublicKey);\nif (!ok) throw new Error('image signature invalid — refusing to parse');\nconst reader = await RvfaReader.fromFile(untrustedPath);","handlingStrategy":"validation","validationCode":"const parsed = JSON.parse(buf.subarray(12, 12 + buf.readUInt32LE(8)).toString('utf8'));\nconst sane = (parsed.sections ?? []).every(\n  (s: any) => Number.isInteger(s?.offset) && s.offset >= 0 &&\n           Number.isInteger(s?.size) && s.size >= 0,\n);\nif (!sane) throw new Error('malformed section table — treat as tampered');","typeGuard":"function hasSaneSections(h: unknown): h is { sections: Array<{ offset: number; size: number }> } {\n  if (typeof h !== 'object' || h === null || !Array.isArray((h as any).sections)) return false;\n  return (h as any).sections.every(\n    (s: any) => Number.isInteger(s?.offset) && s.offset >= 0 &&\n             Number.isInteger(s?.size) && s.size >= 0,\n  );\n}","tryCatchPattern":"try { reader = RvfaReader.fromBuffer(buf); }\ncatch (e) {\n  if (/negative offset or size/.test(String((e as Error).message))) {\n    // tamper signal: quarantine the file, alert — do not retry\n  }\n  throw e;\n}","preventionTips":["Verify the detached signature before parsing any image from untrusted sources","Never hand-edit section offsets; rebuild with RvfaWriter instead","Treat negative offsets/sizes as a security event, not a data glitch"],"tags":["rvfa","security","malformed-header","binary-format"],"backgroundTag":"malformed-binary-header","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"}