TermUI.Sanitize (TermUI v1.0.0)

View Source

Input sanitization for terminal escape sequence injection prevention.

This module provides utilities to sanitize user input before rendering to prevent terminal escape sequence injection attacks.

Security Model

Terminal escape sequences can be maliciously injected into user input to:

  • Clear the screen
  • Modify terminal colors
  • Move cursor position
  • Execute arbitrary commands (in some terminals)
  • Hide/alter displayed content

This module strips or neutralizes such sequences.

Example

iex> Sanitize.sanitize("Malicious")
"[ESC][31mMalicious[ESC][0m"

iex> Sanitize.sanitize("Malicious", escape: :remove)
"Malicious"

iex> Sanitize.sanitize("Normal text")
"Normal text"

Summary

Functions

Escapes a string for safe rendering by replacing dangerous sequences with safe bracket notation.

Returns true if the string contains ANSI escape sequences.

Sanitizes a string by processing terminal escape sequences.

Strips all ANSI escape sequences from the string.

Validates that a string contains only safe printable characters.

Functions

escape_bracket(input)

@spec escape_bracket(binary()) :: binary()

Escapes a string for safe rendering by replacing dangerous sequences with safe bracket notation.

This is useful when you want to visually indicate that escape sequences were present without allowing them to execute.

Examples

iex> Sanitize.escape_bracket("\e[31m")
"[ESC][31m"

iex> Sanitize.escape_bracket("Normal")
"Normal"

has_ansi?(input)

@spec has_ansi?(binary()) :: boolean()

Returns true if the string contains ANSI escape sequences.

Examples

iex> Sanitize.has_ansi?("\e[31mRed")
true

iex> Sanitize.has_ansi?("Plain text")
false

sanitize(input, opts \\ [])

@spec sanitize(
  binary(),
  keyword()
) :: binary()

Sanitizes a string by processing terminal escape sequences.

Options

  • :escape - How to handle ANSI escapes:

    • :bracket (default) - Replace with safe bracket notation
    • :remove - Remove entirely
    • :keep - Keep as-is (use with caution)
  • :max_length - Maximum string length (default: 10_000)

Returns

  • Sanitized string
  • String truncated if exceeds max_length

Examples

iex> Sanitize.sanitize("\e[31mRed\e[0m")
"[ESC][31mRed[ESC][0m"

iex> Sanitize.sanitize("\e[31mRed\e[0m", escape: :remove)
"Red"

iex> Sanitize.sanitize(String.duplicate("a", 20000))
String.duplicate("a", 10000)

strip_ansi(input)

@spec strip_ansi(binary()) :: binary()

Strips all ANSI escape sequences from the string.

Examples

iex> Sanitize.strip_ansi("\e[31mRed\e[0m")
"Red"

iex> Sanitize.strip_ansi("\e[2J\e[HHello")
"Hello"

validate(input)

@spec validate(binary()) :: :ok | {:error, atom()}

Validates that a string contains only safe printable characters.

Returns :ok if safe, {:error, reason} if unsafe.

Safety Rules

  • Only printable ASCII (32-126) and valid UTF-8
  • No control characters (except tab, newline, carriage return)
  • No ANSI escape sequences
  • No null bytes

Examples

iex> Sanitize.validate("Safe text")
:ok

iex> Sanitize.validate("\e[31mUnsafe")
{:error, :contains_ansi}

iex> Sanitize.validate("Null\x00byte")
{:error, :contains_null_byte}