From 596a6253784794d30b8216e2cbb71b18c6eb4dc1 Mon Sep 17 00:00:00 2001 From: Parv Sharma Date: Mon, 11 May 2026 21:06:39 +0100 Subject: [PATCH] fix(security): input validation for command injection vectors (issue #6089) Four unpatched command injection vulnerabilities from issue #6089: - Startup.js: validate service_name before passing to systemctl shell commands (prevents RCE as root via --service-name flag) - LogManagement.js: validate user param before writeFileSync path construction (prevents path traversal and logrotate postrotate script injection) - Extra.js: add explicit allowlist for remoteV2 command parameter (prevents arbitrary CLI method invocation via RPC) - Deploy.js: validate env name before shell string construction (prevents shell injection in post-deploy commands) Ref: https://github.com/Unitech/pm2/issues/6089 --- lib/API/Deploy.js | 5 +++++ lib/API/Extra.js | 15 +++++++++++++++ lib/API/LogManagement.js | 2 ++ lib/API/Startup.js | 4 ++++ 4 files changed, 26 insertions(+) diff --git a/lib/API/Deploy.js b/lib/API/Deploy.js index b758a7987..9b1a285ff 100644 --- a/lib/API/Deploy.js +++ b/lib/API/Deploy.js @@ -94,6 +94,11 @@ module.exports = function(CLI) { return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT); } + if (!/^[a-zA-Z0-9._-]+$/.test(env)) { + Common.printError('Invalid environment name: ' + env); + return cb ? cb(new Error('Invalid environment name')) : that.exitCli(cst.ERROR_EXIT); + } + if (!json_conf.deploy || !json_conf.deploy[env]) { Common.printError('%s environment is not defined in %s file', env, file); return cb ? cb('%s environment is not defined in %s file') : that.exitCli(cst.ERROR_EXIT); diff --git a/lib/API/Extra.js b/lib/API/Extra.js index bb20b6391..51762e2fa 100644 --- a/lib/API/Extra.js +++ b/lib/API/Extra.js @@ -631,6 +631,21 @@ module.exports = function(CLI) { CLI.prototype.remoteV2 = function(command, opts, cb) { var that = this; + var ALLOWED_COMMANDS = [ + 'start', 'stop', 'restart', 'reload', 'delete', 'list', 'describe', + 'logs', 'flush', 'reloadLogs', 'sendDataToProcessId', 'trigger', + 'gracefulReload', 'killDaemon', 'ping', 'launchBus', 'attach', + 'getVersion', 'infoVizion', 'monit', 'dashboard', 'update', + 'install', 'uninstall', 'publish', 'save', 'resurrect', 'reset', + 'scale', 'env', 'show', 'id', 'deepUpdate', 'startup', 'unstartup', + 'prettyList', 'jlist', 'streamLogs', 'flushLogs', 'pullAndRestart', + 'pullAndReload', 'forward', 'backward', 'gc', 'getMonitorData', + 'getSystemData', 'infoVizion' + ]; + + if (ALLOWED_COMMANDS.indexOf(command) === -1) + return cb(new Error('Command not allowed: ' + command)); + if (that[command].length == 1) return that[command](cb); diff --git a/lib/API/LogManagement.js b/lib/API/LogManagement.js index cdfbef157..09e78a1f1 100644 --- a/lib/API/LogManagement.js +++ b/lib/API/LogManagement.js @@ -89,6 +89,8 @@ module.exports = function(CLI) { var user = opts.user || 'root'; + if (!/^[a-zA-Z0-9_-]+$/.test(user)) return cb ? cb(Common.retErr('Invalid user')) : that.exitCli(cst.ERROR_EXIT); + script = script.replace(/%HOME_PATH%/g, cst.PM2_ROOT_PATH) .replace(/%USER%/g, user); diff --git a/lib/API/Startup.js b/lib/API/Startup.js index 6bdb97c7b..ff000c027 100644 --- a/lib/API/Startup.js +++ b/lib/API/Startup.js @@ -77,6 +77,8 @@ module.exports = function(CLI) { var openrc_service_name = 'pm2'; var launchd_service_name = (opts.serviceName || 'pm2.' + user); + if (!/^[a-zA-Z0-9._-]+$/.test(service_name)) return cb(new Error('Invalid service name')); + if (!platform) platform = actual_platform; else if (actual_platform && actual_platform !== platform) { @@ -203,6 +205,8 @@ module.exports = function(CLI) { var openrc_service_name = 'pm2'; var launchd_service_name = (opts.serviceName || 'pm2.' + user); + if (!/^[a-zA-Z0-9._-]+$/.test(service_name)) return cb(new Error('Invalid service name')); + if (!platform) platform = actual_platform; else if (actual_platform && actual_platform !== platform) {