Keep archive entries inside their extraction directory

An import job writes archive entry names directly beneath its work directory. An entry containing ../ can escape. Repair the entry-name policy before any file is created.

Requires Node.js 22 or newer. No package installation or server needed. To work in a container instead, open the folder with VS Code Dev Containers or GitHub Codespaces: .devcontainer/devcontainer.json uses the Dev Containers Node.js 24 image, pinned by digest.
1. Unzip all files into one folder.
2. In that folder, run: node --test exercise.test.mjs
3. Edit archive-policy.mjs, then run the same command again.

The starter deliberately fails some tests. A successful repair passes every case, including cases that catch common wrong fixes. The browser runs this same cases.mjs with the same fixtures; locally Node runs your code directly. If you completed the optional first stage in the browser, Download my work adds your input as stage1-input.json and the tests include it. Only run code you wrote or reviewed.

1. Find the escape
Run the starter and inspect the parent-directory and symbolic-link cases. A safe-looking suffix does not make a path safe.

2. Define the accepted language
Check that name is a nonempty string, length is bounded and kind is file. Reject control characters, backslashes and colons rather than trying to repair ambiguous paths.

3. Validate every segment
Split on /. Reject empty, . and .. segments. This also rejects absolute paths and double separators.

4. Preserve nested files
The documented safe name docs/guide.txt must pass. Rejecting every slash would break legitimate nested imports.

SUPPLIED CONTRACT
allowEntry(entry) returns a boolean. entry.name is an already decoded archive path; entry.kind must be file. The importer accepts only forward-slash-separated relative paths with nonempty segments, no dot segments, no ASCII control characters (U+0000–U+001F or U+007F), no colon, no backslash, and at most 200 UTF-16 code units as measured by JavaScript name.length. Symlinks and other member types are outside this import format.

REFERENCE
reference.mjs contains an explained solution: docs/guide.txt passes because both segments are ordinary relative names. ../outside.txt and docs/../../outside.txt fail when any segment equals ..; /etc/settings and docs//guide.txt fail because splitting produces an empty segment. A symlink fails even if its name looks safe. This function returns a decision only: it neither resolves a filesystem path nor proves protection against existing symlinks, write races, duplicates or decompression limits.

SCOPE
Name filtering is one boundary. A real extractor must also avoid existing symlinks and races, verify the final filesystem destination, bound count and expanded bytes, and extract in an isolated directory. These tests execute the policy; they do not create files or validate an archive library.

NEXT TASK
How does your extraction library identify symlinks, hard links, duplicate entries and decompression limits?

SOURCE
https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html
