128 lines
3.6 KiB
Rust
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)))
|
|
}
|
|
}
|