wowlab_tidy/languages/rust/rules/correctness/
bidirectional_unicode.rs1use crate::{Example, FileCtx, Violation, violation};
2
3#[rustfmt::skip]
4const EXAMPLES: &[Example] = &[
5 Example {
6 label: "bidi LRE character",
7 code: "let x = \"\u{202A}test\";",
8 pass: false,
9 },
10 Example {
11 label: "bidi RLO character",
12 code: "let x = \"\u{202E}test\";",
13 pass: false,
14 },
15 Example {
16 label: "normal ASCII",
17 code: "let x = \"hello world\";",
18 pass: true,
19 },
20 Example {
21 label: "bidi LRM character",
22 code: "let x = \"\u{200E}test\";",
23 pass: false,
24 },
25];
26
27crate::line_rule!(
28 bidirectional_unicode,
29 "Ban Unicode bidi control characters that enable trojan-source attacks.",
30 "Bidi control characters can reorder displayed code to hide malicious logic (CVE-2021-42574).",
31 High,
32);
33
34const BIDI_CHARS: &[char] = &[
35 '\u{202A}', '\u{202B}', '\u{202C}', '\u{202D}', '\u{202E}', '\u{2066}', '\u{2067}', '\u{2068}', '\u{2069}', '\u{200E}', '\u{200F}', ];
47
48fn check_bidirectional_unicode(ctx: &FileCtx<'_>) -> Vec<Violation> {
49 let mut out = Vec::new();
50
51 for (i, line) in ctx.lines.iter().enumerate() {
52 let lineno = i + 1;
53
54 for ch in BIDI_CHARS {
55 if line.contains(*ch) {
56 out.push(violation(
57 ctx.rel,
58 lineno,
59 format!(
60 "bidirectional Unicode control character U+{:04X} (trojan-source risk)",
61 *ch as u32
62 ),
63 ));
64 break;
65 }
66 }
67 }
68
69 out
70}
71
72crate::tidy_test!(check_bidirectional_unicode, {
73 crate::example_tests!(EXAMPLES, check_bidirectional_unicode);
74});