mod common;
use common::test_yce;
use xss_filters::css;
const CSS_PATTERNS: [&str; 26] = [
"undefined",
"null",
"&",
"1.1",
"10%",
"+10px",
"-10px",
"#fff",
"\u{D7FF}",
"<lone-high-surrogate>", "<lone-low-surrogate>", "\u{1234}567",
"\u{0}",
" ",
"\r\n\t\u{0C}\u{0B}",
"\\",
"\\n\\r\\f\\0\\9\\a\\f",
"-ide_nt",
"\"string\"",
"'string'",
"- _ : ; ( ) \" ' / , % # ! * @ . { } []",
"http://username:password@www.evil.com:8080/?k1=v1&k2=v2#hash",
"url(https://www.evil.com)",
"u\u{0}\u{0}rl(https://www.evil.com)",
"\\u\\r\u{0}\u{0}\\l\u{0}\\((evil.com))",
"expression(body.scrollTop + 50 + px)",
];
const CSS_PATTERNS_TAIL: &str = "(((()))) \\28 \\29";
fn run_css(filter: impl Fn(&str) -> String, expected: &[&str], tail_expected: &str) {
let mut patterns: Vec<&str> = Vec::new();
let mut exp: Vec<&str> = Vec::new();
for (i, (p, e)) in CSS_PATTERNS.iter().zip(expected.iter()).enumerate() {
if i == 9 || i == 10 {
continue;
}
patterns.push(p);
exp.push(e);
}
patterns.push(CSS_PATTERNS_TAIL);
exp.push(tail_expected);
test_yce(filter, &patterns, &exp);
}
#[test]
fn css_value_expression() {
let expected = [
"undefined",
"null",
";-x:'&';-v:",
"1.1",
"10%",
"+10px",
"-10px",
"#fff",
";-x:'\u{D7FF}';-v:",
"", "", ";-x:'\u{1234}567';-v:",
";-x:'\u{FFFD}';-v:",
";-x:' ';-v:",
";-x:'\\d \\a \\9 \\c \\b ';-v:",
";-x:'\\5c ';-v:",
";-x:'\\5c n\\5c r\\5c f\\5c 0\\5c 9\\5c a\\5c f';-v:",
"-ide_nt",
";-x:'\"string\"';-v:",
";-x:'\\27 string\\27 ';-v:",
";-x:'- _ : ; ( ) \" \\27 / , % # ! * @ . \\7b \\7d \\5b \\5d ';-v:",
";-x:'http://username:password@www.evil.com:8080/?k1=v1&k2=v2#hash';-v:",
";-x:'-x-url(https://www.evil.com)';-v:",
";-x:'u\u{FFFD}\u{FFFD}rl(https://www.evil.com)';-v:",
";-x:'\\5c u\\5c r\u{FFFD}\u{FFFD}\\5c l\u{FFFD}\\5c ((evil.com))';-v:",
";-x:'expression(body.scrollTop + 50 + px)';-v:",
];
run_css(
css::css_value,
&expected,
";-x:'(((()))) \\5c 28 \\5c 29';-v:",
);
}
#[test]
fn css_double_quoted_string() {
let expected = [
"undefined",
"null",
"&",
"1.1",
"10%",
"+10px",
"-10px",
"#fff",
"\u{D7FF}",
"",
"",
"\u{1234}567",
"\u{FFFD}",
" ",
"\\d \\a \\9 \\c \\b ",
"\\5c ",
"\\5c n\\5c r\\5c f\\5c 0\\5c 9\\5c a\\5c f",
"-ide_nt",
"\\22 string\\22 ",
"'string'",
"- _ : ; ( ) \\22 ' / , % # ! * @ . \\7b \\7d \\5b \\5d ",
"http://username:password@www.evil.com:8080/?k1=v1&k2=v2#hash",
"-x-url(https://www.evil.com)",
"u\u{FFFD}\u{FFFD}rl(https://www.evil.com)",
"\\5c u\\5c r\u{FFFD}\u{FFFD}\\5c l\u{FFFD}\\5c ((evil.com))",
"expression(body.scrollTop + 50 + px)",
];
run_css(
css::css_double_quoted,
&expected,
"(((()))) \\5c 28 \\5c 29",
);
}
#[test]
fn css_single_quoted_string() {
let expected = [
"undefined",
"null",
"&",
"1.1",
"10%",
"+10px",
"-10px",
"#fff",
"\u{D7FF}",
"",
"",
"\u{1234}567",
"\u{FFFD}",
" ",
"\\d \\a \\9 \\c \\b ",
"\\5c ",
"\\5c n\\5c r\\5c f\\5c 0\\5c 9\\5c a\\5c f",
"-ide_nt",
"\"string\"",
"\\27 string\\27 ",
"- _ : ; ( ) \" \\27 / , % # ! * @ . \\7b \\7d \\5b \\5d ",
"http://username:password@www.evil.com:8080/?k1=v1&k2=v2#hash",
"-x-url(https://www.evil.com)",
"u\u{FFFD}\u{FFFD}rl(https://www.evil.com)",
"\\5c u\\5c r\u{FFFD}\u{FFFD}\\5c l\u{FFFD}\\5c ((evil.com))",
"expression(body.scrollTop + 50 + px)",
];
run_css(
css::css_single_quoted,
&expected,
"(((()))) \\5c 28 \\5c 29",
);
}
const URL_PATTERNS: [&str; 25] = [
"undefined",
"null",
"&",
"1.1",
"10%",
"+10px",
"-10px",
"#fff",
"\\a",
"\u{D7FF}",
"\u{1234}567",
"\u{0}",
" ",
"\r\n\t\u{0C}\u{0B}",
"\\",
"\\n\\r\\f\\0\\9\\a\\f",
"-ide_nt",
"\"string\"",
"'string'",
"- _ : ; ( ) \" ' / , % # ! * @ . { } [ ]",
"http://username:password@www.evil.com:8080/?k1=v1&k2=v2#hash",
"\u{0}\u{8}\u{B}\u{7F}\u{E}-\u{1F}",
")))((('''""""",
"javascript:alert(1)",
"(((()))) \\28 \\29",
];
#[test]
fn css_unquoted_url_filter() {
let expected = [
"undefined",
"null",
"&",
"1.1",
"10%25",
"+10px",
"-10px",
"#fff",
"%5Ca",
"%ED%9F%BF",
"%E1%88%B4567",
"%EF%BF%BD",
"%20",
"%0D%0A%09%0C%0B",
"%5C",
"%5Cn%5Cr%5Cf%5C0%5C9%5Ca%5Cf",
"-ide_nt",
"%22string%22",
"\\27 string\\27 ",
"-%20%20_%20:%20;%20%28%20%29%20%22%20\\27 %20/%20,%20%25%20#%20!%20*%20@%20.%20%7B%20%7D%20%5B%20%5D",
"http://username:password@www.evil.com:8080/?k1=v1&k2=v2#hash",
"%EF%BF%BD%08%0B%7F%0E-%1F",
"%29%29%29%28%28%28\\27 \\27 \\27 %22%22%22%22",
"##javascript:alert%281%29",
"%28%28%28%28%29%29%29%29%20%5C28%20%5C29",
];
test_yce(css::css_unquoted_url, &URL_PATTERNS, &expected);
}
#[test]
fn css_double_quoted_url_filter() {
let expected = [
"undefined",
"null",
"&",
"1.1",
"10%25",
"+10px",
"-10px",
"#fff",
"%5Ca",
"%ED%9F%BF",
"%E1%88%B4567",
"%EF%BF%BD",
"%20",
"%0D%0A%09%0C%0B",
"%5C",
"%5Cn%5Cr%5Cf%5C0%5C9%5Ca%5Cf",
"-ide_nt",
"%22string%22",
"'string'",
"-%20%20_%20:%20;%20(%20)%20%22%20'%20/%20,%20%25%20#%20!%20*%20@%20.%20%7B%20%7D%20%5B%20%5D",
"http://username:password@www.evil.com:8080/?k1=v1&k2=v2#hash",
"%EF%BF%BD%08%0B%7F%0E-%1F",
")))((('''%22%22%22%22",
"##javascript:alert(1)",
"(((())))%20%5C28%20%5C29",
];
test_yce(css::css_double_quoted_url, &URL_PATTERNS, &expected);
}
#[test]
fn css_single_quoted_url_filter() {
let expected = [
"undefined",
"null",
"&",
"1.1",
"10%25",
"+10px",
"-10px",
"#fff",
"%5Ca",
"%ED%9F%BF",
"%E1%88%B4567",
"%EF%BF%BD",
"%20",
"%0D%0A%09%0C%0B",
"%5C",
"%5Cn%5Cr%5Cf%5C0%5C9%5Ca%5Cf",
"-ide_nt",
"%22string%22",
"\\27 string\\27 ",
"-%20%20_%20:%20;%20(%20)%20%22%20\\27 %20/%20,%20%25%20#%20!%20*%20@%20.%20%7B%20%7D%20%5B%20%5D",
"http://username:password@www.evil.com:8080/?k1=v1&k2=v2#hash",
"%EF%BF%BD%08%0B%7F%0E-%1F",
")))(((\\27 \\27 \\27 %22%22%22%22",
"##javascript:alert(1)",
"(((())))%20%5C28%20%5C29",
];
test_yce(css::css_single_quoted_url, &URL_PATTERNS, &expected);
}
#[test]
fn css_string_filters_escape_angle_bracket() {
for out in [
css::css_value("a</style>b"),
css::css_double_quoted("a</style>b"),
css::css_single_quoted("a</style>b"),
] {
assert!(!out.contains('<'), "leaked < in {out:?}");
}
}