{"record":{"id":"56ed9a7dfbaa21ff","repo":"ruvnet/ruflo","slug":"decryptbuffer-bad-magic-blob-is-not-ruflo-encry","errorCode":null,"errorMessage":"decryptBuffer: bad magic — blob is not Ruflo-encrypted (RFE1)","messagePattern":"decryptBuffer: bad magic — blob is not Ruflo-encrypted \\(RFE1\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/encryption/vault.ts","lineNumber":163,"sourceCode":" * garbage.\n */\nexport function decryptBuffer(blob: Buffer, key: Buffer): Buffer {\n  if (!Buffer.isBuffer(blob)) {\n    throw new TypeError('decryptBuffer: blob must be a Buffer');\n  }\n  if (!Buffer.isBuffer(key) || key.length !== KEY_LEN) {\n    throw new TypeError(`decryptBuffer: key must be a ${KEY_LEN}-byte Buffer`);\n  }\n  if (blob.length < MIN_BLOB_LEN) {\n    throw new Error(\n      `decryptBuffer: blob too short (${blob.length}B; need >= ${MIN_BLOB_LEN}B)`,\n    );\n  }\n  const magic = blob.subarray(0, MAGIC_LEN);\n  // timingSafeEqual to avoid an oracle on the magic bytes specifically;\n  // not strictly required (the magic isn't secret) but cheap and correct.\n  if (!timingSafeEqual(magic, MAGIC)) {\n    throw new Error(\n      'decryptBuffer: bad magic — blob is not Ruflo-encrypted (RFE1)',\n    );\n  }\n  const iv = blob.subarray(MAGIC_LEN, MAGIC_LEN + IV_LEN);\n  const tag = blob.subarray(blob.length - TAG_LEN);\n  const ciphertext = blob.subarray(MAGIC_LEN + IV_LEN, blob.length - TAG_LEN);\n\n  const decipher = createDecipheriv(ALG, key, iv);\n  decipher.setAuthTag(tag);\n  return Buffer.concat([decipher.update(ciphertext), decipher.final()]);\n}\n\n/**\n * Magic-byte sniff. True iff the blob starts with the RFE1 magic AND is\n * long enough to be a valid encrypted blob. Used by readers during the\n * incremental migration: legacy plaintext files return false and flow\n * through the existing read path unchanged.\n *","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/encryption/vault.ts#L145-L181","documentation":"Thrown by decryptBuffer() when the first 4 bytes of the blob do not equal the MAGIC bytes 'RFE1' (compared with timingSafeEqual). The blob is long enough to have a header but is not a Ruflo-encrypted blob — typically a plaintext value or ciphertext from a different/older encryption scheme. The guard exists so a mistaken plaintext input fails loudly rather than yielding garbage plaintext.","triggerScenarios":"Passing a plaintext secret (never encrypted) to decryptBuffer, passing ciphertext produced by a different tool or an older format without the RFE1 magic, or a buffer that begins mid-stream.","commonSituations":"Migrating from a previous non-vault secret store and forgetting to encrypt first, a field that is sometimes plaintext and sometimes encrypted, or a down-level vault format predating the magic header.","solutions":["Gate decryption with isEncryptedBlob() and skip plaintext values (or migrate them) rather than unconditionally decrypting.","If migrating an old format, write a one-time converter that re-encrypts under the current scheme.","Confirm the file actually came from encryptBuffer() (the only writer of the RFE1 magic)."],"exampleFix":"// before\ndecryptBuffer(maybeCiphertext, key)   // bad magic\n// after\nimport { isEncryptedBlob } from '../encryption/vault.js';\nconst out = isEncryptedBlob(maybeCiphertext)\n  ? decryptBuffer(maybeCiphertext, key)\n  : maybeCiphertext; // already plaintext","handlingStrategy":"type-guard","validationCode":"import { isEncryptedBlob } from '../encryption/vault.js';\nfunction readSecret(buf: Buffer, key: Buffer): Buffer {\n  return isEncryptedBlob(buf) ? decryptBuffer(buf, key) : buf; // plaintext passthrough\n}","typeGuard":"import { isEncryptedBlob } from '../encryption/vault.js';\nconst isRufloCiphertext = (b: Buffer): boolean => isEncryptedBlob(b);","tryCatchPattern":"try {\n  decryptBuffer(blob, key);\n} catch (e) {\n  const msg = e instanceof Error ? e.message : String(e);\n  if (msg.includes('bad magic')) {\n    // probably plaintext from a legacy store; handle migration\n    return blob;\n  }\n  throw e;\n}","preventionTips":["Gate decryption with isEncryptedBlob(); pass through plaintext.","Migrate legacy secrets through encryptBuffer() once.","Confirm the blob came from the vault's encryptBuffer (the only RFE1 writer)."],"tags":["encryption","validation","vault","buffer"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}