Aug 25, 2026·6 min read·16 visits
Unsanitized rendering of user input in the 'pickem' library allows attackers to inject terminal escape sequences, potentially leading to remote code execution through clipboard hijacking (OSC 52).
The npm package 'pickem' is vulnerable to a terminal escape-sequence injection (CWE-150). Unsanitized terminal outputs allow attackers to execute arbitrary shell commands via clipboard hijacking (OSC 52) or manipulate terminal displays through Control Sequence Introducers (CSI).
The pickem package is an autocomplete and multi-select picker utility for command-line interface (CLI) applications. It is commonly used in interactive workflows where developers select options such as Git branches, pull requests, files, or API response fields. The core functionality depends on rendering lists of choices on the terminal standard output (stdout) and capturing user selection keys.
Because CLI tools frequently ingest and display external data, the strings processed by pickem often originate from untrusted or attacker-controllable sources. In versions prior to 1.0.7, the package printed these strings directly to the terminal emulator without sanitizing control characters. This lack of sanitization exposes users to terminal escape-sequence injection, categorized under CWE-150.
When a terminal emulator processes stdout, it interprets specific non-printable characters as functional commands. By injecting raw ANSI/VT100 escape codes and C0/C1 control characters into an item's label, an attacker can manipulate the terminal session. This enables capabilities ranging from clipboard hijacking to complete visual spoofing of the CLI layout.
The primary defect in pickem versions prior to 1.0.7 is the absence of input sanitation at the terminal output boundary. During rendering, the library uses a core 'chrome' builder located in src/core/chrome.ts to format and write the interactive rows to standard output. While inactive rows were rendered completely raw, active rows utilized a generic utility called stripAnsi.
However, standard ANSI-stripping utilities like stripAnsi are typically designed only to remove Select Graphic Rendition (SGR) sequences. These SGR sequences control visual elements like text color and background attributes. Traditional ANSI strippers fail to target raw C0 and C1 control characters or complex Operating System Commands (OSC).
Terminal emulators parse control characters using a state machine. For instance, receiving an ESC byte (0x1B) followed by specific sequences signals the terminal to switch modes or execute commands. By omitting a comprehensive character-by-character validation pass, pickem allowed raw control bytes—such as ESC (\x1b), BEL (\x07), backspace, and carriage returns—to transition directly to the terminal emulator.
In the vulnerable versions, the library printed strings to stdout with minimal filtering. In version 1.0.7, the maintainers introduced the sanitizeDisplay function and the private helper scrubControls in src/core/width.ts to isolate and strip malicious control sequences.
Below is the implementation of the new sanitization routine:
function scrubControls(s: string): string {
return s
// Remove OSC/DCS/PM/APC/SOS and ST/BEL/EOL
.replace(/\x1b[\]P^_X][\s\S]*?(?:\x07|\x1b\\|$)/g, '')
// Remove CSI (cursor move, erase, etc.)
.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, '')
// Remove remaining C0/C1 and DEL control bytes
.replace(/[\x00-\x1f\x7f\x80-\x9f]/g, '')
}
export function sanitizeDisplay(str: string, { allowStyle = true }: { allowStyle?: boolean } = {}): string {
if (!allowStyle) return scrubControls(str.replace(new RegExp(SGR_RE, 'g'), ''))
const result = str
.split(SGR_RE)
.map((part, i) => (i % 2 === 1 ? part : scrubControls(part)))
.join('')
return /\x1b\[/.test(result) ? `${result}\x1b[0m` : result
}The scrubControls helper employs three distinct regular expressions. The first regex targets Operating System Commands (OSC), Device Control Strings (DCS), and other system-level directives. The second regex neutralizes Control Sequence Introducers (CSI), which govern terminal movement. The third regex functions as a catch-all for remaining C0/C1 control bytes, such as BEL (\x07) and DEL (\x7f).
This architecture ensures that legitimate user-defined styling (such as ANSI text colors) is retained while eliminating any executable escape codes. Additionally, the function appends a reset escape sequence (\x1b[0m) at the end of the string if any color sequence is detected. This prevents accidental color bleeding across other rows in the terminal display.
Exploitation of this vulnerability requires an attacker to inject escape sequences into a data source read by a vulnerable CLI tool. For example, in a collaborative development workflow, an attacker can create a Git branch containing a crafted payload. When another developer runs a CLI picker to select a branch, the payload is rendered.
One severe exploitation technique leverages the OSC 52 sequence to hijack the host operating system's clipboard. The OSC 52 sequence instructs the terminal emulator to write specified text into the clipboard. An attacker can construct a payload that places a malicious command into the clipboard silently.
When the victim subsequently uses a shortcut to paste inside their terminal, the malicious command executes immediately. Another technique involves using CSI sequences to reposition the cursor. By moving the cursor up and erasing preceding lines, an attacker can overwrite application outputs, spoofing verification screens or hiding security alerts.
The impact of terminal escape-sequence injection depends heavily on the capabilities of the host terminal emulator. Emulators supporting OSC 52 clipboard modifications expose the system to arbitrary command execution. Because developers frequently execute CLI tools within administrative or elevated development contexts, this vector can lead to immediate host compromise.
For terminal emulators that disable OSC 52 or restrict clipboard writes, the impact shifts toward visual integrity compromise and denial of service. Attackers can execute CSI-based UI spoofing to mislead developers into running insecure actions. For instance, a spoofed prompt can trick a developer into approving a malicious deployment or committing to a compromised branch.
Furthermore, continuous injection of BEL (\x07) or backspace characters can degrade terminal performance or trigger excessive system noise. This creates a local denial-of-service condition that renders the shell session unusable until the terminal process is restarted.
The primary remediation for this vulnerability is upgrading the pickem dependency to version 1.0.7 or newer. This release implements the sanitizeDisplay function globally across all rendering pathways. This covers inactive rows, active rows, and custom formatting wrappers within the application.
To upgrade the package in a Node.js project, run the following command in your repository terminal:
npm install pickem@latestIf upgrading immediately is not feasible, implement a pre-rendering sanitization wrapper on all external strings passed to the library. Applications can utilize a utility that strips non-printable C0 and C1 characters before they are passed into the choices list. This prevents the terminal from processing downstream payloads.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H| Attribute | Detail |
|---|---|
| CWE ID | CWE-150 |
| Attack Vector | Local / Network-Adjacent (via external inputs like branch names or PR titles) |
| CVSS v3.1 Score | 7.8 |
| EPSS Score | N/A |
| Exploit Status | Proof of Concept (PoC) available in official unit tests |
| CISA KEV Status | Not Listed |
CVE-2026-109618 (dubbed PHP2Shell) is a critical vulnerability in the PHP engine core resulting from improper out-of-memory exception handling via longjmp(). When request allocation fails mid-processing, non-local control flow bypasses cleanup procedures, leaving memory structures uninitialized or in a use-after-free state during request shutdown.
CVE-2026-109619 (PHP2Shell) is a critical Use-After-Free vulnerability in the core PHP engine caused by improper global state cleanup during out-of-memory exception handling across persistent worker requests.
In Python virtualenv prior to version 21.14.9, activation script generators and configuration writers use Path.exists() before writing files. Because Path.exists() resolves symbolic links, it returns False for dangling symlinks. Consequently, virtualenv skips unlinking pre-planted symbolic links or hard links, writing data directly to target locations outside the intended virtual environment directory.
A remote code execution vulnerability exists in Microsoft .NET due to untrusted data deserialization within the PresentationCore component. The flaw stems from implicit auto-conversion flags during clipboard data retrieval operations.
An out-of-bounds write vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio allows unauthenticated remote attackers to execute arbitrary code via crafted pixel-shader bytecode or auto-converted clipboard payloads.
ASP.NET Core contains a thread deadlock vulnerability (CWE-833) that allows an unauthenticated remote attacker to cause a denial of service over the network by exhausting server thread pool resources.