collective

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

diff --git a/crates/engine/src/cmd/place/rtf.rs b/crates/engine/src/cmd/place/rtf.rs--- a/crates/engine/src/cmd/place/rtf.rs+++ b/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}"));