# napi-postinstall@0.3.4 security report (npm)

- Verdict: **Needs review** (risk level: medium)
- Scanned: 2026-10-06T14:19:30.000Z
- Files reviewed: 7
- Findings: 2 high, 11 medium, 6 low severity findings
- Report: https://security.togoder.click/npm/napi-postinstall
- Source: Togoder Security (https://security.togoder.click), AI source-code review

## Summary

Togoder Security scanned the npm package napi-postinstall@0.3.4 on Oct 6, 2026. An AI review of 7 source files produced 2 high, 11 medium, 6 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

### [high] Shell command execution with string interpolation

Finding ID: `NPS-1484734A4435`

File: `lib/index.js:105`

`execSync` is called with a template string that interpolates `pkg`, `version`, and constants into an npm install command. If any of these values originate from an attacker-controlled package.json (e.g. a malicious dependency name/version), command injection is possible. The same pattern appears in the yarn P'n'P branch (`yarn add -D ${pkg}@${version}`).

### [high] Shell command execution with string interpolation

Finding ID: `NPS-C7600FF125B5`

File: `lib/index.js:224`

`execSync` with `yarn add -D ${pkg}@${version}` uses interpolated values from package metadata. A crafted package name could inject shell metacharacters and execute arbitrary commands in the P'n'P environment.

### [medium] Opaque downstream behavior

Finding ID: `NPS-EA664602C314`

File: `lib/cli.js:24`

The core logic is delegated to checkAndPreparePackage in ./index.js, which is not visible here. The safety of this CLI depends entirely on that implementation, which could contain network calls, credential harvesting, or process spawning.

### [medium] Dynamic code execution via require()

Finding ID: `NPS-9FBB457382B9`

File: `lib/fallback.js:37`

The function uses require(packageJsonPath), require(bindingEntry), and require(pkgDir) to load modules. In the WebContainer branch, bindingEntry is constructed from values read from package.json (napi.packageName, napi.binaryName, name, version). If an attacker can influence package.json contents, this could lead to loading arbitrary code from paths derived from the package metadata.

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

Finding ID: `NPS-3974FD1242F3`

File: `lib/fallback.js:44`

Writes to os.tmpdir() by removing and recreating a directory named after the package (${name}-${version}) and installing dependencies there. This manipulates shared temporary storage and could overwrite or interfere with other applications' temporary files if name/version collide.

### [medium] Process spawning / shell command execution

Finding ID: `NPS-2B3AF5968949`

File: `lib/fallback.js:57`

Uses execFileSync to execute 'pnpm' (WebContainer path) and a package-manager-derived executor (npx/pnpm/yarn/bun/deno) with constructed arguments. While using execFileSync avoids shell interpolation, it still executes external binaries and could be abused if package.json fields (name, version, napi info) are attacker-controlled, allowing arbitrary package installation or execution.

### [medium] Command Execution

Finding ID: `NPS-2C7A450A31D4`

File: `lib/helpers.js:13`

Uses child_process.execSync to run 'npm config get registry', which spawns a shell command. While intended to read the npm registry configuration, this executes an external command at import/runtime. If an attacker can manipulate the environment or npm binary, this could lead to arbitrary command execution.

### [medium] Dynamic Module Resolution

Finding ID: `NPS-3D342C2A6407`

File: `lib/helpers.js:44`

Uses require.resolve(name + '/package.json') where 'name' is a parameter. If this function is called with untrusted input, it could resolve and load arbitrary packages, potentially leading to unexpected module loading or path traversal. In this context, it's likely called with a known package name, but the pattern is risky.

### [medium] Command Execution

Finding ID: `NPS-9944A3D5B15F`

File: `lib/helpers.js:128`

Uses child_process.execSync to run 'ldd --version' to detect musl libc. This spawns a shell command based on the runtime environment. The command is fixed, but it still executes an external process at import/runtime, which could be exploited if the PATH or ldd binary is compromised.

### [medium] Insecure HTTP fallback

Finding ID: `NPS-A04AC5C70AAE`

File: `lib/index.js:16`

`fetch` uses the plain `http` module when the URL starts with `http://`. This allows package downloads over an unencrypted channel, enabling man-in-the-middle tampering with the downloaded binary package.

### [medium] Untrusted URL redirect following

Finding ID: `NPS-197F538AC24F`

File: `lib/index.js:18`

The `fetch` function follows HTTP redirects (301, 302, 307, 308) to arbitrary locations without validating the destination host. A malicious or compromised NPM registry response could redirect the download to an attacker-controlled server, leading to download of a malicious tarball that is then extracted and used.

### [medium] File system writes outside package scope

Finding ID: `NPS-51C3D309757E`

File: `lib/index.js:137`

The install logic writes/renames files into `node_modules` paths resolved from `require.resolve` and ancestor directories (`path.resolve(pkgDir, hostPkg.split('/').map(() => '..').join('/'), pkg)`), potentially modifying files outside the current package's own directory tree.

### [medium] Dynamic module loading / resolution

Finding ID: `NPS-F06EC73461BA`

File: `lib/index.js:174`

`require.resolve` is invoked on computed package names derived from package.json metadata, and `require(packageNameOrPackageJson + '/package.json')` uses an externally supplied string. This can trigger loading of arbitrary packages specified by a malicious manifest.

### [low] Filesystem access at runtime

Finding ID: `NPS-EC816DD39B48`

File: `lib/cli.js:8`

The CLI reads package.json from the current working directory using fs.readFileSync. While this appears to be legitimate behavior for verifying the local package version before invoking checkAndPreparePackage, it represents filesystem access that could be abused if the downstream functions behave unexpectedly.

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

Finding ID: `NPS-A0A834FA04B4`

File: `lib/cli.js:19`

Code executes immediately at module load time (reads process.argv and calls checkAndPreparePackage). If this module is imported rather than run as a CLI, execution still occurs, potentially triggering unintended side effects.

### [low] Environment variable manipulation

Finding ID: `NPS-843DF9A42DDF`

File: `lib/fallback.js:68`

Sets process.env[`SKIP_${name...}_FALLBACK`] = '1' to bypass future fallback checks. While not inherently malicious, it modifies global process state that persists for the lifetime of the process.

### [low] Dynamic import / module cache manipulation

Finding ID: `NPS-68C4646230F0`

File: `lib/fallback.js:70`

Deletes the resolved package path from require.cache and re-requires it. This cache-busting technique can cause re-execution of top-level module code and may be used to force reload of modified modules.

### [low] File System Access

Finding ID: `NPS-0C088C3DDED2`

File: `lib/helpers.js:96`

Reads /usr/bin/ldd directly from the filesystem to detect musl. While not malicious per se, it accesses system files outside the package scope. This is a common pattern for native module detection but should be noted.

### [low] Environment variable access

Finding ID: `NPS-089856F0D962`

File: `lib/index.js:94`

The code reads `process.env` and spreads it into the child process environment (`{ ...process.env, npm_config_global: undefined }`), and reads `process.env.npm_config_user_agent`. Environment contents are passed to spawned npm processes, which is standard but broadens exposure of any secrets present in the environment to the child process.

## Files reviewed

- `lib/cli.js` (medium): The CLI itself shows no direct malicious code, but it performs filesystem reads and delegates core behavior to an unaudited index.js module, warranting review of the downstream implementation.
- `lib/fallback.js` (medium): The code is a legitimate native-addon fallback mechanism but performs several potentially risky operations—dynamic require of computed paths, spawning package managers from metadata, and writing to shared temp directories—that could be exploited if package.json or napi metadata is attacker-controlled.
- `lib/helpers.js` (medium): The code appears to be a legitimate helper module for detecting native platform targets and managing npm packages, but it uses shell command execution and dynamic module resolution that could be exploited if inputs are untrusted.
- `lib/index.js` (medium): This appears to be legitimate napi binary-fetching logic, but it contains risky patterns: shell command construction via string interpolation of package metadata (command injection risk), unvalidated redirect following and HTTP fallback in downloads, and file system writes outside package scope.
- `lib/constants.js` (safe): No malicious patterns detected
- `lib/target.js` (safe): Cleared by Jev triage; no further analysis needed
- `lib/types.js` (safe): No malicious patterns detected

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