Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for sanitizing (filtering) style properties #208

Merged
merged 1 commit into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ maplit = "1.0"
tendril = "0.4"
url = "2"
once_cell = "1.10"
cssparser = "0.34.0"

[dev-dependencies]
version-sync = "0.9"
Expand Down
40 changes: 40 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub mod rcdom;
#[cfg(not(ammonia_unstable))]
mod rcdom;

mod style;

use html5ever::interface::Attribute;
use html5ever::serialize::{serialize, SerializeOpts};
use html5ever::tree_builder::{NodeOrText, TreeSink};
Expand Down Expand Up @@ -367,6 +369,7 @@ pub struct Builder<'a> {
strip_comments: bool,
id_prefix: Option<&'a str>,
generic_attribute_prefixes: Option<HashSet<&'a str>>,
style_properties: Option<HashSet<&'a str>>,
}

impl<'a> Default for Builder<'a> {
Expand Down Expand Up @@ -487,6 +490,7 @@ impl<'a> Default for Builder<'a> {
strip_comments: true,
id_prefix: None,
generic_attribute_prefixes: None,
style_properties: None,
}
}
}
Expand Down Expand Up @@ -1651,6 +1655,34 @@ impl<'a> Builder<'a> {
self
}

/// Only allows the specified properties in `style` attributes.
///
/// Irrelevant if `style` is not an allowed attribute.
///
/// Note that if style filtering is enabled style properties will be normalised e.g.
/// invalid declarations and @rules will be removed, with only syntactically valid
/// declarations kept.
///
/// # Examples
///
/// use ammonia::Builder;
/// use maplit::hashset;
///
/// # fn main() {
/// let attributes = hashset!["style"];
/// let properties = hashset!["color"];
/// let a = Builder::new()
/// .generic_attributes(attributes)
/// .filter_style_properties(properties)
/// .clean("<p style=\"font-weight: heavy; color: red\">my html</p>")
/// .to_string();
/// assert_eq!(a, "<p style=\"color:red\">my html</p>");
/// # }
pub fn filter_style_properties(&mut self, value: HashSet<&'a str>) -> &mut Self {
self.style_properties = Some(value);
self
}

/// Constructs a [`Builder`] instance configured with the [default options].
///
/// # Examples
Expand Down Expand Up @@ -2048,6 +2080,7 @@ impl<'a> Builder<'a> {
///
/// * relative URL rewriting
/// * adding `<a rel>` attributes
/// * filtering out banned style properties
/// * filtering out banned classes
fn adjust_node_attributes(
&self,
Expand Down Expand Up @@ -2141,6 +2174,13 @@ impl<'a> Builder<'a> {
attrs.swap_remove(i);
}
}
if let Some(allowed_values) = &self.style_properties {
for attr in &mut *attrs.borrow_mut() {
if &attr.name.local == "style" {
attr.value = style::filter_style_attribute(&attr.value, allowed_values).into();
}
}
}
if let Some(allowed_values) = self.allowed_classes.get(&*name.local) {
for attr in &mut *attrs.borrow_mut() {
if &attr.name.local == "class" {
Expand Down
Loading