Skip to content

fix(registry_keys): truncate the value preview char-safely via jsonguard - #7

Draft
h4x0r wants to merge 2 commits into
mainfrom
fix/registry-value-byte-slice-panic
Draft

fix(registry_keys): truncate the value preview char-safely via jsonguard#7
h4x0r wants to merge 2 commits into
mainfrom
fix/registry-value-byte-slice-panic

Conversation

@h4x0r

@h4x0r h4x0r commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

The defect

crates/memf-windows/src/registry_keys.rs:576 truncated a decoded REG_SZ / REG_EXPAND_SZ / REG_LINK preview with a byte slice:

let s = String::from_utf16_lossy(&words);
if s.len() > 80 {
    format!("{}...", &s[..80])

The value is decoded from the memory image, so it is attacker-controlled. A multi-byte character straddling byte 80 panics and aborts the registry walk rather than previewing a value:

end byte index 80 is not a char boundary; it is inside '世' (bytes 79..82 of string)

Any non-Latin registry value — a Chinese, Japanese, Korean, Cyrillic, Arabic or emoji-bearing string — is a candidate, so this fires on ordinary non-English evidence long before anyone crafts it deliberately.

The repo has been bitten by this class before: src/main.rs:7471 carries a regression test whose comment reads "it previously byte-sliced &value[..77]".

The fix

jsonguard::cap_display(s.as_str(), 80) — the fleet's char-safe truncation helper. This crate already depended on jsonguard; only the memf-windows member needed the dependency line.

cap_display also drops control and bidi-override characters, which a preview rendered next to other evidence must never carry: a right-to-left override in a registry value reorders the analyst's view of neighbouring text.

Commits

  • RED be64da9"A".repeat(79) + "世界", which puts byte 80 inside a 3-byte character. Confirmed failing before the fix with the panic quoted above.
  • GREEN 34c08be — migrate to jsonguard::cap_display.

One behaviour change

Truncation is now marked with (U+2026) rather than three dots, matching the shared helper. format_data_preview_reg_sz_long_string_truncated is updated to that contract, and now asserts the character count rather than the byte length — the byte length of a truncated preview depends on the script it is written in, which is what made the old assertion misleading.

Adoption note

jsonguard::cap_display had zero callers anywhere in the fleet before this change. The correct primitive was published and exported; two byte-slice truncations in this one repo went in around it.

Gate

cargo build, cargo test --workspace (all suites, 0 failed), cargo clippy --all-targets -- -D warnings, cargo fmt --check — all clean.

🤖 Generated with Claude Code

h4x0r and others added 2 commits August 2, 2026 08:20
format_data_preview truncates a decoded REG_SZ/REG_EXPAND_SZ/REG_LINK
value with a byte slice, &s[..80]. The value is decoded from the memory
image and is attacker-controlled, so a multi-byte character straddling
byte 80 aborts the registry walk rather than previewing a value.

79 ASCII characters followed by U+4E16 puts byte 80 inside a 3-byte
character. This test fails on the current implementation:

    end byte index 80 is not a char boundary; it is inside '世'
    (bytes 79..82 of string)

The same defect class was already fixed once in this repo — see the
regression test at src/main.rs:7471, whose comment reads "it previously
byte-sliced &value[..77]".

RED commit: test only, no implementation change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replace the &s[..80] byte slice with jsonguard::cap_display(s, 80). The
fleet already publishes the char-safe truncation helper; this crate
already depends on jsonguard.

cap_display also drops control and bidi-override characters, which a
preview rendered next to other evidence must never carry — a
right-to-left override in a registry value reorders the analyst's view of
neighbouring text.

Truncation is now marked with U+2026 rather than three dots, matching the
shared helper. format_data_preview_reg_sz_long_string_truncated is updated
to that contract and now asserts the character count rather than the byte
length, since the byte length of a truncated preview depends on the script
it is written in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant