TermUI.Backend.InputBuffer (TermUI v1.0.0)

View Source

Shared input buffer management for terminal backends.

This module provides secure input buffer handling with:

  • Size limits to prevent memory exhaustion
  • Rate-limited logging to prevent log flooding
  • Consistent behavior across backends

Security

The input buffer protects against memory exhaustion attacks where malformed input streams send unterminated escape sequences. Without protection, the buffer would grow indefinitely.

Buffer Size Limits

  • Maximum buffer size: 1024 bytes
  • Keep size on truncation: 256 bytes

The 256-byte keep size preserves potential partial escape sequences (typical sequences are 8-20 bytes, max CSI is ~100 bytes).

Rate-Limited Logging

Buffer overflow warnings are rate-limited to prevent log flooding attacks. Maximum one warning every 5 seconds per backend instance.

Usage

Backends should use this module instead of implementing their own buffer management:

# In your backend module
alias TermUI.Backend.InputBuffer

# Appending data
new_buffer = InputBuffer.append(state.input_buffer, data)

# Applying limit (returns {buffer, overflow_occurred?})
{limited_buffer, overflowed} = InputBuffer.apply_limit(new_buffer)

# Or use the combined function that handles state
new_state = InputBuffer.append_with_limit(state, data, :input_buffer)

Summary

Functions

Appends data to a buffer and returns the new buffer.

Appends data to a state's buffer field with limit protection.

Applies the buffer size limit, truncating if necessary.

Clears the rate limit state (useful for testing).

Returns the number of bytes kept when truncating.

Returns the maximum buffer size allowed.

Functions

append(buffer, data)

@spec append(binary(), binary()) :: binary()

Appends data to a buffer and returns the new buffer.

This is a simple append without limit checking. Use apply_limit/2 or append_with_limit/4 for protected appending.

Parameters

  • buffer - The existing buffer (binary)
  • data - Data to append (binary)

Returns

The combined buffer.

Examples

iex> TermUI.Backend.InputBuffer.append("hello", " world")
"hello world"

append_with_limit(state, data, field, opts \\ [])

@spec append_with_limit(map(), binary(), atom(), keyword()) :: map()

Appends data to a state's buffer field with limit protection.

This is a convenience function that:

  1. Appends data to the specified buffer field
  2. Applies the size limit
  3. Returns the updated state

Parameters

  • state - Map or struct containing the buffer
  • data - Data to append (binary)
  • field - The field name containing the buffer (atom)
  • opts - Options passed to apply_limit/2

Returns

The updated state with the new buffer value.

Examples

iex> state = %{input_buffer: "partial"}
iex> new_state = TermUI.Backend.InputBuffer.append_with_limit(state, "[A", :input_buffer)
iex> new_state.input_buffer
"partial[A"

apply_limit(buffer, opts \\ [])

@spec apply_limit(
  binary(),
  keyword()
) :: {binary(), boolean()}

Applies the buffer size limit, truncating if necessary.

If the buffer exceeds the maximum size, it is truncated to keep only the most recent bytes (to preserve potential partial escape sequences).

Parameters

  • buffer - The buffer to check (binary)
  • opts - Options:
    • :source - Identifier for rate-limited logging (default: :unknown)
    • :log - Whether to log overflow (default: true)

Returns

Tuple of {limited_buffer, overflowed?}.

Examples

iex> {buffer, false} = TermUI.Backend.InputBuffer.apply_limit("short")
iex> buffer
"short"

iex> long = String.duplicate("x", 2000)
iex> {buffer, true} = TermUI.Backend.InputBuffer.apply_limit(long, source: :test)
iex> byte_size(buffer)
256

clear_rate_limits()

@spec clear_rate_limits() :: :ok

Clears the rate limit state (useful for testing).

Examples

iex> TermUI.Backend.InputBuffer.clear_rate_limits()
:ok

keep_size()

@spec keep_size() :: pos_integer()

Returns the number of bytes kept when truncating.

Examples

iex> TermUI.Backend.InputBuffer.keep_size()
256

max_size()

@spec max_size() :: pos_integer()

Returns the maximum buffer size allowed.

Examples

iex> TermUI.Backend.InputBuffer.max_size()
1024