registry  /  pm2-orbit  /  1.3.2

pm2-orbit@1.3.2

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

AI Security Review

scanned 1d ago · by lpm-firewall-ai

No confirmed malicious install-time behavior was found. Runtime server has dangerous unauthenticated/weakly authenticated PM2 control and environment exposure surfaces if reachable beyond localhost.

Static reason
High-risk behavior combination matched malicious policy.
Trigger
User runs pm2-orbit server or CLI
Impact
Potential exposure of process environment and PM2 control if the server is bound to an accessible interface; no fixed attacker collection path confirmed.
Mechanism
local PM2 monitoring dashboard with process control, env display, settings storage, and user-configured notifications
Attack narrative
The package is a PM2 dashboard. Its CLI starts a local web server and can auto-install pm2 when invoked. The server can show process environment data and perform PM2 lifecycle actions through API routes; the intended token auth hook is effectively empty, making this a serious runtime exposure if reachable. I did not find lifecycle exfiltration, persistence, AI-agent hijack, or a hardcoded attacker endpoint.
Rationale
Static inspection supports a dangerous runtime vulnerability/dual-use PM2 control surface, but not concrete malicious package behavior or unconsented install-time mutation. Scanner credential-exfiltration hints appear to map to user-configured alert/webhook settings rather than a fixed attacker endpoint.
Evidence
package.jsonbin/pm2-orbit.jsdist/server.jsdist-ui/assets/index-B00ypQ6c.jsREADME.md~/.pm2-orbit/settings.json~/.pm2-orbit/.key~/.pm2-orbit/alerts.json~/.pm2-orbit/history.db
Network endpoints5
registry.npmjs.org/pm2-orbit/latestuser-configured WEBHOOK_URLuser-configured SLACK_WEBHOOK_URLuser-configured DISCORD_WEBHOOK_URLuser-configured SMTP_HOST

Decision evidence

public snapshot
AI called this Suspicious at 86.0% confidence as Critical Vulnerability with medium false-positive risk.
Evidence for warning
  • dist/server.js defines /api/processes/:id/env returning PM2 process env values after only a denylist filter
  • dist/server.js qX auth hook is empty even when PM2_ORBIT_TOKEN is set
  • dist/server.js exposes PM2 actions via /api/processes/:id/action for restart/stop/start/reload/delete/scale
  • dist/server.js allows user-configured webhook test POSTs and alert notifications to arbitrary configured URLs
  • bin/pm2-orbit.js user-invoked CLI runs npm install -g pm2 if pm2 is missing
Evidence against
  • package.json postinstall only checks pm2 resolution and prints an install hint; it does not install or mutate files
  • No install-time AI-agent control-surface writes or persistence hooks found
  • No hardcoded attacker exfiltration endpoint found; outbound alert URLs come from settings/env/user input
  • Runtime files written are package-aligned ~/.pm2-orbit settings, key, alerts, and history data
  • UI credential fields are sent to local /api/settings and stored encrypted/masked rather than directly exfiltrated
  • Network to registry.npmjs.org is an update check on server listen
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, registry.npmjs.org, 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)]}xW.createHtmlDocument=AAe});var wW=S((WHe,bW)=>{"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 "+pO(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,{[w2]:"",[_2]:o,[E2]:N,[S2]:Ob,[Rb]:C,[T2]:P}),X="";p!==null&&(f===void 0?X=q(p):X=q(Object.assign({},p,{name:f})));let B=a instanceof Function?a:a?k2:nre,U=B().... L26: `:""},this._extScope=t,this._scope=new Wr.Scope({parent:t}),this._nodes=[new xw]}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+=yA(o,a).slice(t.length+2... L3: `).slice(0,2).map(i=>i.trim()).join(" -- ")}EA.exports={getPluginName:tJ}});var wh=S((V$e,CA)=>{"use strict";function iJ(e){return e!==null&&typeof e=="object"&&typeof e.then=="fun... L4: causes have become circular...`;let r=qv(e);return r?(t.add(e),i+` L5: caused by: `+Ik(r,t)):i},jQ=e=>Ik(e,new Set),Lk=(e,t,i)=>{if(!Zu(e))return"";let r=i?"":e.message||"";if(t.has(e))return r+": ...";let n=qv(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,{[w2]:"",[_2]:o,[E2]:N,[S2]:Ob,[Rb]:C,[T2]:P}),X="";p!==null&&(f===void 0?X=q(p):X=q(Object.assign({},p,{name:f})));let B=a instanceof Function?a:a?k2:nre,U=B().... L26: `:""},this._extScope=t,this._scope=new Wr.Scope({parent:t}),this._nodes=[new xw]}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+=yA(o,a).slice(t.length+2... L3: `).slice(0,2).map(i=>i.trim()).join(" -- ")}EA.exports={getPluginName:tJ}});var wh=S((V$e,CA)=>{"use strict";function iJ(e){return e!==null&&typeof e=="object"&&typeof e.then=="fun... L4: causes have become circular...`;let r=qv(e);return r?(t.add(e),i+` L5: caused by: `+Ik(r,t)):i},jQ=e=>Ik(e,new Set),Lk=(e,t,i)=>{if(!Zu(e))return"";let r=i?"":e.message||"";if(t.has(e))return r+": ...";let n=qv(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,{[w2]:"",[_2]:o,[E2]:N,[S2]:Ob,[Rb]:C,[T2]:P}),X="";p!==null&&(f===void 0?X=q(p):X=q(Object.assign({},p,{name:f})));let B=a instanceof Function?a:a?k2:nre,U=B().... L26: `:""},this._extScope=t,this._scope=new Wr.Scope({parent:t}),this._nodes=[new xw]}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-B00ypQ6c.jsView file
1import{j as i,_ as Dn}from"./cmdk-cQPtLM7A.js";import{r as Cc,g as Np,a as b,b as J,B as wi,M as T0,c as Ep,S as zp,W as C0,d as A0,C as M0,e as Tp,f as R0,A as gc,N as D0,H as k0,... L2: * @license React ... L16: * LICENSE file in the root directory of this source tree. L17: */var Zh;function lb(){if(Zh)return ks;Zh=1;var s=ab(),o=Cc(),u=K0();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(Jx,"")}function oh(e,t){return t=ih(t),ih(e)===t}function Me(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([A],{type:"text/csv"}),Ne=URL.createObjectURL($),ye=document.createElement("a");ye.href=Ne,ye.download=`pm2-orbit-processes-${Date.now()}.csv`,ye.click(),URL.revokeOb... L55: `);for(const re of le)if(re)try{con
Critical
Credential Exfiltration

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

dist-ui/assets/index-B00ypQ6c.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-B00ypQ6c.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