Back to Blog
high SEVERITY8 min read

ReDoS in Nushell's TUI: When Search Input Freezes Your Terminal

A high-severity Regular Expression Denial of Service (ReDoS) vulnerability was discovered and patched in Nushell's interactive TUI explorer, where unvalidated user keystrokes could be passed directly into regex compilation, allowing adversarial inputs to consume 100% CPU and freeze the interface. This fix adds proper input validation and length limits to the search input handler, preventing catastrophic backtracking attacks. Understanding this vulnerability is essential for any developer buildin

O
By Orbis AppSec
Published May 20, 2026Reviewed June 3, 2026

Answer Summary

Regular Expression Denial of Service (ReDoS) is a vulnerability where specially crafted input patterns cause regex engines to enter catastrophic backtracking, consuming excessive CPU and freezing applications. In Nushell's TUI explorer, the search input handler compiled user keystrokes directly into regex patterns without validation. The fix adds input length limits and validates search patterns before compilation, preventing adversarial inputs from triggering exponential regex matching behavior. CWE-1333 covers this vulnerability class.

Vulnerability at a Glance

cweCWE-1333
fixAdd input validation, length limits, and safe regex compilation with timeout protection
riskApplication freeze, 100% CPU consumption, denial of service
languageRust (Nushell)
root causeUnvalidated user input passed directly to regex compilation without length limits
vulnerabilityRegular Expression Denial of Service (ReDoS)

ReDoS in Nushell's TUI: When Search Input Freezes Your Terminal

Introduction

Imagine typing a search query into your terminal explorer and watching your entire system grind to a halt — CPU pegged at 100%, the interface completely frozen, and no way out short of killing the process. That's the real-world consequence of a Regular Expression Denial of Service (ReDoS) vulnerability, and it's exactly what was lurking in Nushell's interactive TUI (Text User Interface) explorer.

This post breaks down the vulnerability discovered in crates/nu-explore/src/explore_config/tui.rs, explains how it could be exploited, and walks through the fix that was applied to protect users. Whether you're a Rust developer, a CLI tool maintainer, or simply someone who cares about writing robust software, this is a pattern worth understanding deeply.


The Vulnerability Explained

What Is ReDoS?

Regular Expression Denial of Service (ReDoS) is a class of vulnerability that exploits the way certain regex engines evaluate patterns. Most traditional regex engines use backtracking to find matches — when a path doesn't work, the engine backs up and tries another. For most inputs, this is fast. But certain combinations of patterns and inputs can cause the engine to explore an exponentially growing number of paths before concluding there's no match.

This is called catastrophic backtracking, and it can turn a single search query into an infinite loop.

The Vulnerable Code Path

The vulnerability existed in Nushell's TUI explorer at tui.rs:112. Here's the problematic flow:

User keystroke
     │
     ▼
handle_search_input()   ← tui.rs:112
     │
     ▼
apply_search_filter()   ← app.rs:194
     │
     ▼
Regex::new(user_input)  ← compiled without validation ⚠️

User keystrokes captured by handle_search_input() were passed directly to apply_search_filter() without:

  • ✗ Input sanitization or escaping
  • ✗ Length limits
  • ✗ Debouncing (rate limiting keystrokes)
  • ✗ Validation that the input forms a safe regex

How Could It Be Exploited?

An attacker (or even an unsuspecting user) could type a carefully crafted pattern into the TUI search box. Classic catastrophic backtracking patterns include:

# Catastrophic backtracking examples
(a+)+
(a|aa)+
([a-zA-Z]+)*
(a+)+$

When matched against a non-matching string like "aaaaaaaaaaaaaaaaaab", these patterns force the regex engine to explore an astronomical number of backtracking paths.

Example attack scenario:

# A user opens nu-explore and navigates to a large data table
# They type the following into the search box:
(a+)+zzzzzzzzzzzzzzzzzzzzz

# The regex engine begins evaluating this against every cell in the table
# CPU usage spikes to 100%
# The TUI freezes  no keyboard input is processed
# The user is forced to kill the terminal or reboot

Even without malicious intent, a user experimenting with regex syntax could accidentally trigger this. In a tool designed for exploring data interactively, a frozen interface is a critical failure.

Why Is This Rated High Severity?

  • Availability impact: The application becomes completely unresponsive
  • No authentication required: Any user of the tool can trigger it
  • Ease of exploitation: The payload is trivial to construct
  • No resource recovery: Without a timeout or kill mechanism, the process must be forcibly terminated
  • Data loss risk: Any unsaved state in the explorer session is lost when the process is killed

This maps to CWE-400: Uncontrolled Resource Consumption and is catalogued under the OWASP category of Denial of Service.


The Fix

The patch was applied in crates/nu-explore/src/explore_config/input.rs and addresses the vulnerability through a combination of defensive input handling strategies.

What Changed

Before: Unvalidated Input Flow

// tui.rs:112 (simplified representation of vulnerable pattern)
fn handle_search_input(&mut self, key: KeyEvent) {
    match key.code {
        KeyCode::Char(c) => {
            self.search_query.push(c);
            // Directly passed to filter — no validation!
            self.app.apply_search_filter(&self.search_query);
        }
        // ...
    }
}

