Command Injection Vulnerability
Description
The System Information Library for Node.JS (npm package "systeminformation") is an open source collection of functions to retrieve detailed hardware, system and OS information. In systeminformation before version 5.3.1 there is a command injection vulnerability. Problem was fixed in version 5.3.1. As a workaround instead of upgrading, be sure to check or sanitize service parameters that are passed to si.inetLatency(), si.inetChecksite(), si.services(), si.processLoad() ... do only allow strings, reject any arrays. String sanitation works as expected.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
systeminformationnpm | < 5.3.1 | 5.3.1 |
Affected products
1- Range: < 5.3.1
Patches
107daa05fb06fdocker, processLoad fixed potential security issue
4 files changed · +77 −28
lib/docker.js+48 −16 modified@@ -109,6 +109,9 @@ function dockerContainers(all, callback) { callback = all; all = false; } + if (typeof all !== 'boolean' && all !== undefined) { + all = false; + } all = all || false; let result = []; @@ -185,16 +188,20 @@ function dockerContainers(all, callback) { // container inspect (for one container) function dockerContainerInspect(containerID, payload) { - containerID = containerID || ''; return new Promise((resolve) => { process.nextTick(() => { - if (containerID) { + containerID = containerID || ''; + if (typeof containerID !== 'string') { + resolve(); + } + const containerIdSanitized = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(containerID, true)).trim(); + if (containerIdSanitized) { if (!_docker_socket) { _docker_socket = new DockerSocket(); } - _docker_socket.getInspect(containerID.trim(), data => { + _docker_socket.getInspect(containerIdSanitized.trim(), data => { try { resolve({ id: payload.Id, @@ -325,19 +332,39 @@ function docker_calcBlockIO(blkio_stats) { function dockerContainerStats(containerIDs, callback) { let containerArray = []; - // fallback - if only callback is given - if (util.isFunction(containerIDs) && !callback) { - callback = containerIDs; - containerArray = ['*']; - } else { - containerIDs = containerIDs || '*'; - containerIDs = containerIDs.trim().toLowerCase().replace(/,+/g, '|'); - containerArray = containerIDs.split('|'); - } - return new Promise((resolve) => { process.nextTick(() => { + // fallback - if only callback is given + if (util.isFunction(containerIDs) && !callback) { + callback = containerIDs; + containerArray = ['*']; + } else { + containerIDs = containerIDs || '*'; + if (typeof containerIDs !== 'string') { + if (callback) { callback([]); } + return resolve([]); + } + let containerIDsSanitized = ''; + containerIDsSanitized.__proto__.toLowerCase = util.stringToLower; + containerIDsSanitized.__proto__.replace = util.stringReplace; + containerIDsSanitized.__proto__.trim = util.stringTrim; + + const s = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(containerIDs, true)).trim(); + for (let i = 0; i <= 2000; i++) { + if (!(s[i] === undefined)) { + s[i].__proto__.toLowerCase = util.stringToLower; + const sl = s[i].toLowerCase(); + if (sl && sl[0] && !sl[1]) { + containerIDsSanitized = containerIDsSanitized + sl[0]; + } + } + } + + containerIDsSanitized = containerIDsSanitized.trim().toLowerCase().replace(/,+/g, '|'); + containerArray = containerIDs.split('|'); + } + const result = []; const workload = []; @@ -444,17 +471,22 @@ exports.dockerContainerStats = dockerContainerStats; // container processes (for one container) function dockerContainerProcesses(containerID, callback) { - containerID = containerID || ''; let result = []; return new Promise((resolve) => { process.nextTick(() => { - if (containerID) { + containerID = containerID || ''; + if (typeof containerID !== 'string') { + resolve(result); + } + const containerIdSanitized = (util.isPrototypePolluted() ? '' : util.sanitizeShellString(containerID, true)).trim(); + + if (containerIdSanitized) { if (!_docker_socket) { _docker_socket = new DockerSocket(); } - _docker_socket.getProcesses(containerID, data => { + _docker_socket.getProcesses(containerIdSanitized, data => { /** * @namespace * @property {Array} Titles
lib/internet.js+2 −2 modified@@ -40,7 +40,7 @@ function inetChecksite(url, callback) { status: 404, ms: null }; - if (typeof url !== "string") { + if (typeof url !== 'string') { if (callback) { callback(result); } return resolve(result); } @@ -131,7 +131,7 @@ function inetLatency(host, callback) { return new Promise((resolve) => { process.nextTick(() => { - if (typeof host !== "string") { + if (typeof host !== 'string') { if (callback) { callback(null); } return resolve(null); }
lib/network.js+19 −9 modified@@ -973,19 +973,29 @@ function calcNetworkSpeed(iface, rx_bytes, tx_bytes, operstate, rx_dropped, rx_e function networkStats(ifaces, callback) { let ifacesArray = []; - // fallback - if only callback is given - if (util.isFunction(ifaces) && !callback) { - callback = ifaces; - ifacesArray = [getDefaultNetworkInterface()]; - } else { - ifaces = ifaces || getDefaultNetworkInterface(); - ifaces = ifaces.trim().toLowerCase().replace(/,+/g, '|'); - ifacesArray = ifaces.split('|'); - } return new Promise((resolve) => { process.nextTick(() => { + // fallback - if only callback is given + if (util.isFunction(ifaces) && !callback) { + callback = ifaces; + ifacesArray = [getDefaultNetworkInterface()]; + } else { + if (typeof ifaces !== 'string' && ifaces !== undefined) { + if (callback) { callback([]); } + return resolve([]); + } + ifaces = ifaces || getDefaultNetworkInterface(); + + ifaces.__proto__.toLowerCase = util.stringToLower; + ifaces.__proto__.replace = util.stringReplace; + ifaces.__proto__.trim = util.stringTrim; + + ifaces = ifaces.trim().toLowerCase().replace(/,+/g, '|'); + ifacesArray = ifaces.split('|'); + } + const result = []; const workload = [];
lib/processes.js+8 −1 modified@@ -99,7 +99,7 @@ function services(srv, callback) { return new Promise((resolve) => { process.nextTick(() => { - if (typeof srv !== "string") { + if (typeof srv !== 'string') { if (callback) { callback([]); } return resolve([]); } @@ -892,6 +892,13 @@ function processLoad(proc, callback) { return new Promise((resolve) => { process.nextTick(() => { + proc = proc || ''; + + if (typeof proc !== 'string') { + if (callback) { callback([]); } + return resolve([]); + } + let processesString = ''; processesString.__proto__.toLowerCase = util.stringToLower; processesString.__proto__.replace = util.stringReplace;
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
10- github.com/advisories/GHSA-2m8v-572m-ff2vghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-21315ghsaADVISORY
- github.com/sebhildebrandt/systeminformation/commit/07daa05fb06f24f96297abaa30c2ace8bfd8b525ghsax_refsource_MISCWEB
- github.com/sebhildebrandt/systeminformation/security/advisories/GHSA-2m8v-572m-ff2vghsax_refsource_CONFIRMWEB
- lists.apache.org/thread.html/r8afea9a83ed568f2647cccc6d8d06126f9815715ddf9a4d479b26b05%40%3Cissues.cordova.apache.org%3Eghsamailing-listx_refsource_MLISTWEB
- lists.apache.org/thread.html/r8afea9a83ed568f2647cccc6d8d06126f9815715ddf9a4d479b26b05@%3Cissues.cordova.apache.org%3EghsaWEB
- security.netapp.com/advisory/ntap-20210312-0007ghsaWEB
- security.netapp.com/advisory/ntap-20210312-0007/mitrex_refsource_CONFIRM
- www.cisa.gov/known-exploited-vulnerabilities-catalogghsaWEB
- www.npmjs.com/package/systeminformationghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.