fix(fs): reject zero-width and invisible Unicode chars in path components#1552
Merged
fix(fs): reject zero-width and invisible Unicode chars in path components#1552
Conversation
…ents Closes the four UNMITIGATED gaps in the threat model's Unicode section by extending find_unsafe_path_char() to reject the documented invisible / confusable ranges. These chars produce visually-identical filenames that let attackers stash content under names that look benign in any UI. Mitigates: - TM-UNI-003: zero-width chars (U+200B-U+200D, U+2060, U+FEFF, U+180E) - TM-UNI-013: deprecated format chars (U+206A-U+206F) - TM-UNI-012: interlinear annotation markers (U+FFF9-U+FFFB) - TM-UNI-011: tag block (U+E0000-U+E007F) Variable names, script source, and command output stay unaffected -- pass-through there matches Bash. Existing tests that documented the "current behavior" gap are rewritten to assert rejection.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | 1dbc1e2 | Commit Preview URL Branch Preview URL |
May 06 2026, 04:10 AM |
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
Closes the four UNMITIGATED gaps in the threat model's Unicode section by extending
find_unsafe_path_char()to reject the documented invisible / confusable code-point ranges. Without this, an attacker can stash content under a filename that looks visually identical to a benign one in any UI that renders the bytes (terminal, file picker, tool output).Mitigates:
Variable names, script source, and command output are intentionally untouched — pass-through there matches Bash and is already covered by TM-UNI-004 / TM-UNI-005 (accepted risk).
Why
specs/threat-model.mdalready documented the gap and the fix shape ("extendfind_unsafe_path_char()"). All four threats share a single mitigation point, so they collapse into one small, focused change.How
crates/bashkit/src/fs/limits.rs:find_unsafe_path_char()gains fourmatch/range checks for the new code-point ranges, each emitting a Display-only error label ((zero-width),(deprecated format),(interlinear annotation),(tag char)).specs/threat-model.md: status flipped to MITIGATED in §11.2, §11.6, the Unicode summary table, and the Open (Medium) table.Tests
Unit tests in
limits.rs(11 new):_rejectedtest per code-point family (ZWSP, ZWNJ, ZWJ, Word Joiner, BOM, Mongolian Vowel Separator, deprecated format range, interlinear annotation range, tag block boundary samples).test_validate_path_adjacent_chars_allowed— guards against over-blocking by asserting U+200A (HAIR SPACE), U+200E/F (LRM/RLM), and U+2070 (SUPERSCRIPT ZERO) still pass.Integration tests in
crates/bashkit/tests/unicode_security_tests.rs:*_current_behaviortests inzero_width_charsandinvisible_char_tests(which previously documented the gap withlet _ = result;) are rewritten to assertexpect_err, so they would fail if the mitigation regressed.Verified locally:
cargo test -p bashkit --lib: 2207 pass.cargo test -p bashkit --test unicode_security_tests: 71 pass.cargo test -p bashkit --test threat_model_tests: 170 pass (1 pre-existing failure onmainunrelated to this PR —builtin_parser_depth::threat_jq_moderate_nesting_works).cargo test -p bashkit --test overlay_path_validation_tests --test custom_fs_tests --test symlink_overlay_security_tests: all pass.cargo fmt --check,cargo clippy --all-targets -- -D warnings: clean.Test plan
Generated by Claude Code