We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Safe Archive Extraction

The fix for our file-path problems is to validate each entry name after resolving its final absolute path. Then use the relative path from the trusted extraction root to check whether the destination escapes it:

import { isAbsolute, relative, resolve, sep } from "node:path";

const root = resolve(extractionDirectory);
const destination = resolve(root, entryName);
const relativePath = relative(root, destination);

if (
  relativePath === "" ||
  relativePath === ".." ||
  relativePath.startsWith(`..${sep}`) ||
  isAbsolute(relativePath)
) {
  throw new Error("Archive entry escapes the extraction directory");
}

Node's path.resolve produces an absolute destination, and path.relative describes how to reach it from the trusted root. If the relative path is:

  • Empty, the destination is the root itself (bad).
  • ..: the destination is the root's parent (bad).
  • Starts with .. plus the platform's path.sep: the destination is outside the root (bad).
  • Absolute: on Windows, the destination may be on another drive (bad).

Validate Before Writing

When it comes to archives, always validate all entry destinations before extracting any of them. If the fifth entry is malicious, but you only discover that after writing the first four, you'll get a partial extraction and leave the system in an inconsistent state.

Zip Bombs

Archive extraction also needs resource limits. Compressed input can expand dramatically, so production systems should cap the number of entries and total uncompressed size. If you don't, you're open to zip bomb attacks. They don't leak anything, but they can bring down your servers' ability to respond by exhausting their disk space or memory as a type of denial-of-service attack.

Assignment

Bearly Secure resolves every ZIP entry before it writes files, but its isInsideDirectory helper trusts every destination. Keep bulk tax-document extraction inside data/bulk-tax-documents.

Run and submit the CLI tests from the project root.