sanitize_html/rules/
pattern.rs

1//! This module contains code dedicated to check validity of attribute's value.
2//!
3//! # Examples
4//! ```
5//! use sanitize_html::rules::pattern::Pattern;
6//! use regex::Regex;
7//!
8//! let href = Pattern::regex(Regex::new("^(ftp:|http:|https:|mailto:)").unwrap()) |
9//!     !Pattern::regex(Regex::new("^[^/]+[[:space:]]*:").unwrap());
10//!
11//! assert!(href.matches("filename.xls"));
12//! assert!(href.matches("http://foo.com/"));
13//! assert!(href.matches(" filename with spaces .zip "));
14//! assert!(!href.matches(" javascript  : window.location = '//example.com/'")); // Attempt to make XSS
15//! ```
16
17use regex::Regex;
18
19/// Value pattern
20pub struct Pattern(pub Box<dyn Fn(&str) -> bool + Sync + Send>);
21
22impl Pattern {
23    /// Creates pattern which accepts any value.
24    ///
25    /// # Example
26    /// ```
27    /// use sanitize_html::rules::pattern::Pattern;
28    /// use regex::Regex;
29    ///
30    /// let pattern = Pattern::any();
31    /// assert!(pattern.matches(""));
32    /// assert!(pattern.matches("pants"));
33    /// ```
34    pub fn any() -> Self {
35        Pattern(Box::new(move |_value| true))
36    }
37
38    /// Creates pattern which uses regular expression to check a value. Panics
39    ///
40    /// # Example
41    /// ```
42    /// use sanitize_html::rules::pattern::Pattern;
43    /// use regex::Regex;
44    ///
45    /// let pattern = Pattern::regex(Regex::new("ant").unwrap());
46    /// assert!(!pattern.matches(""));
47    /// assert!(pattern.matches("pants"));
48    /// ```
49    pub fn regex(re: Regex) -> Self {
50        Pattern(Box::new(move |value| re.is_match(value)))
51    }
52
53    /// Checks if a value matches to a pattern.
54    pub fn matches(&self, value: &str) -> bool {
55        (self.0)(value)
56    }
57}
58
59impl ::std::ops::Not for Pattern {
60    type Output = Pattern;
61
62    /// Negates pattern
63    ///
64    /// # Example
65    /// ```
66    /// use sanitize_html::rules::pattern::Pattern;
67    /// use regex::Regex;
68    ///
69    /// let pattern = !Pattern::any();
70    /// assert!(!pattern.matches(""));
71    /// assert!(!pattern.matches("pants"));
72    /// ```
73    fn not(self) -> Self::Output {
74        let cb = self.0;
75        Pattern(Box::new(move |value| !cb(value)))
76    }
77}
78
79impl ::std::ops::BitAnd for Pattern {
80    type Output = Pattern;
81
82    /// Combines two patterns into a pattern which matches a string iff both patterns match that string.
83    ///
84    /// # Example
85    /// ```
86    /// use sanitize_html::rules::pattern::Pattern;
87    /// use regex::Regex;
88    ///
89    /// let pan = Pattern::regex(Regex::new("pan").unwrap());
90    /// let ant = Pattern::regex(Regex::new("ant").unwrap());
91    /// let pattern = pan & ant;
92    ///
93    /// assert!(!pattern.matches("pan"));
94    /// assert!(!pattern.matches("ant"));
95    /// assert!(pattern.matches("pants"));
96    /// ```
97    fn bitand(self, rhs: Pattern) -> Self::Output {
98        let cb1 = self.0;
99        let cb2 = rhs.0;
100        Pattern(Box::new(move |value| cb1(value) && cb2(value)))
101    }
102}
103
104impl ::std::ops::BitOr for Pattern {
105    type Output = Pattern;
106
107    /// Combines two patterns into a pattern which matches a string if one of patterns matches that string.
108    ///
109    /// # Example
110    /// ```
111    /// use sanitize_html::rules::pattern::Pattern;
112    /// use regex::Regex;
113    ///
114    /// let pan = Pattern::regex(Regex::new("pan").unwrap());
115    /// let pot = Pattern::regex(Regex::new("pot").unwrap());
116    /// let pattern = pan | pot;
117    ///
118    /// assert!(pattern.matches("pants"));
119    /// assert!(pattern.matches("pot"));
120    /// assert!(!pattern.matches("jar"));
121    /// ```
122    fn bitor(self, rhs: Pattern) -> Self::Output {
123        let cb1 = self.0;
124        let cb2 = rhs.0;
125        Pattern(Box::new(move |value| cb1(value) || cb2(value)))
126    }
127}