registry  /  pm2-orbit  /  1.3.0

pm2-orbit@1.3.0

High-performance PM2 monitoring dashboard — event-driven, 1000+ processes, < 150KB

AI Security Review

scanned 2d ago · by lpm-firewall-ai

No confirmed malicious package behavior was found, but the runtime server exposes sensitive PM2 control and environment-reading APIs with apparently nonfunctional token enforcement. Risk is activated when the dashboard server is run, especially if bound beyond localhost.

Static reason
High-risk behavior combination matched malicious policy.
Trigger
User runs pm2-orbit or imports/starts dist/server.js
Impact
Potential disclosure of process environment variables and remote PM2 process control if the service is reachable by an attacker.
Mechanism
PM2 monitoring web server with unauthenticated sensitive API routes
Attack narrative
A user-invoked PM2 dashboard starts a local Fastify/WebSocket service that reads PM2 process state, logs, selected environment variables, and can issue PM2 process actions. The code does not show install-time malware or fixed exfiltration, but its token hook is empty, leaving sensitive routes effectively unprotected if exposed.
Rationale
Static inspection supports a package-aligned PM2 dashboard rather than malware; scanner findings around network/env are explained by monitoring, alerting, and UI behavior. The unauthenticated env/process-control surface is a real security risk, so warn rather than block.
Evidence
package.jsonbin/pm2-orbit.jsdist/server.jsdist-ui/index.htmldist-ui/sw.jsdist-ui/assets/index-mMrbUw_m.js~/.pm2-orbit/history.db~/.pm2-orbit/alerts.json~/.pm2/logs/*
Network endpoints5
127.0.0.1:9823localhost:9823ws://127.0.0.1:9823/ws127.0.0.1:5151ws://127.0.0.1:5151

Decision evidence

public snapshot
AI called this Suspicious at 78.0% confidence as Critical Vulnerability with medium false-positive risk.
Evidence for warning
  • dist/server.js exposes /api/processes/:id/env, returning most PM2 environment entries after only a broad allowlist filter.
  • dist/server.js exposes /api/processes/:id/action for restart/stop/start/reload/delete/scale/flush PM2 actions.
  • dist/server.js defines function jX(){return async function(t,i){}} and registers it when PM2_ORBIT_TOKEN is set, so token auth appears ineffective.
  • dist/server.js sends alert data to user-configured WEBHOOK_URL, SLACK_WEBHOOK_URL, DISCORD_WEBHOOK_URL, and SMTP settings.
  • bin/pm2-orbit.js auto-runs npm install -g pm2 and better-sqlite3 when the CLI is invoked if dependencies are missing.
Evidence against
  • package.json postinstall only checks for pm2 and prints an install hint; it does not download or mutate files.
  • No lifecycle hook writes agent config, shell startup files, VCS hooks, or persistence outside package-owned paths.
  • Server/network behavior is aligned with a PM2 monitoring dashboard and is started by CLI/imported main, not install-time.
  • Files written by runtime are package-owned ~/.pm2-orbit/history.db and ~/.pm2-orbit/alerts.json.
  • No hardcoded attacker endpoint, credential exfiltration destination, obfuscated staged payload, or destructive install-time behavior found.
Behavioral surface
Source
ChildProcessCryptoEnvironmentVarsEvalFilesystemNetworkWebSocket
Supply chain
HighEntropyStringsMinifiedObfuscatedProtestwareUrlStrings
ManifestNo manifest risk signals triggered.
scanned 9 file(s), 2.21 MB of source, external domains: 127.0.0.1, accounts.google.com, api.nodemailer.com, discord.com, ethereal.email, fastify.dev, github.com, hooks.slack.com, json-schema.org, localhost.com, mail.google.com, nodemailer.com, plus-innovations.com, pm2.io, pm2.keymetrics.io, raw.githubusercontent.com, react.dev, systeminformation.io, www.buymeacoffee.com, www.w3.org, your-webhook-endpoint.com

Source & flagged code

10 flagged · loading source
package.jsonView file
scripts.postinstall = node -e "try{require.resolve('pm2')}catch{console.log('\n ℹ Install pm2 for process monitoring: npm install -g pm2\n')}"
Critical
Red Install Lifecycle Script

Install-time lifecycle script matches a deterministic static-gate block pattern.

package.jsonView on unpkg
scripts.postinstall = node -e "try{require.resolve('pm2')}catch{console.log('\n ℹ Install pm2 for process monitoring: npm install -g pm2\n')}"
High
Install Time Lifecycle Scripts

Package defines install-time lifecycle scripts.

package.jsonView on unpkg
bin/pm2-orbit.jsView file
67try { L68: const { execSync } = require('child_process'); L69: execSync(`npm install -g ${name}`, { stdio: 'inherit', timeout: 120000 });
High
Child Process

Package source references child process execution.

bin/pm2-orbit.jsView on unpkg · L67
dist/server.jsView file
28|| (${o} === "string" && ${n} && ${n} == +${n} && !(${n} % 1))`).assign(a,(0,Le._)`+${n}`);return;case"boolean":r.elseIf((0,Le._)`${n} === "false" || ${n} === 0 || ${n} === null`).... L29: || ${o} === "boolean" || ${n} === null`).assign(a,(0,Le._)`[${n}]`)}}}function Boe({gen:e,parentData:t,parentDataProperty:i},r){e.if((0,Le._)`${t} !== undefined`,()=>e.assign((0,Le... L30: missingProperty: ${r},
High
Eval

Package source references dynamic code evaluation.

dist/server.jsView on unpkg · L28
423globstar while`,A,ie,M,ce,Te),this.matchOne(A.slice(ie),M.slice(ce),j))return this.debug("globstar found match!",ie,I,Te),!0;if(Te==="."||Te===".."||!$.dot&&Te.charAt(0)==="."){thi... L424: >>> no match, partial?`,A,ie,M,ce),ie===I))}let Ae;if(typeof V=="string"?(Ae=oe===V,this.debug("string match",V,oe,Ae)):(Ae=V.test(oe),this.debug("pattern match",V,oe,Ae)),!Ae)retu... L425: <html lang="en"> ... L434: `;return[i,Buffer.byteLength(i)]}yW.createHtmlDocument=AAe});var bW=S((GHe,vW)=>{"use strict";function kAe(e,t){if(typeof e=="string")return[e];if(e===!1)return[];if(Array.isArray(... L435: at `+e[i].toString();return t}function BAe(e){if(!e)throw new TypeError("argument namespace is required");var t=Wy(),i=iu(t[1]),r=i[0];function n(s){Vy.call(n,s)}return n._file=r,n... L436: `,"utf8")}}}function iu(e){var t=e.getFileName()||"<anonymous>",i=e.getLineNumber(),r=e.getColumnNumber();e.isEval()&&(t=e.getEvalOrigin()+", "+t);var n=[t,i,r];return n.callSite=e... L437: at `+i[s].toString();return n}return t&&(n+=" at "+uO(t)),n}function WAe(e,t,i){var r="\x1B[36;1m"+this._namespace+"\x1B[22;39m \x1B[33;1mdeprecated\x1B[22;39m \x1B[0m"+e+"\x1B[39m... L438: \x1B[36mat `+i[n].toString(
High
Same File Env Network Execution

A single source file combines environment access, network access, and code or shell execution; review context before blocking.

dist/server.jsView on unpkg · L423
22${y}${O} L23: ${x}`),m.pop(),`{${O}}`}case"number":return isFinite(h)?String(h):t?t(h):"null";case"boolean":return h===!0?"true":"false";case"undefined":return;case"bigint":if(r)return String(h)... L24: `:` L25: `),q=jie.bind(null,{[b2]:"",[w2]:o,[S2]:N,[_2]:Cb,[Ob]:C,[E2]:P}),X="";p!==null&&(f===void 0?X=q(p):X=q(Object.assign({},p,{name:f})));let B=a instanceof Function?a:a?A2:nre,U=B().... L26: `:""},this._extScope=t,this._scope=new Wr.Scope({parent:t}),this._nodes=[new yw]}toString(){return this._root.render(this.opts)}name(t){return this._scope.name(t)}scopeName(t){retu... ... L28: || (${o} === "string" && ${n} && ${n} == +${n} && !(${n} % 1))`).assign(a,(0,Le._)`+${n}`);return;case"boolean":r.elseIf((0,Le._)`${n} === "false" || ${n} === 0 || ${n} === null`).... L29: || ${o} === "boolean" || ${n} === null`).assign(a,(0,Le._)`[${n}]`)}}}function Boe({gen:e,parentData:t,parentDataProperty:i},r){e.if((0,Le._)`${t} !== undefined`,()=>e.assign((0,Le... L30: missingProperty: ${r},
High
Command Output Exfiltration

Source combines command execution, command-output handling, and outbound requests; review data flow before blocking.

dist/server.jsView on unpkg · L22
2`;for(let s=0;s<r;++s){let o=e.nodes[s],a=t+(s===n?" ":"\u2502 ");i+=t,i+=s===n?"\u2514\u2500":"\u251C\u2500",i+=o.nodes.length===0?"\u2500 ":"\u252C ",i+=gA(o,a).slice(t.length+2... L3: `).slice(0,2).map(i=>i.trim()).join(" -- ")}SA.exports={getPluginName:tJ}});var wh=S((W$e,TA)=>{"use strict";function iJ(e){return e!==null&&typeof e=="object"&&typeof e.then=="fun... L4: causes have become circular...`;let r=jv(e);return r?(t.add(e),i+` L5: caused by: `+kk(r,t)):i},jQ=e=>kk(e,new Set),Ik=(e,t,i)=>{if(!Zu(e))return"";let r=i?"":e.message||"";if(t.has(e))return r+": ...";let n=jv(e);if(n){t.add(e);let s=typeof e.cause==... L6: ${x}`,_=`, ... L22: ${y}${O} L23: ${x}`),m.pop(),`{${O}}`}case"number":return isFinite(h)?String(h):t?t(h):"null";case"boolean":return h===!0?"true":"false";case"undefined":return;case"bigint":if(r)return String(h)... L24: `:` L25: `),q=jie.bind(null,{[b2]:"",[w2]:o,[S2]:N,[_2]:Cb,[Ob]:C,[E2]:P}),X="";p!==null&&(f===void 0?X=q(p):X=q(Object.assign({},p,{name:f})));let B=a instanceof Function?a:a?A2:nre,U=B().... L26: `:""},this._extScope=t,this._scope=new Wr.Scope({parent:t}),this._nodes=[new yw]}toString(){return this._root.render(this.opts)}name(t){retu
High
Sandbox Evasion Gated Capability

Source gates dangerous network, credential, or execution behavior behind CI, host, platform, time, or geo fingerprint checks.

dist/server.jsView on unpkg · L2
2`;for(let s=0;s<r;++s){let o=e.nodes[s],a=t+(s===n?" ":"\u2502 ");i+=t,i+=s===n?"\u2514\u2500":"\u251C\u2500",i+=o.nodes.length===0?"\u2500 ":"\u252C ",i+=gA(o,a).slice(t.length+2... L3: `).slice(0,2).map(i=>i.trim()).join(" -- ")}SA.exports={getPluginName:tJ}});var wh=S((W$e,TA)=>{"use strict";function iJ(e){return e!==null&&typeof e=="object"&&typeof e.then=="fun... L4: causes have become circular...`;let r=jv(e);return r?(t.add(e),i+` L5: caused by: `+kk(r,t)):i},jQ=e=>kk(e,new Set),Ik=(e,t,i)=>{if(!Zu(e))return"";let r=i?"":e.message||"";if(t.has(e))return r+": ...";let n=jv(e);if(n){t.add(e);let s=typeof e.cause==... L6: ${x}`,_=`, ... L22: ${y}${O} L23: ${x}`),m.pop(),`{${O}}`}case"number":return isFinite(h)?String(h):t?t(h):"null";case"boolean":return h===!0?"true":"false";case"undefined":return;case"bigint":if(r)return String(h)... L24: `:` L25: `),q=jie.bind(null,{[b2]:"",[w2]:o,[S2]:N,[_2]:Cb,[Ob]:C,[E2]:P}),X="";p!==null&&(f===void 0?X=q(p):X=q(Object.assign({},p,{name:f})));let B=a instanceof Function?a:a?A2:nre,U=B().... L26: `:""},this._extScope=t,this._scope=new Wr.Scope({parent:t}),this._nodes=[new yw]}toString(){return this._root.render(this.opts)}name(t){retu
Low
Weak Crypto

Package source references weak cryptographic algorithms.

dist/server.jsView on unpkg · L2
dist-ui/assets/index-mMrbUw_m.jsView file
1import{j as i,_ as Dn}from"./cmdk-cQPtLM7A.js";import{r as zc,g as wp,a as b,b as K,B as wi,M as zx,c as Np,S as Ep,W as Tx,d as Cx,C as Ax,e as zp,f as Mx,A as hc,N as Rx,H as Dx,... L2: * @license React ... L16: * LICENSE file in the root directory of this source tree. L17: */var Zh;function ab(){if(Zh)return ks;Zh=1;var s=tb(),o=zc(),u=$x();function c(e){var t="https://react.dev/errors/"+e;if(1<arguments.length){t+="?args[]="+encodeURIComponent(argum... L18: at`)?" (<anonymous>)":-1<a.stack.indexOf("@")?"@unknown:0:0":""}return` ... L23: Error generating stack: `+l.message+` L24: `+l.stack}}var Xn=Object.prototype.hasOwnProperty,ql=s.unstable_scheduleCallback,fa=s.unstable_cancelCallback,Is=s.unstable_shouldYield,ma=s.unstable_requestPaint,dt=s.unstable_now... L25: `).replace(K0,"")}function ih(e,t){return t=rh(t),rh(e)===t}function Ae(e,t,a,l,n,r){switch(a){case"children":typeof l=="string"?t==="body"||t==="textarea"&&l===""||Fl(e,l):(typeof... ... L54: `),$=new Blob([M],{type:"text/csv"}),Se=URL.createObjectURL($),Y=document.createElement("a");Y.href=Se,Y.download=`pm2-orbit-processes-${Date.now()}.csv`,Y.click(),URL.revokeObject... L55: `);for(const V of F)if(V)try{const
Critical
Credential Exfiltration

Source appears to send environment or credential material to an external endpoint.

dist-ui/assets/index-mMrbUw_m.jsView on unpkg · L1
dist-ui/fonts/Exo-400.woff2View file
path = dist-ui/fonts/Exo-400.woff2 kind = high_entropy_blob sizeBytes = 21828 magicHex = [redacted]
High
Ships High Entropy Blob

Package ships high-entropy non-source blobs.

dist-ui/fonts/Exo-400.woff2View on unpkg

Findings

2 Critical7 High4 Medium7 Low
CriticalRed Install Lifecycle Scriptpackage.json
CriticalCredential Exfiltrationdist-ui/assets/index-mMrbUw_m.js
HighInstall Time Lifecycle Scriptspackage.json
HighChild Processbin/pm2-orbit.js
HighEvaldist/server.js
HighSame File Env Network Executiondist/server.js
HighCommand Output Exfiltrationdist/server.js
HighSandbox Evasion Gated Capabilitydist/server.js
HighShips High Entropy Blobdist-ui/fonts/Exo-400.woff2
MediumNetwork
MediumEnvironment Vars
MediumProtestware
MediumStructural Risk Force Deep Review
LowNon Install Lifecycle Scripts
LowScripts Present
LowWeak Cryptodist/server.js
LowFilesystem
LowObfuscated
LowHigh Entropy Strings
LowUrl Strings