{"record":{"id":"dec7ab12d56eb10a","repo":"can1357/oh-my-pi","slug":"encrypted-arj-archives-are-unsupported","errorCode":null,"errorMessage":"Encrypted ARJ archives are unsupported","messagePattern":"Encrypted ARJ archives are unsupported","errorType":"exception","errorClass":"ArchiveError","httpStatus":null,"severity":"error","filePath":"packages/utils/src/ar/arj.ts","lineNumber":248,"sourceCode":"\tassertInMemorySize(source.size, options.limits);\n\tlet bytes: Uint8Array;\n\ttry {\n\t\tbytes = await readAllBytes(source);\n\t} catch (error) {\n\t\tif (error instanceof ArchiveError) throw error;\n\t\tthrow new ArchiveError(`Unable to read ARJ archive: ${error instanceof Error ? error.message : String(error)}`);\n\t}\n\tif (bytes.byteLength !== source.size) throw new ArchiveError(\"Invalid ARJ archive: truncated data\");\n\tif (!sniffArj(bytes)) throw new ArchiveError(\"Invalid ARJ archive header\");\n\n\tconst main = parseArjBlock(bytes, 0, options);\n\tif (main.isEnd) throw new ArchiveError(\"Invalid ARJ archive: missing main header\");\n\tconst mainFirstHeaderSize = bytes[main.bodyStart]!;\n\tif (mainFirstHeaderSize < 30 || mainFirstHeaderSize > main.bodySize || bytes[main.bodyStart + 6] !== 2) {\n\t\tthrow new ArchiveError(\"Invalid ARJ main header\");\n\t}\n\tconst mainFlags = bytes[main.bodyStart + 4]!;\n\tif ((mainFlags & 0x01) !== 0) throw new ArchiveError(\"Encrypted ARJ archives are unsupported\");\n\tif ((mainFlags & 0x04) !== 0) throw new ArchiveError(\"Multi-volume ARJ archives are unsupported\");\n\n\tconst entries: ArchiveIndexEntry[] = [];\n\tlet offset = main.nextOffset;\n\tlet metadataSize = main.metadataSize;\n\tlet parsedCount = 0;\n\tfor (;;) {\n\t\tconst block = parseArjBlock(bytes, offset, options);\n\t\tmetadataSize += block.metadataSize;\n\t\tassertIndexSize(metadataSize, options.limits, \"index\");\n\t\tif (block.isEnd) break;\n\t\tassertEntryCount(++parsedCount, options.limits);\n\t\tconst firstHeaderSize = bytes[block.bodyStart]!;\n\t\tif (firstHeaderSize < 30 || firstHeaderSize > block.bodySize) throw new ArchiveError(\"Invalid ARJ local header\");\n\t\tconst hostOs = bytes[block.bodyStart + 3]!;\n\t\tconst flags = bytes[block.bodyStart + 4]!;\n\t\tconst method = bytes[block.bodyStart + 5]!;\n\t\tconst fileType = bytes[block.bodyStart + 6]!;","sourceCodeStart":230,"sourceCodeEnd":266,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/ar/arj.ts#L230-L266","documentation":"The ARJ reader in readArj validates the flags byte of the archive's main (archive) header. Bit 0 (0x01) of that byte marks the entire archive as password-encrypted. This library does not implement ARJ decryption, so it refuses the archive up front instead of producing unreadable members.","triggerScenarios":"readArj (the ARJ FormatReader) is called on a buffer whose main header at bodyStart has flags byte (bytes[bodyStart+4]) with bit 0x01 set — i.e. an ARJ archive created with a password ('-g' password option in ARJ tooling).","commonSituations":"Processing legacy ARJ archives that were password-protected for distribution; archives shared with the password communicated out-of-band (email, chat) that the caller never supplied; automated ingest of old shareware-era archives.","solutions":["Decrypt the archive with the original ARJ tooling (arj x -g<password>) or an external tool, then feed the plaintext archive to the library.","Obtain the password from the archive's source and pre-process it out of band; this library will not decrypt.","If encrypted archives are expected, detect this error and route to a pipeline/tool that supports ARJ encryption, or reject the input earlier with your own pre-check.","If the archive is NOT actually sensitive and you control creation, regenerate it without the password option."],"exampleFix":"// before\nconst entries = await readArj(source, options); // throws on password-protected ARJ\n// after\nif (!hasArjPassword(source)) {\n  const entries = await readArj(source, options);\n} else {\n  const plain = await decryptArjExternally(source, password);\n  const entries = await readArj(plain, options);\n}","handlingStrategy":"validation","validationCode":"function isArjEncrypted(bytes: Uint8Array): boolean {\n\t// main header located by the same walk readArj performs; at bodyStart:\n\t// byte 4 is the flags byte, bit 0x01 = encrypted archive\n\tconst flags = bytes[mainHeaderBodyStart(bytes) + 4] ?? 0;\n\treturn (flags & 0x01) !== 0;\n}\nif (isArjEncrypted(bytes)) throw new Error(\"Rejecting password-protected ARJ before parse\");","typeGuard":"function isPlaintextArjFlags(flags: number): boolean {\n\treturn (flags & 0x01) === 0;\n}","tryCatchPattern":"try {\n\tentries = await readArj(source, options);\n} catch (e) {\n\tif (e instanceof ArchiveError && e.message === \"Encrypted ARJ archives are unsupported\") {\n\t\treturn { status: \"needs-external-decrypt\", archive: path };\n\t}\n\tthrow e;\n}","preventionTips":["Pre-scan the ARJ main header flags byte before parsing and reject encrypted inputs in your ingest pipeline.","Document to archive producers that password-protected ARJ files are not accepted.","Keep the decryption step (external ARJ tool) as an explicit, separate pipeline stage with password management."],"tags":["archive","arj","encryption","unsupported-feature"],"backgroundTag":"encrypted-archive-unsupported","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}