// app.rs:194 (simplified)
fn apply_search_filter(&mut self, query: &str) {
    // Compiles user input directly as a regex ⚠️
    if let Ok(re) = Regex::new(query) {
        self.filter_data_with_regex(re);
    }
}

After: Validated, Length-Limited Input

// input.rs (patched)
const MAX_SEARCH_INPUT_LENGTH: usize = 128;

fn handle_search_input(&mut self, key: KeyEvent) {
    match key.code {
        KeyCode::Char(c) => {
            // Enforce length limit before appending
            if self.search_query.len() >= MAX_SEARCH_INPUT_LENGTH {
                return;
            }
            self.search_query.push(c);

            // Validate the regex before applying
            if let Some(safe_query) = validate_search_input(&self.search_query) {
                self.app.apply_search_filter(&safe_query);
            }
        }
        // ...
    }
}

fn validate_search_input(input: &str) -> Option<String> {
    // Reject inputs that are too long
    if input.len() > MAX_SEARCH_INPUT_LENGTH {
        return None;
    }

    // Attempt to compile — but with a complexity budget
    // or escape the input to treat it as a literal string
    // rather than a raw regex pattern
    Some(regex::escape(input))
}

Note: The exact implementation details may vary from the simplified representation above. The core principle — length limiting and input validation before regex compilation — is what matters.

How Does This Fix the Problem?

The patch applies defense in depth through multiple layers:

  1. Length limits: Capping input at 128 characters dramatically reduces the search space available to an attacker. Catastrophic backtracking patterns typically need room to grow — a hard length cap cuts them off at the knees.

  2. Input validation: By validating or escaping the input before passing it to the regex engine, the fix ensures that user input is treated as a literal search string rather than an arbitrary regex pattern. This is the safest default for a general-purpose search box.

  3. Separation of concerns: Moving validation logic into input.rs creates a clear boundary — input handling is now responsible for ensuring data is safe before it ever reaches the application logic layer.


Prevention & Best Practices

This vulnerability is far from unique to Nushell. Any application with a user-facing search or filter feature backed by regex is potentially at risk. Here's how to protect yourself and your users:

1. Never Compile Untrusted Input as Raw Regex

Unless your application is explicitly a regex tool, treat user search input as a literal string. Most regex libraries provide an escape function:

// Rust (regex crate)
use regex::escape;
let safe_pattern = escape(user_input);
let re = Regex::new(&safe_pattern)?;

// Python
import re
safe_pattern = re.escape(user_input)

// JavaScript
const safePattern = user_input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

2. Enforce Input Length Limits Early

Set a reasonable maximum length for search inputs and enforce it at the point of entry — not just at the point of use:

const MAX_QUERY_LEN: usize = 256;

if input.len() > MAX_QUERY_LEN {
    return Err(InputError::TooLong);
}

3. Use a ReDoS-Safe Regex Engine

The Rust regex crate is actually ReDoS-safe by design — it uses a finite automaton approach that guarantees linear time matching. However, this protection only applies when the pattern itself is controlled. User-supplied patterns can still construct inputs that are slow to compile or that exploit edge cases.

If you must allow user-supplied regex patterns, consider:

  • regex crate in Rust: Linear time matching, but validate patterns before compilation
  • RE2: Google's regex library, designed to avoid catastrophic backtracking
  • Timeout wrappers: Set a hard timeout on regex operations
// Using a timeout to bound regex execution time
use std::time::Duration;
use std::thread;

let handle = thread::spawn(move || {
    apply_search_filter(&query)
});

match handle.join_timeout(Duration::from_millis(100)) {
    Ok(result) => result,
    Err(_) => { /* timeout — abort the operation */ }
}

4. Implement Debouncing for Live Search

Even with safe regex handling, compiling a new regex on every keystroke is wasteful. Debouncing ensures the filter only fires after the user has paused typing:

// Conceptual debounce — wait 300ms after last keystroke
fn handle_search_input(&mut self, key: KeyEvent) {
    self.search_query.update(key);
    self.debounce_timer.reset(Duration::from_millis(300), || {
        self.apply_filter();
    });
}

5. Consider Literal Search as the Default

For most data exploration tools, users want to find literal strings — not write regex patterns. Offer regex as an opt-in mode (e.g., toggled with a button or prefix like /regex:), and default to safe literal matching.

Detection Tools

  • vuln-regex-detector: Detects potentially vulnerable regex patterns
  • safe-regex: JavaScript library to check regex safety
  • Clippy (Rust): While it doesn't catch ReDoS directly, linting can surface unsafe patterns
  • CodeQL: GitHub's static analysis tool has queries for ReDoS detection
  • regexploit: Tool for finding ReDoS vulnerabilities in regex patterns

Relevant Standards & References

Standard Reference
CWE CWE-400: Uncontrolled Resource Consumption
CWE CWE-1333: Inefficient Regular Expression Complexity
OWASP Denial of Service Cheat Sheet
OWASP Input Validation Cheat Sheet

Conclusion

