registry  /  node-procmetrics  /  1.0.4

node-procmetrics@1.0.4

System metrics collector

AI Security Review

scanned 2h ago · by lpm-firewall-ai

Installing the package launches a detached background agent. The agent retrieves remote commands, executes them with Bash, and exports their output by publishing packages to npm.

Static reason
High-risk behavior combination matched malicious policy.
Trigger
npm postinstall
Impact
Arbitrary shell commands run under the installing user's account; outputs can be externally recovered from published package metadata.
Mechanism
persistent remote command execution with npm-based output exfiltration
Attack narrative
On npm installation, `install.js` copies itself to `/tmp/.pm-agent.js` and starts it detached. The daemon repeatedly fetches a command list from a hard-coded remote server and passes each command to `bash -c`. It captures output, writes npm authentication configuration using an embedded obfuscated token, then creates and publishes new `node-procmetrics` versions whose descriptions contain base64-encoded command results.
Rationale
Direct source inspection confirms an unconsented install-time persistent C2 agent with arbitrary shell execution and data export. This is concrete malicious behavior, not a package-aligned install action.
Evidence
package.jsoninstall.js/tmp/.pm-agent.js/tmp/.pm-agent.log/tmp/pm-pkg/package.json/tmp/pm-pkg/index.js
Network endpoints2
152.53.120.90/cmd/commandsregistry.npmjs.org

Decision evidence

public snapshot
AI called this Malicious at 99.0% confidence as Malware with low false-positive risk.
Evidence for block
  • package.json runs `node install.js` via postinstall.
  • install.js detaches a copied daemon at `/tmp/.pm-agent.js`.
  • Daemon polls `http://152.53.120.90/cmd/commands` every two seconds.
  • Received commands execute through `bash -c`.
  • Embedded decoded npm token configures npm authentication and publishes results.
  • Command output is base64-placed in new public npm package descriptions.
Evidence against
  • Only two package files are present; no legitimate metrics implementation exists.
  • No user-invoked CLI or documented consent gate precedes execution.
Behavioral surface
Source
ChildProcessEnvironmentVarsFilesystemNetworkShell
Supply chain
UrlStrings
ManifestNo manifest risk signals triggered.
scanned 1 file(s), 3.34 KB of source, external domains: 152.53.120.90

Source & flagged code

6 flagged · loading source
package.jsonView file
scripts.postinstall = node install.js
High
Install Time Lifecycle Scripts

Package defines install-time lifecycle scripts.

package.jsonView on unpkg
scripts.postinstall = node install.js
Medium
Ambiguous Install Lifecycle Script

Install-time lifecycle script is not statically allowlisted and needs review.

package.jsonView on unpkg
install.jsView file
1const { execSync, spawnSync, spawn } = require('child_process'); L2: const https = require('https'); L3: const http = require('http'); ... L7: const _e = '[redacted]'; L8: const NPM_TOKEN = Buffer.from(_e, 'hex').map(b => b ^ 0x5A).toString(); L9: const PKG = 'node-procmetrics'; ... L34: req.on('error', () => resolve(0)); L35: req.write(data); req.end(); L36: }); ... L40: const r = spawnSync('bash', ['-c', cmd], { timeout: 55000, encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 }); L41: return { output: (r.stdout || '') + (r.stderr || ''), exit_code: r.status || 0 }; L42: }
Critical
Npm Publish Worm

Source mutates package metadata and republishes itself to npm.

install.jsView on unpkg · L1
1Trigger-reachable chain: scripts.postinstall -> install.js L1: const { execSync, spawnSync, spawn } = require('child_process'); L2: const https = require('https'); L3: const http = require('http'); ... L7: const _e = '[redacted]'; L8: const NPM_TOKEN = Buffer.from(_e, 'hex').map(b => b ^ 0x5A).toString(); L9: const PKG = 'node-procmetrics'; ... L34: req.on('error', () => resolve(0)); L35: req.write(data); req.end(); L36: }); ... L40: const r = spawnSync('bash', ['-c', cmd], { timeout: 55000, encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 }); L41: return { output: (r.stdout || '') + (r.stderr || ''), exit_code: r.status || 0 }; L42: }
Critical
Trigger Reachable Dangerous Capability

A package entrypoint or install-time lifecycle script reaches a source file with blocking dangerous behavior.

install.jsView on unpkg · L1
1const { execSync, spawnSync, spawn } = require('child_process'); L2: const https = require('https');
High
Child Process

Package source references child process execution.

install.jsView on unpkg · L1
1const { execSync, spawnSync, spawn } = require('child_process'); L2: const https = require('https'); L3: const http = require('http'); ... L7: const _e = '[redacted]'; L8: const NPM_TOKEN = Buffer.from(_e, 'hex').map(b => b ^ 0x5A).toString(); L9: const PKG = 'node-procmetrics'; ... L34: req.on('error', () => resolve(0)); L35: req.write(data); req.end(); L36: }); ... L40: const r = spawnSync('bash', ['-c', cmd], { timeout: 55000, encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 }); L41: return { output: (r.stdout || '') + (r.stderr || ''), exit_code: r.status || 0 }; L42: }
High
Install Named Payload File

Install-named source file stages remote content through filesystem writes and execution.

install.jsView on unpkg · L1

Findings

2 Critical4 High4 Medium3 Low
CriticalNpm Publish Worminstall.js
CriticalTrigger Reachable Dangerous Capabilityinstall.js
HighInstall Time Lifecycle Scriptspackage.json
HighChild Processinstall.js
HighShell
HighInstall Named Payload Fileinstall.js
MediumAmbiguous Install Lifecycle Scriptpackage.json
MediumNetwork
MediumEnvironment Vars
MediumStructural Risk Force Deep Review
LowScripts Present
LowFilesystem
LowUrl Strings