Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Feb 6, 2026

Addresses review feedback on PR #54 identifying three security bypasses in ANSI escape sequence sanitization that could enable terminal escape injection attacks.

Changes

CSI parameter byte handling

  • Extended pattern to match DEC private modes (\e[?25l, \e[?1049h) and parameter prefixes (<, >, =, !)
  • Previous pattern only matched [0-9;], missing valid CSI sequences with other parameter bytes

OSC sequence termination

  • Added ST terminator (\e\\) handling alongside existing BEL (\a) support
  • Implemented non-greedy matching via sed loop to handle embedded escape sequences and prevent cross-sequence matching
  • Pattern \([^\x1b]\|\x1b[^\\]\)* matches any char except ESC, or ESC if not followed by backslash

Independent sanitization modes

  • Verified newline and ANSI sanitization operate independently
  • Previously untested: LOG_UNSAFE_ALLOW_NEWLINES=true + LOG_UNSAFE_ALLOW_ANSI_CODES=false should preserve newlines while stripping ANSI

Test coverage

Added 6 tests covering:

  • DEC private modes (\e[?25l)
  • ST-terminated OSC (\e]0;title\e\\)
  • OSC with embedded escapes (\e]0;\e[31mred\e\\)
  • Multiple consecutive OSC sequences
  • Mixed-mode sanitization
  • Other parameter bytes (\e[>c)

250 tests passing.


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Fix log sanitization logic in pull request fix(sanitization): close security gaps in ANSI escape sequence stripping Feb 6, 2026
Copilot AI requested a review from GingerGraham February 6, 2026 14:33
@GingerGraham GingerGraham marked this pull request as ready for review February 6, 2026 17:03
Copilot AI review requested due to automatic review settings February 6, 2026 17:03
@GingerGraham GingerGraham merged commit 92c78d2 into bug/52-recommended-fixes-02 Feb 6, 2026
5 checks passed
@GingerGraham GingerGraham deleted the copilot/sub-pr-54 branch February 6, 2026 17:03
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens bash-logger’s ANSI/terminal escape sanitization to prevent additional bypasses that could enable terminal escape injection, and adds targeted tests for the newly covered cases.

Changes:

  • Expands CSI stripping to cover DEC private modes and additional CSI parameter bytes.
  • Improves OSC stripping by supporting ST (ESC \) termination and handling embedded escapes / consecutive OSC sequences.
  • Adds tests for the new CSI/OSC cases and for mixed newline/ANSI sanitization mode behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
logging.sh Updates _strip_ansi_codes to recognize more CSI/OSC variants (DEC private modes; ST-terminated OSC via a sed loop).
tests/test_ansi_injection.sh Adds regression tests for DEC private modes, ST-terminated OSC, embedded escapes inside OSC, consecutive OSC sequences, mixed sanitization modes, and extra CSI parameter bytes.

Comment on lines 577 to +585
# Remove CSI (Control Sequence Introducer) sequences: ESC [ ... letter
# Includes color codes (\e[...m), cursor movement (\e[H), clearing (\e[2J), etc.
# Pattern: \e[ followed by zero or more digits/semicolons, followed by a letter
# Also handles DEC private modes (e.g., \e[?25l, \e[?1049h) and other parameter bytes
# Pattern: \e[ followed by zero or more parameter bytes ([<=>?!] plus digits/semicolons),
# followed by a letter or @
local step1
# Use direct escapes to avoid quoting issues in patterns
# shellcheck disable=SC1117
step1=$(printf '%s' "$input" | sed 's/\x1b\[[0-9;]*[a-zA-Z@]//g')
step1=$(printf '%s' "$input" | sed 's/\x1b\[[0-9;<?>=!]*[a-zA-Z@]//g')
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSI stripping regex is still not spec-complete: CSI “parameter bytes” include ':' (0x3A) and the final byte can be any in 0x40–0x7E (e.g., sequences ending in '' like "\e[1"). With the current [0-9;<?>=!]*[a-zA-Z@] pattern, valid CSI sequences can remain unstripped, which keeps a terminal escape injection bypass open. Consider widening the parameter-byte and final-byte character classes to cover the full CSI grammar (and optionally intermediate bytes 0x20–0x2F).

Copilot uses AI. Check for mistakes.
Comment on lines +595 to +599
# Pattern: \([^\x1b]\|\x1b[^\\]\)* matches any char except ESC, OR ESC if not followed by \
# This allows embedded ESC codes like \e[31m while still stopping at \e\\ terminator
# The loop ensures multiple consecutive OSC sequences are all removed
# shellcheck disable=SC1117
step2=$(printf '%s' "$step2" | sed ':loop; s/\x1b\]\(\([^\x1b]\|\x1b[^\\]\)*\)\x1b\\//g; t loop')
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ST-terminated OSC sed pattern uses \| alternation, which is a GNU sed BRE extension and will fail on BSD/macOS sed. Since this sanitizer is security-sensitive, a non-portable sed expression can silently leave OSC sequences unstripped on macOS. Please rewrite this using a portable approach (e.g., sed -E with |, or another POSIX-compatible implementation) and ensure the behavior is covered across supported platforms.

Suggested change
# Pattern: \([^\x1b]\|\x1b[^\\]\)* matches any char except ESC, OR ESC if not followed by \
# This allows embedded ESC codes like \e[31m while still stopping at \e\\ terminator
# The loop ensures multiple consecutive OSC sequences are all removed
# shellcheck disable=SC1117
step2=$(printf '%s' "$step2" | sed ':loop; s/\x1b\]\(\([^\x1b]\|\x1b[^\\]\)*\)\x1b\\//g; t loop')
# Pattern (ERE): ([^\x1b]|\x1b[^\\])* matches any char except ESC, OR ESC if not followed by \
# This allows embedded ESC codes like \e[31m while still stopping at \e\\ terminator
# The loop ensures multiple consecutive OSC sequences are all removed
# shellcheck disable=SC1117
step2=$(printf '%s' "$step2" | sed -E ':loop; s/\x1b\](([^\x1b]|\x1b[^\\])*)\x1b\\//g; t loop')

Copilot uses AI. Check for mistakes.
Comment on lines +366 to +382
# Test: Other CSI parameter bytes are handled
test_other_csi_parameter_bytes_stripped() {
start_test "CSI sequences with various parameter bytes are stripped"

# Test various parameter byte prefixes
local malicious_input=$'A\e[>cB\e[=cC\e[!pD'
local expected="ABCD"

LOG_UNSAFE_ALLOW_ANSI_CODES="false"
local sanitized
sanitized=$(_strip_ansi_codes "$malicious_input")

if [[ "$sanitized" == "$expected" ]]; then
pass_test
else
fail_test "Other parameter bytes not stripped: got '$sanitized' expected '$expected'"
fi
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These new tests cover additional CSI parameter bytes, but they still don’t exercise common non-alphabetic CSI final bytes (e.g., sequences ending in ~). Adding at least one case like "ESC [ 1 ~" would prevent regressions where valid CSI sequences are not stripped.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants