diff --git a/swarm-controller/src/review_classifier/review.rs b/swarm-controller/src/review_classifier/review.rs index 621537b5..799cdb2f 100644 --- a/swarm-controller/src/review_classifier/review.rs +++ b/swarm-controller/src/review_classifier/review.rs @@ -736,7 +736,7 @@ fn table(rows: &[Row]) -> String { } /// `text` as one table cell's inline text: on one line, with every character -/// that could end the cell, start markup or raw HTML escaped, and a zero-width +/// that could end the cell, start markup, math (`$`) or raw HTML escaped, and a zero-width /// space after every `@`, `#`, `!`, `://` and `www.`, so it carries no live /// mention, issue or PR reference, or link. The forge links issue and PR /// numbers and bare URLs itself, so the escapes alone don't stop those. @@ -748,7 +748,8 @@ fn cell(text: &str) -> String { } for c in word.chars() { match c { - '\\' | '|' | '`' | '*' | '_' | '~' | '[' | ']' | '(' | ')' | '<' | '>' | '&' => { + '\\' | '|' | '`' | '*' | '_' | '~' | '[' | ']' | '(' | ')' | '<' | '>' | '&' + | '$' => { out.push('\\'); out.push(c); } @@ -1484,6 +1485,29 @@ mod tests { ); } + #[test] + fn a_reason_carries_no_live_math_delimiter() { + let mut r = row(None, true, true); + r.reason = + "uses https://${hyperhiveCfg.swarm.forge.domain}/metrics and $$x$$ | y".to_owned(); + let body = table(&[r]); + let line = body.lines().nth(2).expect("a data row"); + assert!( + line.contains("\\${hyperhiveCfg") && line.contains("\\$\\$x\\$\\$"), + "{line}" + ); + let bare = line + .char_indices() + .filter(|(i, c)| *c == '$' && (*i == 0 || line.as_bytes()[i - 1] != b'\\')) + .count(); + assert_eq!(bare, 0, "{line}"); + let unescaped = line + .char_indices() + .filter(|(i, c)| *c == '|' && (*i == 0 || line.as_bytes()[i - 1] != b'\\')) + .count(); + assert_eq!(unescaped, 8, "{line}"); + } + #[test] fn a_reason_carries_no_link_or_reference() { let text = cell("see https://evil.example/x, WWW.evil.example, #1, o/r#3 and !4");