Skip to main content

aster_forge_utils/
html.rs

1//! HTML and inline-script escaping helpers.
2//!
3//! These helpers are intentionally small and dependency-free. They are meant for server-side
4//! placeholder injection into already generated HTML, not for sanitizing untrusted rich HTML.
5
6/// Escapes text for HTML text or attribute placeholder insertion.
7pub fn escape_html(value: impl AsRef<str>) -> String {
8    value
9        .as_ref()
10        .replace('&', "&amp;")
11        .replace('"', "&quot;")
12        .replace('\'', "&#39;")
13        .replace('<', "&lt;")
14        .replace('>', "&gt;")
15}
16
17/// Escapes a JSON string fragment before embedding it in an inline `<script>` context.
18///
19/// Call this after JSON serialization when the resulting text is inserted into a script block.
20/// It prevents HTML parser breakouts through `</script>`-relevant characters and preserves the
21/// JavaScript line terminator semantics of U+2028 and U+2029.
22pub fn escape_script_json(value: impl AsRef<str>) -> String {
23    value
24        .as_ref()
25        .replace('&', "\\u0026")
26        .replace('<', "\\u003C")
27        .replace('>', "\\u003E")
28        .replace('\u{2028}', "\\u2028")
29        .replace('\u{2029}', "\\u2029")
30}
31
32#[cfg(test)]
33mod tests {
34    use super::{escape_html, escape_script_json};
35
36    #[test]
37    fn escape_html_replaces_markup_and_quote_characters() {
38        assert_eq!(
39            escape_html(r#"<meta title="A&B's">"#),
40            "&lt;meta title=&quot;A&amp;B&#39;s&quot;&gt;"
41        );
42    }
43
44    #[test]
45    fn escape_script_json_replaces_html_parser_breakout_characters() {
46        assert_eq!(
47            escape_script_json("</script>&\u{2028}\u{2029}"),
48            "\\u003C/script\\u003E\\u0026\\u2028\\u2029"
49        );
50    }
51}