Skip to content

[BUG] _strip_ansi_codes() leaves DCS/PM/APC escape sequence payload bytes in output #105

@GingerGraham

Description

@GingerGraham

Description

_strip_ansi_codes() uses a multi-pass sed approach to remove ANSI/escape sequences.
The current passes handle CSI sequences (\e[...) and OSC sequences (\e]...) completely,
but only remove the two-byte introducer for Device Control String (DCS: \eP), Privacy
Message (PM: \e^), and Application Program Command (APC: \e_) sequences. The payload
data and ST terminator (\e\\) that follow each opener are left intact in the output.

Steps to Reproduce

  1. Source logging.sh
  2. Call _strip_ansi_codes() with a string containing a DCS, PM, or APC sequence
  3. Observe that the payload survives stripping
source logging.sh
init_logger --no-color

# DCS sequence: ESC P <payload> ESC \
dcs=$'\ePsixel-data-here\e\\'
result=$(_strip_ansi_codes "$dcs")
echo "DCS result: '$result'"
# Expected: ''
# Actual:   'sixel-data-here' + trailing ESC + backslash

# PM sequence: ESC ^ <payload> ESC \
pm=$'\e^metadata\e\\'
result=$(_strip_ansi_codes "$pm")
echo "PM result: '$result'"
# Expected: ''
# Actual:   'metadata' + trailing ESC + backslash

Expected Behavior

All bytes belonging to a DCS, PM, or APC sequence — including the opener, payload, and
ST terminator (\e\\) — should be stripped, producing an empty string for a pure-sequence
input.

Actual Behavior

After step 3 removes the two-byte \eP / \e^ / \e_ introducer, the payload and the
\e\\ ST terminator survive and appear in the sanitised output.

Environment

  • Bash Version: any (sed behaviour is consistent)
  • Shell: bash
  • OS: any
  • bash-logger Version: 2.4.0
  • Installation Method: sourced directly

Logger Configuration

  • Output Targets: console (file output is unaffected as terminals don't interpret
    raw log files; journal output strips ANSI separately)
  • Relevant setting: LOG_UNSAFE_ALLOW_ANSI_CODES=false (the default)

Additional Context

The existing step 2 logic for OSC sequences provides the exact pattern to follow. A
set of additional passes using the same approach applied to DCS, PM, and APC introducers
would complete the coverage:

# After current step2b, before existing step3:

# Remove DCS sequences (\eP...payload...\e\\)
local step2c
step2c=$(printf '%s' "$step2b" | sed "s|${esc}P[^${esc}]*${esc}\\\\||g")

# Remove PM sequences (\e^...payload...\e\\)
local step2d
step2d=$(printf '%s' "$step2c" | sed "s|${esc}\\^[^${esc}]*${esc}\\\\||g")

# Remove APC sequences (\e_...payload...\e\\)
local step2e
step2e=$(printf '%s' "$step2d" | sed "s|${esc}_[^${esc}]*${esc}\\\\||g")

# Rename: existing step3 takes step2e as input
local step3
step3=$(printf '%s' "$step2e" | sed 's/\x1b[^[]//g')

New regression tests should be added to tests/test_ansi_injection.sh covering DCS,
PM, and APC payload stripping.

Minimal Example (if applicable)

source logging.sh
init_logger --no-color --log /dev/null

# All three should produce empty output after stripping:
for seq in $'\ePdata\e\\' $'\e^data\e\\' $'\e_data\e\\'; do
    result=$(_strip_ansi_codes "$seq")
    [[ -z "$result" ]] && echo "PASS" || echo "FAIL: '$result' not fully stripped"
done

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions