# @npmcli/arborist@9.9.1 security report (npm)

- Verdict: **Needs review** (risk level: medium)
- Scanned: 2026-10-06T14:22:56.000Z
- Files reviewed: 64
- Findings: 1 medium, 15 low severity findings
- Report: https://security.togoder.click/npm/@npmcli/arborist
- Source: Togoder Security (https://security.togoder.click), AI source-code review

## Summary

Togoder Security scanned the npm package @npmcli/arborist@9.9.1 on Oct 6, 2026. An AI review of 64 source files produced 1 medium, 15 low severity findings. The overall verdict is medium: the findings flag risky but common patterns (dynamic code, unsafe defaults, broad file or network access) rather than confirmed malware.

## Findings

### [medium] Dynamic module loading with computed input

Finding ID: `NPS-D47B070F1A17`

File: `bin/index.js:66`

The code dynamically reads all .js files from the current directory and requires them using a computed path: require(`./${file}`). While this is a common CLI pattern for loading command modules, it does execute arbitrary code from any .js file in the bin directory. If an attacker could add a malicious file to this directory, it would be executed. In the context of a third-party package, this is a moderate concern but typical for CLI tools.

### [low] File system manipulation

Finding ID: `NPS-808869B7898F`

File: `bin/actual.js:15`

When options.saveHidden is true, the code sets tree.meta.filename to a path constructed from options.path + '/node_modules/.package-lock.json' and writes to it via tree.meta.save(). This writes to node_modules/.package-lock.json which is outside the typical package scope, though it is within the hybrid tree structure. This could be used to persist configuration or metadata outside expected boundaries.

### [low] Top-level code execution on import

Finding ID: `NPS-330EF84C3885`

File: `bin/index.js:63`

The file executes top-level code immediately when run, including reading the directory, requiring all command files, and dispatching a command. This is expected for a CLI entry point but means any import of this file triggers filesystem reads and module loading.

### [low] File system read outside package scope

Finding ID: `NPS-2A22A22634BE`

File: `bin/index.js:63`

fs.readdirSync(__dirname) reads the bin directory to discover command files. This is scoped to the package's own bin directory, not outside package scope, so it is low risk.

### [low] File system manipulation outside package scope

Finding ID: `NPS-9A041FDE3478`

File: `bin/lib/logging.js:68`

The code writes log output to a user-specified logfile via options.logfile. It creates directories recursively with fs.mkdirSync and opens the file in append mode with fs.openSync. This behavior is expected for a logging utility and is controlled by user configuration, but it does write outside the package directory.

### [low] Top-level code execution at import time

Finding ID: `NPS-66E4763EC6AE`

File: `bin/lib/timers.js`

The module registers event listeners on import, which executes code at load time. However, this is benign and part of the module's intended functionality.

### [low] Custom event listener on process

Finding ID: `NPS-37C361BC5376`

File: `bin/lib/timers.js:7`

The code listens for a non-standard 'time' event on the process object, which is unusual but not inherently malicious; it appears to be an internal API for performance timing.

### [low] Path traversal risk mitigated

Finding ID: `NPS-F295B1B370D8`

File: `lib/arborist/isolated-reifier.js`

Uses nameFromFolder() to sanitize package names before joining into filesystem paths (e.g., `${safeName}@${node.version}` under node_modules/.store). Comments explicitly note stripping path traversal from package.json name fields. This appears to be a defensive measure rather than a vulnerability.

### [low] Package extraction to filesystem

Finding ID: `NPS-CAF23B882D36`

File: `lib/arborist/isolated-reifier.js`

Calls pacote.extract(node.resolved, dir, ...) to download and extract tarballs from registry/resolved URLs into node_modules/.store. This is normal npm Arborist functionality for installing dependencies, not exfiltration, but does write files and make network requests to resolved package URLs.

### [low] Recursive tree recreation with user-controlled options

Finding ID: `NPS-4D0C3F7CE484`

File: `lib/arborist/isolated-reifier.js`

On shrinkwrap nodes, creates a new Arborist instance with `{ ...this.options, path: dir }` and recursively calls buildIdealTree/makeIdealGraph. Options are inherited from the parent npm invocation. No suspicious option injection beyond what npm normally supports.

### [low] No dynamic code execution

Finding ID: `NPS-D9026F03ED62`

File: `lib/arborist/isolated-reifier.js`

No use of eval, new Function, vm, child_process, shell commands, or dynamic requires with computed paths. crypto is used only for hashing (shake256), not for any key/credential material.

### [low] No credential or environment harvesting

Finding ID: `NPS-BA12D425DFF8`

File: `lib/arborist/isolated-reifier.js`

No reads of ~/.npmrc, ~/.ssh, ~/.aws, environment variables, or browser data. No outbound requests beyond expected pacote.extract calls to resolved package tarball URLs.

### [low] Filesystem existence check

Finding ID: `NPS-DB61B83B88C2`

File: `lib/diff.js:121`

The code uses existsSync to check whether binary paths exist as part of diffing node trees. This is a legitimate local file existence check and does not read or exfiltrate file contents.

### [low] Tree mutation during diff

Finding ID: `NPS-A635B77436A4`

File: `lib/diff.js:310`

The diff logic mutates the input trees by reassigning node.parent for bundled dependencies. This is documented as a deliberate performance optimization, not a security issue, though it could have unintended side effects in consumers.

### [low] Signal handling logic

Finding ID: `NPS-617841539AF5`

File: `lib/signal-handling.js`

The code handles process signals to perform cleanup and re-raises the caught signal; this is benign and common in CLI tools.

### [low] Retry without nonce on package spec resolution

Finding ID: `NPS-96057EEFD5F3`

File: `lib/spec-from-lock.js:33`

If resolving with the passed-in name and version fails, the fallback at the end attempts npa.resolve(name, lock.version, where) again, without changing parameters, then returns {}. This is benign but could produce misleading results for a lockfile handling utility. No actual malicious behavior detected.

## Files reviewed

- `bin/actual.js` (medium): This file appears to be a legitimate part of the npm Arborist library for loading and saving package trees, with one minor file system write concern when saveHidden is enabled.
- `bin/index.js` (medium): This appears to be a legitimate CLI entry point for npm's arborist tool with no clear malicious patterns, though it dynamically loads all .js files in its directory which is a minor risk if the package directory could be tampered with.
- `lib/arborist/isolated-reifier.js` (medium): This is npm's Arborist isolated-mode reifier; it performs expected dependency resolution, hashing, and tarball extraction with defensive path sanitization, and contains no malicious patterns or exfiltration behavior.
- `lib/spec-from-lock.js` (medium): Code appears to be a clean, benign helper extracted from npm v6 for resolving lockfile specs; no malicious patterns, exfiltration, or dangerous execution detected.
- `bin/audit.js` (safe): No malicious patterns detected
- `bin/funding.js` (safe): No malicious patterns detected; the file simply loads package metadata and prints funding information.
- `bin/ideal.js` (safe): No malicious patterns detected
- `bin/lib/logging.js` (safe): No malicious patterns detected; the code is a standard logging utility with expected file system access controlled by user configuration.
- `bin/lib/options.js` (safe): No malicious patterns detected; this is a standard CLI option parser for npm/arborist with no network, credential, or execution risks.
- `bin/lib/print-tree.js` (safe): Cleared by Jev triage; no further analysis needed
- `bin/lib/timers.js` (safe): The code is a benign internal timing utility with no malicious patterns such as data exfiltration, credential harvesting, or dynamic code execution.
- `bin/license.js` (safe): No malicious patterns detected; the code is a legitimate CLI module for npm's Arborist that queries and logs license information from a dependency tree.
- `bin/prune.js` (safe): The code is a standard npm CLI subcommand for pruning extraneous packages with no malicious patterns, network requests, or filesystem access outside normal package operations.
- `bin/reify.js` (safe): No malicious patterns detected; the file is a legitimate CLI wrapper for npm's Arborist dependency reification with no suspicious behavior.
- `bin/shrinkwrap.js` (safe): No malicious patterns detected in bin/shrinkwrap.js; the code only loads and serializes a lockfile without network, process, or filesystem side effects.
- `bin/virtual.js` (safe): No malicious patterns detected
- `lib/add-rm-pkg-deps.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/arborist/build-ideal-tree.js` (safe): This file is a legitimate part of npm's arborist dependency tree builder, with no malicious patterns such as data exfiltration, credential harvesting, obfuscated code, backdoors, or suspicious network/process activity detected.
- `lib/arborist/index.js` (safe): No malicious patterns detected; this is legitimate npm arborist tree management code.
- `lib/arborist/load-actual.js` (safe): No malicious patterns detected; the file is a legitimate npm Arborist module for loading the actual dependency tree with no data exfiltration, credential harvesting, obfuscation, process spawning, or other suspicious behavior.
- `lib/arborist/load-virtual.js` (safe): No malicious patterns detected; the code is a legitimate npm arborist virtual tree loader with path traversal protections.
- `lib/arborist/rebuild.js` (safe): This is legitimate npm arborist rebuild logic that runs standard package lifecycle scripts, bin linking, and node-gyp detection with no data exfiltration, credential harvesting, obfuscation, or backdoor patterns.
- `lib/arborist/reify.js` (safe): This is npm's legitimate arborist reify implementation for package installation, with no malicious patterns such as exfiltration, credential harvesting, obfuscation, or backdoors; all network and filesystem operations are standard package-manager behaviors.
- `lib/audit-report.js` (safe): No malicious patterns detected; this is a legitimate npm audit report module that communicates only with the configured registry endpoint for vulnerability advisories.
- `lib/calc-dep-flags.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/can-place-dep.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/case-insensitive-map.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/consistent-resolve.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/debug.js` (safe): No malicious patterns detected; the file contains only benign debug logging logic gated by environment variables and working directory checks.
- `lib/deepest-nesting-target.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/dep-valid.js` (safe): No malicious patterns detected; the code is a standard npm dependency validation module with no network, credential, execution, or filesystem manipulation beyond path comparison.
- `lib/diff.js` (safe): No malicious patterns detected; the file is a legitimate npm dependency diff implementation using standard tree traversal and integrity checking without network, credential, or code execution activity.
- `lib/edge.js` (safe): No malicious patterns detected; the code is a legitimate npm dependency graph edge implementation with no network, process, filesystem, or credential-harvesting behavior.
- `lib/from-path.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/gather-dep-set.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/index.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/install-scripts.js` (safe): No malicious patterns detected
- `lib/inventory.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/isolated-classes.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/link.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/node.js` (safe): No malicious patterns detected; the code is a legitimate part of the npm CLI's Arborist dependency tree management library.
- `lib/optional-set.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/override-resolves.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/override-set.js` (safe): No malicious patterns detected; the file contains only package override resolution logic using standard npm libraries without network, filesystem, process, or environment access.
- `lib/packument-cache.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/peer-entry-sets.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/place-dep.js` (safe): No malicious patterns detected; the code is a legitimate npm dependency placement module with no network, filesystem, or process execution risks.
- `lib/printable.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/query-selector-all.js` (safe): No malicious patterns detected; the code is a legitimate npm query selector implementation that only performs expected registry audit and packument requests.
- `lib/realpath.js` (safe): No malicious patterns detected; the code is a legitimate realpath implementation using Node.js fs and path modules with caching.
- `lib/release-age-exclude.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/relpath.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/reset-dep-flags.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/retire-path.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/script-allowed.js` (safe): This file implements an allowScripts policy matcher that carefully uses trusted registry URL identities rather than attacker-controllable package.json fields, with no data exfiltration, process spawning, or other malicious patterns.
- `lib/shrinkwrap.js` (safe): The code is a legitimate npm lockfile management module with no malicious patterns detected.
- `lib/signal-handling.js` (safe): No malicious patterns detected; the code is a standard signal handler for cleanup on exit.
- `lib/signals.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/tracker.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/tree-check.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/unreviewed-scripts.js` (safe): No malicious patterns detected; the code is a legitimate allowScripts walker for npm's arborist with no network, credential, process, or execution concerns.
- `lib/version-from-tgz.js` (safe): No malicious patterns detected; the code is a straightforward utility for parsing version information from tarball filenames.
- `lib/vuln.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/yarn-lock.js` (safe): Cleared by Jev triage; no further analysis needed

AI analysis is guidance, not a guarantee. Methodology: https://security.togoder.click/methodology
