Best Practices for Handling Unrar in Node.js

Extracting RAR archives in Node.js typically involves interacting with external CLI utilities or WebAssembly-based wrappers, both of which require robust output management. This guide covers the essential practices for handling unrar output effectively, focusing on stream management, accurate error detection, progress tracking, and security measures to ensure stable archive extraction in production environments.

Prefer spawn Over exec for CLI Execution

When running the system unrar binary via the child_process module, always use spawn rather than exec. The exec method buffers all standard output (stdout) and standard error (stderr) into memory up to a default limit (typically 1 MB). If an archive contains thousands of files, unrar will exceed this buffer, causing the process to crash with an ERR_CHILD_PROCESS_STDIO_MAXBUFFER error.

Using spawn streams the output chunk-by-chunk:

const { spawn } = require('child_process');

const unrar = spawn('unrar', ['x', '-y', 'archive.rar', 'destination/']);

unrar.stdout.on('data', (chunk) => {
  // Handle streaming output incrementally
});

Parse Streams Line-by-Line

stdout chunks emitted by a Node.js stream do not always correspond to single lines of text; a chunk might cut off mid-word or contain multiple lines. To reliably parse output for file names, percentage updates, or status messages, pipe the stream through the built-in readline module:

const readline = require('readline');

const rl = readline.createInterface({
  input: unrar.stdout,
  terminal: false
});

rl.on('line', (line) => {
  if (line.startsWith('Extracting ')) {
    const filename = line.replace('Extracting ', '').trim();
    // Track extracted file
  }
});

Rely on Exit Codes Instead of stderr for Errors

CLI tools often write diagnostic information, notices, or warnings to stderr even when an operation succeeds. Do not assume an extraction failed simply because stderr received data. Instead, monitor the process close or exit event to evaluate the numeric exit code.

Common unrar exit codes include:

  • 0: Successful operation.
  • 1: Non-fatal error (such as minor warnings).
  • 2: Fatal error (corrupt archive, read error).
  • 3: CRC error (checksum verification failed).
  • 4: Attempted to modify a locked archive.
  • 5: Write error (disk full or permission denied).
  • 6: File open error.
  • 7: Wrong command-line syntax.
  • 8: Memory error.

Capture stderr in a variable during runtime, but only log or throw it if the process exits with a non-zero status code:

let errorOutput = '';

unrar.stderr.on('data', (data) => {
  errorOutput += data.toString();
});

unrar.on('close', (code) => {
  if (code !== 0) {
    throw new Error(`Unrar failed with code ${code}: ${errorOutput}`);
  }
});

Defend Against Path Traversal (Zip Slip)

Maliciously crafted RAR files may contain filenames with relative directory paths (such as ../../etc/passwd) designed to write files outside the intended destination directory. If you are handling output or manually extracting entries using libraries like node-unrar-js:

  1. Resolve the absolute path of the target extraction folder.
  2. Resolve the absolute path of each incoming file path.
  3. Verify that the file's target path begins strictly with the extraction directory's base path before allowing the write operation.

Consider In-Memory WebAssembly Alternatives

If installing the native unrar CLI binary on your deployment environment is impractical or restricted, use WebAssembly-compiled alternatives such as node-unrar-js. When using WebAssembly libraries, output is handled via JavaScript promises and typed arrays rather than standard OS streams:

  • Extract metadata first using non-extracting methods to inspect archive contents before unpacking.
  • Process file entries sequentially to avoid consuming excessive heap memory when dealing with large archives.
  • Explicitly release memory buffers once an entry has been written to disk to allow the Node.js garbage collector to reclaim heap space.