newsletter-to-web/sanitize-html-rs/src/rules/pattern.rs

128 lines
3.6 KiB
Rust

//! This module contains code dedicated to check validity of attribute's value.
//!
//! # Examples
//! ```
//! use sanitize_html::rules::pattern::Pattern;
//! use regex::Regex;
//!
//! let href = Pattern::regex(Regex::new("^(ftp:|http:|https:|mailto:)").unwrap()) |
//! !Pattern::regex(Regex::new("^[^/]+[[:space:]]*:").unwrap());
//!
//! assert!(href.matches("filename.xls"));
//! assert!(href.matches("http://foo.com/"));
//! assert!(href.matches(" filename with spaces .zip "));
//! assert!(!href.matches(" javascript : window.location = '//example.com/'")); // Attempt to make XSS
//! ```
use regex::Regex;
/// Value pattern
pub struct Pattern(pub Box<dyn Fn(&str) -> bool + Sync + Send>);
impl Pattern {
/// Creates pattern which accepts any value.
///
/// # Example
/// ```
/// use sanitize_html::rules::pattern::Pattern;
/// use regex::Regex;
///
/// let pattern = Pattern::any();
/// assert!(pattern.matches(""));
/// assert!(pattern.matches("pants"));
/// ```
pub fn any() -> Self {
Pattern(Box::new(move |_value| true))
}
/// Creates pattern which uses regular expression to check a value. Panics
///
/// # Example
/// ```
/// use sanitize_html::rules::pattern::Pattern;
/// use regex::Regex;
///
/// let pattern = Pattern::regex(Regex::new("ant").unwrap());
/// assert!(!pattern.matches(""));
/// assert!(pattern.matches("pants"));
/// ```
pub fn regex(re: Regex) -> Self {
Pattern(Box::new(move |value| re.is_match(value)))
}
/// Checks if a value matches to a pattern.
pub fn matches(&self, value: &str) -> bool {
(self.0)(value)
}
}
impl ::std::ops::Not for Pattern {
type Output = Pattern;
/// Negates pattern
///
/// # Example
/// ```
/// use sanitize_html::rules::pattern::Pattern;
/// use regex::Regex;
///
/// let pattern = !Pattern::any();
/// assert!(!pattern.matches(""));
/// assert!(!pattern.matches("pants"));
/// ```
fn not(self) -> Self::Output {
let cb = self.0;
Pattern(Box::new(move |value| !cb(value)))
}
}
impl ::std::ops::BitAnd for Pattern {
type Output = Pattern;
/// Combines two patterns into a pattern which matches a string iff both patterns match that string.
///
/// # Example
/// ```
/// use sanitize_html::rules::pattern::Pattern;
/// use regex::Regex;
///
/// let pan = Pattern::regex(Regex::new("pan").unwrap());
/// let ant = Pattern::regex(Regex::new("ant").unwrap());
/// let pattern = pan & ant;
///
/// assert!(!pattern.matches("pan"));
/// assert!(!pattern.matches("ant"));
/// assert!(pattern.matches("pants"));
/// ```
fn bitand(self, rhs: Pattern) -> Self::Output {
let cb1 = self.0;
let cb2 = rhs.0;
Pattern(Box::new(move |value| cb1(value) && cb2(value)))
}
}
impl ::std::ops::BitOr for Pattern {
type Output = Pattern;
/// Combines two patterns into a pattern which matches a string if one of patterns matches that string.
///
/// # Example
/// ```
/// use sanitize_html::rules::pattern::Pattern;
/// use regex::Regex;
///
/// let pan = Pattern::regex(Regex::new("pan").unwrap());
/// let pot = Pattern::regex(Regex::new("pot").unwrap());
/// let pattern = pan | pot;
///
/// assert!(pattern.matches("pants"));
/// assert!(pattern.matches("pot"));
/// assert!(!pattern.matches("jar"));
/// ```
fn bitor(self, rhs: Pattern) -> Self::Output {
let cb1 = self.0;
let cb2 = rhs.0;
Pattern(Box::new(move |value| cb1(value) || cb2(value)))
}
}