use regex::Regex;
use std::collections::HashSet;
use std::sync::LazyLock;
use unicase::UniCase;
macro_rules! hashset_unicase {
() => {
hashset![]
};
($($x:expr),+ $(,)?) => {
hashset![
$(
UniCase::ascii($x)
),+
]
};
}
pub static SAFE_ATTRIBUTES: LazyLock<HashSet<UniCase<&'static str>>> =
LazyLock::new(|| {
hashset_unicase![
"accept",
"align",
"alt",
"autocapitalize",
"autoplay",
"background",
"bgcolor",
"border",
"buffered",
"checked",
"cite",
"class",
"cols",
"colspan",
"contenteditable",
"controls",
"coords",
"datetime",
"decoding",
"default",
"dir",
"dirname",
"disabled",
"download",
"draggable",
"for",
"form",
"frameborder",
"headers",
"height",
"hidden",
"high",
"href",
"hreflang",
"id",
"inputmode",
"ismap",
"itemprop",
"kind",
"label",
"lang",
"list",
"loop",
"low",
"max",
"maxlength",
"min",
"minlength",
"multiple",
"muted",
"name",
"optimum",
"pattern",
"placeholder",
"poster",
"preload",
"readonly",
"required",
"reversed",
"role",
"rows",
"rowspan",
"scope",
"selected",
"shape",
"size",
"sizes",
"span",
"spellcheck",
"src",
"srclang",
"srcset",
"start",
"step",
"style",
"tabindex",
"target",
"title",
"translate",
"type",
"usemap",
"value",
"width",
"wrap",
]
});
pub static BOOLEAN_ATTRIBUTES: LazyLock<HashSet<UniCase<&'static str>>> =
LazyLock::new(|| {
hashset_unicase![
"allowfullscreen",
"allowpaymentrequest",
"async",
"autofocus",
"autoplay",
"checked",
"controls",
"default",
"disabled",
"formnovalidate",
"hidden",
"ismap",
"itemscope",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"readonly",
"required",
"reversed",
"selected",
"truespeed",
]
});
pub static URL_ATTRIBUTES: LazyLock<HashSet<UniCase<&'static str>>> =
LazyLock::new(|| hashset_unicase!["href", "src"]);
static ATTRIBUTE_SUFFIX_SAFE: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"[a-zA-z0-9\-]+").unwrap());
pub const SAFE_ATTRIBUTE_PREFIXES: [&str; 2] = ["aria-", "data-"];
pub fn is_safe_attribute(attribute: UniCase<&str>) -> bool {
if SAFE_ATTRIBUTES.contains(&attribute) {
return true;
}
for prefix in &SAFE_ATTRIBUTE_PREFIXES {
if attribute.starts_with(prefix) && ATTRIBUTE_SUFFIX_SAFE.is_match(&attribute) {
return true;
}
}
false
}