Skip to content

fix: strip NUL bytes from history commands (closes #3589)#3590

Open
AndrianBalanescu wants to merge 1 commit into
atuinsh:mainfrom
AndrianBalanescu:fix-issue-3589-nul-bytes
Open

fix: strip NUL bytes from history commands (closes #3589)#3590
AndrianBalanescu wants to merge 1 commit into
atuinsh:mainfrom
AndrianBalanescu:fix-issue-3589-nul-bytes

Conversation

@AndrianBalanescu

Copy link
Copy Markdown
Contributor

Summary

External tools — in particular AI agents that mis-escape values — can write raw NUL bytes (\0) into the shell history. For example, writing start=\0 instead of start=0 lands a literal NUL byte in the command string.

NUL bytes break everything downstream that expects valid UTF-8 text: atuin search output needs to be piped through tr -d \x27\\000\x27 or 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_command helper has a fast-path: it checks command.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 — verifies start=\0 foo\0bar becomes start= foobar
  • test_sanitize_preserves_clean_command — verifies a normal command is untouched

All 12 existing history:: tests continue to pass:

running 12 tests
test history::store::tests::test_serialize_deserialize_delete ... ok
test history::store::tests::test_serialize_deserialize_create ... ok
test history::tests::disable_secrets ... ok
test history::tests::known_agents_include_pi ... ok
test history::tests::test_sanitize_preserves_clean_command ... ok
test history::tests::test_sanitize_strips_nul_bytes ... ok
test history::tests::test_serialize_deserialize ... ok
test history::tests::test_serialize_deserialize_deleted ... ok
test history::tests::test_serialize_deserialize_version ... ok
test history::tests::test_serialize_deserialize_with_author_and_intent ... ok
test history::store::tests::test_history_skips_corrupt_records ... ok
test history::tests::privacy_test ... ok
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 82 filtered out

Closes #3589.

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.
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Fossier: Manual Review Requested

@andrianbalanescu is a new contributor. A maintainer should review this PR before merging.

Score Breakdown

Total Score: 44.2/100 | Confidence: 100% | Outcome: REVIEW

Signal Value Score Weight
account_age 3028 1.00 0.09
public_repos 16 0.80 0.05
contribution_history 34 0.17 0.05
follower_ratio 0.18 0.09 0.05
bot_signals False 0.50 0.04
open_prs_elsewhere 8 0.00 0.09
closed_prs_elsewhere 5 0.50 0.10
merged_prs_elsewhere 1 0.36 0.08
prior_interaction 0 0.00 0.08
activity_velocity 2 0.67 0.08
pr_content ... 1.00 0.08
commit_email no_email 0.50 0.04
pr_description ... 0.55 0.05
repo_stars 30478 0.30 0.04
org_membership 0 0.20 0.03
commit_verification ... 0.30 0.04
contributor_stars 4 0.08 0.04

@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Strips NUL bytes from history command strings at the single construction point History::new, preventing downstream breakage in search, pipes, and messagepack serialization. The fix uses a contains fast-path so clean commands (the vast majority) incur no allocation.

  • sanitize_command is a focused, private helper called only in History::new, covering all entry paths (capture, import, daemon) without touching deserialization of already-stored records.
  • Two new unit tests verify stripping and passthrough behavior.

Confidence Score: 5/5

Straightforward 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 History::new correctly covers all write paths without touching reads from the database.

No files require special attention.

Important Files Changed

Filename Overview
crates/atuin-client/src/history.rs Adds sanitize_command helper to strip NUL bytes in History::new, with a fast-path for the common clean case and two new unit tests.

Reviews (1): Last reviewed commit: "fix: strip NUL bytes from history comman..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: NUL bytes in history

2 participants