fix: strip NUL bytes from history commands (closes #3589)#3590
fix: strip NUL bytes from history commands (closes #3589)#3590AndrianBalanescu wants to merge 1 commit into
Conversation
External tools (e.g. AI agents) can mis-escape values and write raw NUL bytes into the shell history. NUL bytes break downstream consumers that expect valid UTF-8 text — search output, pipes, messagepack serialisation, and so on. Rather than reject the entire otherwise-useful entry, strip the NUL bytes at the point where new history entries are constructed (History::new), so clean and dirty paths alike are handled. Adds a sanitize_command helper with a fast-path for the common case (no NUL bytes present) and a test for both the stripped and clean paths.
Fossier: Manual Review Requested
Score BreakdownTotal Score: 44.2/100 | Confidence: 100% | Outcome: REVIEW
|
Greptile SummaryStrips NUL bytes from history command strings at the single construction point
Confidence Score: 5/5Straightforward single-point sanitization with no behavioral risk for clean commands; safe to merge. The change is minimal and well-contained — one private helper, one call site, two tests. The fast-path avoids any overhead for normal commands, and placement in No files require special attention. Important Files Changed
Reviews (1): Last reviewed commit: "fix: strip NUL bytes from history comman..." | Re-trigger Greptile |
Summary
External tools — in particular AI agents that mis-escape values — can write raw NUL bytes (
\0) into the shell history. For example, writingstart=\0instead ofstart=0lands a literal NUL byte in the command string.NUL bytes break everything downstream that expects valid UTF-8 text:
atuin searchoutput needs to be piped throughtr -d \x27\\000\x27or it corrupts the consuming process, messagepack serialisation chokes, and even simple terminal output is garbled.Fix
Rather than reject the whole entry (which loses an otherwise useful command), this PR strips NUL bytes at the single point where all new history entries are constructed —
History::new. This covers every entry path (import, capture, daemon capture) with one change.The new
sanitize_commandhelper has a fast-path: it checkscommand.contains(\x27\\0\x27)first and returns the string unchanged when clean, so there is zero allocation overhead for the overwhelmingly common case of a normal command.Testing
Added two unit tests in
history::tests:test_sanitize_strips_nul_bytes— verifiesstart=\0 foo\0barbecomesstart= foobartest_sanitize_preserves_clean_command— verifies a normal command is untouchedAll 12 existing
history::tests continue to pass:Closes #3589.