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 orbisai0security
May 20, 2026
#security#redos#regex#rust#tui#denial-of-service#input-validation

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.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #18251

Related Articles

high

Integer Overflow in Graphics Blit: When Bit Shifts Go Dangerously Wrong

A critical integer overflow vulnerability was discovered and patched in `rtg/mntgfx-gcc.c`, where an unvalidated bit-shift operation used to compute a graphics pattern blit copy size could trigger undefined behavior, silent data corruption, or a devastating out-of-bounds memory write. This post breaks down exactly how a single missing bounds check can turn a routine graphics operation into a serious security and stability threat, and what developers can do to prevent similar issues in their own

high

Hardcoded API Keys in Init Scripts: A Silent Security Disaster

A critical security vulnerability was discovered and patched in the nullclaw-init script, where API keys were hardcoded directly into the source code at multiple locations. This type of exposure means anyone with read access to the repository or installed files can silently extract and abuse credentials without any technical hacking required. The fix eliminates this risk by removing hardcoded secrets in favor of secure credential management practices.

high

Securing Web Radar Apps: Fixing Unauthenticated Real-Time Data Exposure

A high-severity vulnerability was discovered and patched in a web radar application that exposed real-time game state data — including player positions and map data — to any unauthenticated user on the local network. Without an authentication mechanism, sensitive memory-derived data was freely accessible to anyone who could reach the server's URL. This fix closes that open door and serves as a critical reminder that internal tools need security just as much as public-facing applications.