Read the docs
TI-2026-67280 Aliases GHSA-p498-v437-472g

humanfs: Recursive copy follows symlinked files and copies data from outside the source tree

Priority LOW 38CWE-22Published · Updated

Key facts

Attack vector
—
Privileges required
—
User interaction
—
Attack complexity
—
Impact type
—
Confidentiality · integrity · availability
—
Fix status
Fixed

No CVSS vector from any source. Fix status from GitHub.

Summary

### Summary `@humanfs/node` does not treat symlinks as a separate case during copy operations. A symlink placed inside an attacker-controlled source tree can make `copyAll()` read and copy the contents of any file readable by the process, even when that file is outside the directory being copied. ### Details The Node implementation exposes symlink state through `list()`, but `copyAll()` ignores it. During recursive copies, every non-directory entry is passed to `copy()`, which delegates to `fs.promises.copyFile()`. On Node, `copyFile()` dereferences symlinks. As a result, a symlink inside the copied tree is handled as if it were an ordinary file, and the destination receives the contents of the symlink target rather than a copy of the link itself. That breaks the expected boundary of a directory copy. A caller can point `copyAll()` at a seemingly self-contained directory and still end up copying data from elsewhere on the host filesystem if the source tree contains attacker-supplied symlinks. The same dereference behavior also affects `copy()` when the source path itself is a symlink. ### PoC ```js import { NodeHfs } from "@humanfs/node"; import fs from "node:fs/promises"; import path from "node:path"; import os from "node:os"; const root = await fs.mkdtemp(path.join(os.tmpdir(), "humanfs-")); const src = path.join(root, "src"); const dst = path.join(root, "dst"); const secret = path.join(root, "secret.txt"); await fs.mkdir(src); await fs.writeFile(secret, "TOPSECRET"); await fs.symlink(secret, path.join(src, "link.txt")); const hfs = new NodeHfs(); await hfs.copyAll(src, dst); console.log(await fs.readFile(path.join(dst, "link.txt"), "utf8")); // TOPSECRET ``` ### Impact If an application uses `copyAll()` or `copy()` on attacker-controlled paths, a symlink can be used to pull arbitrary readable host files into the copied output. In practice, that can turn a normal workspace copy, export, or packaging step into a file disclosure primitive.

Why it matters now

Fix Fixed @humanfs/node (npm): 0.16.8

SSVC decision points

Not available: CISA has not published SSVC decision points for this record.

Affected products

VendorProductAffected versionsFixed inStatusSource
—@humanfs/node (npm)before 0.16.80.16.8Patch availableGHSA-p498-v437-472g
  • @humanfs/node (npm) Vendor— Affected before 0.16.8 Fixed in 0.16.8 Patch available GHSA-p498-v437-472g

Remediation

  • Fix · GHSA-p498-v437-472g @humanfs/node (npm) Fixed in 0.16.8 Advisory →

Timeline

  1. 2026-10-03 Threatist record created from GHSA-p498-v437-472g

References

Sources

This product uses the NVD API but is not endorsed or certified by the NVD. Sources and licences