The ReDoS vulnerability in Nushell's TUI explorer is a textbook example of how a seemingly innocuous feature — a search box — can become a denial-of-service vector when input isn't properly validated. The fix is elegant in its simplicity: enforce length limits, validate or escape input before it reaches the regex engine, and separate input handling from application logic.

Key Takeaways

  • 🔍 Treat all user input as untrusted, even in local, single-user tools
  • 📏 Enforce length limits early — at the point of input, not the point of use
  • 🛡️ Escape user input before using it as a regex pattern, unless regex mode is explicitly intended
  • ⏱️ Debounce live search to reduce unnecessary computation
  • 🧪 Test with adversarial inputs — include ReDoS patterns in your security test suite

Security vulnerabilities in developer tools deserve just as much attention as vulnerabilities in web applications. The developers who use these tools are often handling sensitive data, and a frozen or crashed tool at the wrong moment can have real consequences.

If you're maintaining a CLI tool or TUI application with search functionality, take 30 minutes today to audit your input handling. The fix is usually simple — the cost of not fixing it is not.


This vulnerability was identified and patched as part of automated security scanning by OrbisAI Security. Responsible disclosure and rapid patching are what keep the open-source ecosystem healthy.

Frequently Asked Questions

What is Regular Expression Denial of Service (ReDoS)?

ReDoS occurs when a regex engine processes specially crafted input that causes catastrophic backtracking, where the regex engine tries exponentially increasing combinations of pattern matches, consuming CPU and freezing the application.

How do you prevent ReDoS in Nushell and similar Rust applications?

Validate input length before regex compilation, implement regex timeouts, use non-backtracking regex engines when possible, and apply input sanitization to remove characters that could trigger backtracking patterns.

What CWE is ReDoS?

CWE-1333 (Inefficient Regular Expression Complexity) covers ReDoS vulnerabilities where regex patterns have excessive complexity.

Is input sanitization alone enough to prevent ReDoS?

No. While sanitization helps, the most effective prevention combines input length limits, regex compilation timeouts, and avoiding user-controlled regex patterns entirely when possible.

Can static analysis detect ReDoS vulnerabilities?

Yes. Tools like Semgrep can detect patterns where user input flows directly into regex compilation, though they cannot always identify vulnerable regex patterns themselves without specialized regex analysis.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #18251

Related Articles

medium

How insecure update manifest parsing happens in C++ UpdateHelper.cpp and how to fix it

TrafficMonitor's software update mechanism in `UpdateHelper.cpp` fetched and parsed update manifests from remote servers without validating the version string or enforcing trusted download URLs, leaving users exposed to man-in-the-middle (MITM) attacks. An attacker on the same network could intercept the update channel and inject a malicious binary under a crafted version string or an HTTP download link pointing to attacker-controlled infrastructure. The fix adds strict version-string sanitizati

high

How integer overflow in malloc happens in C bipartite matching and how to fix it

A high-severity integer overflow vulnerability was discovered in the bipartite matching algorithm implementation where unchecked multiplication operations for memory allocation could wrap around, causing undersized buffer allocations and subsequent heap overflow. The fix replaces vulnerable `malloc(sizeof(int) * V)` patterns with safe `calloc(V, sizeof(int))` calls and adds proper bounds validation to prevent exploitation.

high

How integer truncation heap overflow happens in C++ UEFI ACPI parsing and how to fix it

A high-severity integer truncation vulnerability was discovered in `Mobility.Uefi.Acpi.cpp` where heap allocation sizes were stored in a 16-bit integer (`MO_UINT16`), causing silent truncation when the computed size exceeded 65535 bytes. This led to undersized heap allocations followed by out-of-bounds writes, exploitable by an attacker who can influence ACPI SRAT table contents in virtualized environments. The fix promotes the size variable to `MO_UINTN` (platform-native width) to prevent trunc

critical

How API key exposure in configuration files happens in TOML config and how to fix it

A critical security vulnerability in `commands/webperf.toml` allowed API keys to be hardcoded directly in configuration files, creating a credential exposure risk. The documentation on line 11 suggested developers could provide `CRUX_API_KEY` or `GOOGLE_API_KEY` directly in the config, which could lead to these sensitive credentials being committed to version control or exposed in logs. The fix updated the documentation to explicitly require environment variables and warn against hardcoding cred

high

How path traversal happens in Ruby YARD server and how to fix it

A high-severity path traversal vulnerability (CVE-2026-41493) in YARD versions prior to 0.9.42 allowed attackers to read arbitrary files from servers running `yard server`. This fix upgrades the yard gem from 0.9.26 to 0.9.42 in the Gemfile and Gemfile.lock, closing a dangerous information disclosure vector that could expose configuration files, credentials, and source code.

high

How buffer overflow via sprintf() happens in C networking code and how to fix it

A high-severity buffer overflow vulnerability was discovered in `profile.c` where `sprintf()` was used to format server addresses without any bounds checking. An attacker who could influence the `SERVER_BASE_PORT` value or trigger integer overflow in the port calculation could write beyond the `server_address` buffer. The fix replaces `sprintf()` with `snprintf()` using explicit buffer size limits at both call sites (lines 99 and 220).