forked from argotorg/fe
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathescape.rs
More file actions
104 lines (93 loc) · 3.28 KB
/
Copy pathescape.rs
File metadata and controls
104 lines (93 loc) · 3.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//! HTML and script-context escaping utilities.
/// Escape for embedding in HTML attribute values and general code content.
///
/// Escapes: `& < > " '`
pub fn escape_html(s: &str) -> String {
s.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
.replace('\'', "'")
}
/// Escape for embedding in HTML element content (e.g., `<title>`).
///
/// Only escapes `& < >` — quotes are safe in element text.
pub fn escape_html_text(s: &str) -> String {
s.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
}
/// Escape content for safe embedding inside a `<script>` tag.
///
/// The only dangerous sequence is `</` which can close the script element.
/// We replace `</` with `<\/` which is valid in JS string literals and JSON.
pub fn escape_script_content(s: &str) -> String {
s.replace("</", r"<\/")
}
/// Encode bytes as standard base64 (no external dependency).
pub fn base64_encode(data: &[u8]) -> String {
const CHARS: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut out = String::with_capacity(data.len().div_ceil(3) * 4);
let mut i = 0;
while i + 2 < data.len() {
let n = ((data[i] as u32) << 16) | ((data[i + 1] as u32) << 8) | (data[i + 2] as u32);
out.push(CHARS[(n >> 18 & 0x3f) as usize] as char);
out.push(CHARS[(n >> 12 & 0x3f) as usize] as char);
out.push(CHARS[(n >> 6 & 0x3f) as usize] as char);
out.push(CHARS[(n & 0x3f) as usize] as char);
i += 3;
}
match data.len() - i {
1 => {
let n = (data[i] as u32) << 16;
out.push(CHARS[(n >> 18 & 0x3f) as usize] as char);
out.push(CHARS[(n >> 12 & 0x3f) as usize] as char);
out.push('=');
out.push('=');
}
2 => {
let n = ((data[i] as u32) << 16) | ((data[i + 1] as u32) << 8);
out.push(CHARS[(n >> 18 & 0x3f) as usize] as char);
out.push(CHARS[(n >> 12 & 0x3f) as usize] as char);
out.push(CHARS[(n >> 6 & 0x3f) as usize] as char);
out.push('=');
}
_ => {}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn escape_html_all_chars() {
assert_eq!(
escape_html("a<b>c&d\"e'f"),
"a<b>c&d"e'f"
);
}
#[test]
fn escape_html_text_no_quotes() {
assert_eq!(escape_html_text("a<b>c&d\"e'f"), "a<b>c&d\"e'f");
}
#[test]
fn escape_script_content_closes_tag() {
assert_eq!(escape_script_content("</script>"), r"<\/script>");
assert_eq!(escape_script_content("hello world"), "hello world");
}
#[test]
fn base64_encode_standard_vectors() {
// RFC 4648 test vectors
assert_eq!(base64_encode(b""), "");
assert_eq!(base64_encode(b"f"), "Zg==");
assert_eq!(base64_encode(b"fo"), "Zm8=");
assert_eq!(base64_encode(b"foo"), "Zm9v");
assert_eq!(base64_encode(b"foob"), "Zm9vYg==");
assert_eq!(base64_encode(b"fooba"), "Zm9vYmE=");
assert_eq!(base64_encode(b"foobar"), "Zm9vYmFy");
}
#[test]
fn base64_encode_binary() {
assert_eq!(base64_encode(&[0, 1, 2, 255]), "AAEC/w==");
}
}