# Patch Analysis — CVE-2026-50289 (systeminformation `networkInterfaces()`)

## Fix under analysis

- **Repository:** `sebhildebrandt/systeminformation`
- **Fix commit:** `bbfddde48672d0ee124fefdb3cb4442fd9dd4f03`
  ("networkInterfaces() fix unsanitized command (linux)", 5.31.7)
- **Vulnerable parent:** `da1cbf5eb09907cf5c3431f4c9ea441b7a227617` (v5.31.6)
- **Latest checked:** `9e79988` (v5.31.17) — fix still present.

## What the fix changes

Files touched: `lib/network.js` (+ `CHANGELOG.md`, `docs/*` documentation only).

`lib/network.js`:

1. Added import:
   ```js
   const readFileSync = require('fs').readFileSync;
   ```
2. In `checkLinuxDCHPInterfaces(file)`, replaced the shell call:
   ```js
   // BEFORE (vulnerable)
   const cmd = `cat ${file} 2> /dev/null | grep 'iface\\|source'`;
   const lines = execSync(cmd, util.execOptsLinux).toString().split('\n');
   // AFTER (fixed)
   const content = readFileSync(file, { encoding: 'utf8' });
   const lines = content.split('\n').filter((l) => /iface|source/.test(l));
   ```

The downstream parsing logic is unchanged: lines are split, `iface ... inet dhcp`
lines push `parts[1]` (the interface name) onto the result, and `source` lines
recurse via `checkLinuxDCHPInterfaces(line.split(' ')[1])`.

## Behavior before vs. after

| Aspect | Before (5.31.6) | After (5.31.7) |
|---|---|---|
| File read mechanism | `execSync(`cat ${file} \| grep ...`)` via `/bin/sh` | `fs.readFileSync(file)` (no shell) |
| `${file}` origin | `line.split(' ')[1]` of a `source` directive (attacker-writable) | same |
| Shell metacharacters in `${file}` | interpreted by `/bin/sh` → arbitrary command execution | inert — `readFileSync` treats them as literal path bytes; non-existent path → caught by `try/catch` |
| Line filtering | shell `grep 'iface\|source'` | in-JS `.filter((l) => /iface|source/.test(l))` (equivalent) |
| Source recursion | preserved (recursive `cat`) | preserved (recursive `readFileSync`) |

## Assumptions the fix makes

1. **The only shell sink on this path was the `cat ${file} | grep` call.** The
   fix assumes nothing else in `checkLinuxDCHPInterfaces` interpolates
   file-derived content into a shell command. Verified: the only other parsed
   token (`parts[1]` from `iface` lines) is pushed to a list and later used in
   `DHCPNics.indexOf(iface)` — never interpolated into a command.
2. **`readFileSync` is a safe substitute for `cat | grep`.** Correct: it
   performs a pure file read with no shell, so metacharacter injection is
   impossible. The in-JS regex filter preserves the same line-selection
   semantics.
3. **All callers funnel through `checkLinuxDCHPInterfaces`.** The public
   `networkInterfaces()` API and aggregate callers `getStaticData()` /
   `getAllData()` reach this helper via `getLinuxDHCPNics()` →
   `checkLinuxDCHPInterfaces('/etc/network/interfaces')`. There is no parallel
   DHCP-discovery path that bypasses this helper.

## Code paths / inputs the fix does NOT cover (examined)

- **`iface`-directive interface name.** An attacker who can write
  `/etc/network/interfaces.d/*` can also craft `iface <name> inet dhcp` lines.
  The parsed name (`parts[1]`) does **not** reach a shell — it is only used in
  `DHCPNics.indexOf(iface)` inside `getLinuxIfaceDHCPstatus()`. Tested
  (Candidate A): no marker created on vulnerable **or** fixed. Not a variant.
- **Other `execSync`/`exec` sites in `lib/network.js`.** The
  `cat /sys/class/net/${ifaceSanitized}/...` block (~lines 943-967) and the
  `netstat`/`ifconfig` blocks (~lines 1414-1450) interpolate interface names
  sourced from `os.networkInterfaces()` (system-controlled) and pass them
  through `util.sanitizeString()`. They do not consume attacker-writable file
  content; they do not share this CVE's trust boundary and are not bypasses of
  this fix.
- **`getLinuxDHCPNics()` first branch** (`execSync('ip a 2> /dev/null')`) uses a
  static command string with no interpolation. Not a sink.

## Completeness assessment

**The fix is complete for the command-injection root cause of CVE-2026-50289.**

- It removes the shell from the only attacker-controlled-file→shell sink in the
  `networkInterfaces()` DHCP discovery flow.
- Bypass testing with three metacharacter styles (`;`, `$()`, backticks) on the
  fixed release (5.31.7) and the latest release (v5.31.17) produced **no**
  command execution.
- The sibling attacker-influenceable data path (the `iface`-directive name)
  was confirmed by runtime test not to reach a shell.

No gap remains for the Coding stage to close for this CVE.

## Target threat model

`SECURITY.md` states the project takes all security bugs seriously, will
"Audit code to find any potential similar problems," and prepare fixes for all
maintained releases. The attacker preconditions for this CVE (Linux; attacker
can write a file reached through `/etc/network/interfaces` source recursion)
define the in-scope trust boundary. The variant search remained inside that
boundary and found no additional reachable sink.
