fix: apply --sanitize=always when output is piped - #3995
Open
pucedoteth wants to merge 1 commit into
Open
Conversation
`SimplePrinter::print_line` writes the input bytes untouched, and the controller
selects it whenever `loop_through` is set -- which is the case whenever stdout is
not a terminal and none of `--color=always`, `--decorations=always`,
`--force-colorization`, `-n` or `-b` was passed.
So an explicit `--sanitize=always` did nothing at all when piping:
$ printf 'a\xe2\x80\xaeb\n' | bat --sanitize=always | od -An -tx1
61 e2 80 ae 62 0a # U+202E survives
That is the case where it matters most. Sanitizing exists to make untrusted bytes
safe for a terminal to render, and redirecting into a file or a pager is exactly
when they get rendered by a terminal later rather than now.
Apply `sanitize()` in `SimplePrinter` when `--sanitize=always` was given. The
passthrough output shape is unchanged -- no header, no decorations -- only the
dangerous bytes are substituted. Every other mode keeps the raw fast path.
The existing sanitize tests all pass `--decorations=always`, which routes to
InteractivePrinter and hides this, so the three added cases deliberately omit it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
pucedoteth
force-pushed
the
fix/sanitize-applies-when-piped
branch
from
September 7, 2026 19:31
1320e9e to
b5dac2b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
--sanitize=alwaysdoes nothing when bat's output is piped.sanitize()is only called fromInteractivePrinter. The controller picksSimplePrinterwheneverloop_throughis set, andloop_throughis true unless stdout is a terminal or one of--color=always,--decorations=always,--force-colorization,-n,-bwas passed.SimplePrinter::print_linewritesline_bufferstraight to the handle.So the flag is silently dropped in exactly the case it matters most.
--sanitizeexists to make untrusted bytes safe for a terminal to render (CVE-2021-42574, added in #3729), and redirecting into a file or a pager is precisely when those bytes get rendered by a terminal later rather than now.bat --sanitize=always untrusted.txt > out.txtreads as a request for safe output and does not produce one.Changes
src/printer.rs—SimplePrinter::print_lineappliessanitize()when--sanitize=alwayswas given:The passthrough output shape is unchanged — no header, no decorations, no line numbers — only the dangerous bytes are substituted:
Every other mode keeps the existing raw
write_allfast path, so--sanitize=neverand the default are byte-for-byte unaffected. Lossy UTF-8 decoding is confined to the sanitizing branch, which seemed acceptable there — the purpose of that branch is to make the bytes safe to display.Scoped deliberately:
--sanitize=autois left alone.autoalready declines to touch plain text, and making it act on piped output would be a behaviour change for people who never asked for it. Only the explicitalwaysis honoured here.--strip-ansi=alwayshas the same gap and is not changed — happy to do it in a follow-up if you want the two kept symmetric, but it is a wider blast radius than the security flag and felt like your call rather than mine.Tests
Three cases in
tests/integration_tests.rs. The existing sanitize tests all pass--decorations=always, which routes toInteractivePrinterand hides this entirely, so the new ones deliberately omit it:sanitize_always_applies_when_output_is_pipedsanitize_always_strips_ansi_when_output_is_pipedsanitize_never_leaves_piped_output_untouchedReverting only
src/printer.rsand keeping the tests fails exactly the two that cover the fix:The
nevercase passes either way — it is there to catch the change over-reaching, not to catch the bug.Full suite green:
🤖 Generated with Claude Code