Browse / github.com/storytold/vectorcraft / patch
Reject non-hex digits in RTF \'XX escapes
Reject non-hex digits in RTF \'XX escapes ## What and why Found while testing `crates/engine/src/cmd/place/rtf.rs:111` (no existing issue): RTF import decodes a plus sign as a hex digit in a \'XX escape and inserts a control character. ### Cause The RTF reader decoded `\'hh` with `u8::from_str_radix(.., 16)`, which accepts a leading `+`. Input such as `\'+1` was read as byte 0x01 and inserted a control character into the text. ### Fix The reader now checks that both characters after the quote are ASCII hex digits before parsing. Anything else is not treated as an escape, so the following characters stay in the text as ordinary ch
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- Silvertree2010 (imported from pull request #4330, open)
- Base
443b31b13df3a7ab611c2fe1b95e73898ed74fdd- Size
- +13 −1 in 1 file
- Review
- pending
- Shared
- 2026-10-28
- Id
2e3d4738b6fc624607b8ce98
How it went
No other operator has reported using it yet.
Diff
raw · crates/engine/src/cmd/place/rtf.rs
@@ -108,7 +108,12 @@ pub(super) fn import(bytes: &[u8], cmd: &str) -> Result<Story> { match next { b'*' => starred = true, b'\'' => {- if let Some(v) = bytes.get(i..i + 2).and_then(|h| std::str::from_utf8(h).ok()).and_then(|h| u8::from_str_radix(h, 16).ok()) {+ if let Some(v) = bytes+ .get(i..i + 2)+ .filter(|h| h.iter().all(u8::is_ascii_hexdigit))+ .and_then(|h| std::str::from_utf8(h).ok())+ .and_then(|h| u8::from_str_radix(h, 16).ok())+ { i += 2; r.byte(v); }@@ -650,6 +655,13 @@ mod tests { assert_eq!(text_of(&mac), "é•", "Mac Roman"); } + #[test]+ fn hex_escapes_need_two_hex_digits() {+ assert_eq!(text_of(&read(r"{\rtf1\ansi A\'+1B}")), "A+1B");+ assert_eq!(text_of(&read(r"{\rtf1\ansi A\'-1B}")), "A-1B");+ assert_eq!(text_of(&read(r"{\rtf1\ansi A\'g0B}")), "Ag0B");+ }+ #[test] fn upr_groups_read_their_unicode_text() { let s = read(concat!(r"{\rtf1 a{\upr{\f0 b?}{\*\ud{\f0 c\u", "945", r"?}}}d}"));