selectors/
validate.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::error::{StringPatternError, ValidationError};
6use crate::ir;
7use fidl_fuchsia_diagnostics as fdiagnostics;
8
9// NOTE: if we could use the negative_impls unstable feature, we could have a single ValidateExt
10// trait instead of one for each type of selector we need.
11pub trait ValidateExt {
12    fn validate(&self) -> Result<(), ValidationError>;
13}
14
15pub trait ValidateComponentSelectorExt {
16    fn validate(&self) -> Result<(), ValidationError>;
17}
18
19pub trait ValidateTreeSelectorExt {
20    fn validate(&self) -> Result<(), ValidationError>;
21}
22
23pub trait Selector {
24    type Component: ComponentSelector;
25    type Tree: TreeSelector;
26
27    fn component(&self) -> Option<&Self::Component>;
28    fn tree(&self) -> Option<&Self::Tree>;
29}
30
31pub trait ComponentSelector {
32    type Segment: StringSelector;
33    fn segments(&self) -> Option<&[Self::Segment]>;
34}
35
36pub trait TreeSelector {
37    type Segment: StringSelector;
38
39    fn node_path(&self) -> Option<&[Self::Segment]>;
40    fn property(&self) -> Option<&Self::Segment>;
41}
42
43pub trait StringSelector {
44    fn exact_match(&self) -> Option<&str>;
45    fn pattern(&self) -> Option<&str>;
46}
47
48impl<T: Selector> ValidateExt for T {
49    fn validate(&self) -> Result<(), ValidationError> {
50        match (self.component(), self.tree()) {
51            (Some(component), Some(tree)) => {
52                component.validate()?;
53                tree.validate()?;
54            }
55            (None, _) => return Err(ValidationError::MissingComponentSelector),
56            (_, None) => return Err(ValidationError::MissingTreeSelector),
57        }
58        Ok(())
59    }
60}
61
62impl<T: ComponentSelector> ValidateComponentSelectorExt for T {
63    fn validate(&self) -> Result<(), ValidationError> {
64        let segments = self.segments().unwrap_or(&[]);
65        if segments.is_empty() {
66            return Err(ValidationError::EmptyComponentSelector);
67        }
68        let last_idx = segments.len() - 1;
69        for segment in &segments[..last_idx] {
70            segment.validate(StringSelectorValidationOpts {
71                allow_empty: false,
72                ..Default::default()
73            })?;
74        }
75        segments[last_idx].validate(StringSelectorValidationOpts {
76            allow_recursive_glob: true,
77            ..Default::default()
78        })?;
79        Ok(())
80    }
81}
82
83impl<T: TreeSelector> ValidateTreeSelectorExt for T {
84    fn validate(&self) -> Result<(), ValidationError> {
85        let node_path = self.node_path().unwrap_or(&[]);
86        if node_path.is_empty() {
87            return Err(ValidationError::EmptySubtreeSelector);
88        }
89        for segment in node_path {
90            segment.validate(StringSelectorValidationOpts::default())?;
91        }
92        if let Some(segment) = self.property() {
93            segment.validate(StringSelectorValidationOpts::default())?;
94        }
95        Ok(())
96    }
97}
98
99#[derive(Default)]
100struct StringSelectorValidationOpts {
101    allow_recursive_glob: bool,
102    allow_empty: bool,
103}
104
105trait ValidateStringSelectorExt {
106    fn validate(&self, opts: StringSelectorValidationOpts) -> Result<(), ValidationError>;
107}
108
109impl<T: StringSelector> ValidateStringSelectorExt for T {
110    fn validate(&self, opts: StringSelectorValidationOpts) -> Result<(), ValidationError> {
111        match (self.exact_match(), self.pattern()) {
112            (None, None) | (Some(_), Some(_)) => Err(ValidationError::InvalidStringSelector),
113            (Some(exact_match), None) => {
114                if !opts.allow_empty && exact_match.is_empty() {
115                    return Err(ValidationError::InvalidStringSelector);
116                }
117                Ok(())
118            }
119            (None, Some(pattern)) => {
120                if opts.allow_recursive_glob && pattern == "**" {
121                    Ok(())
122                } else {
123                    validate_pattern(pattern)
124                }
125            }
126        }
127    }
128}
129
130/// Checks if the `target` string contains the `forbidden` string without the
131/// character `/` preceding the `forbidden` string.
132fn contains_unescaped(target: &str, forbidden: &str) -> bool {
133    if target.len() < forbidden.len() {
134        return false;
135    }
136    if target.starts_with(forbidden) {
137        return true;
138    }
139    let flen = forbidden.len();
140    for i in 1..(target.len() - flen + 1) {
141        if &target[i..i + flen] == forbidden && !target[i - 1..i + flen].starts_with("\\") {
142            return true;
143        }
144    }
145    false
146}
147
148/// Checks if the payload of an already-parsed `StringSelector::Pattern` is
149/// indeed valid.
150/// Does not validate correctly for patterns in the selector *language*
151fn validate_pattern(pattern: &str) -> Result<(), ValidationError> {
152    if pattern.is_empty() {
153        return Err(ValidationError::EmptyStringPattern);
154    }
155
156    let mut errors = vec![];
157    if contains_unescaped(pattern, "**") {
158        errors.push(StringPatternError::UnescapedGlob);
159    }
160    if !errors.is_empty() {
161        return Err(ValidationError::InvalidStringPattern(pattern.to_string(), errors));
162    }
163    Ok(())
164}
165
166impl Selector for fdiagnostics::Selector {
167    type Component = fdiagnostics::ComponentSelector;
168    type Tree = fdiagnostics::TreeSelector;
169
170    fn component(&self) -> Option<&Self::Component> {
171        self.component_selector.as_ref()
172    }
173
174    fn tree(&self) -> Option<&Self::Tree> {
175        self.tree_selector.as_ref()
176    }
177}
178
179impl ComponentSelector for fdiagnostics::ComponentSelector {
180    type Segment = fdiagnostics::StringSelector;
181
182    fn segments(&self) -> Option<&[Self::Segment]> {
183        self.moniker_segments.as_deref()
184    }
185}
186
187impl TreeSelector for fdiagnostics::TreeSelector {
188    type Segment = fdiagnostics::StringSelector;
189
190    fn node_path(&self) -> Option<&[Self::Segment]> {
191        match self {
192            Self::SubtreeSelector(t) => Some(&t.node_path[..]),
193            Self::PropertySelector(p) => Some(&p.node_path[..]),
194            fdiagnostics::TreeSelectorUnknown!() => None,
195        }
196    }
197
198    fn property(&self) -> Option<&Self::Segment> {
199        match self {
200            Self::SubtreeSelector(_) => None,
201            Self::PropertySelector(p) => Some(&p.target_properties),
202            fdiagnostics::TreeSelectorUnknown!() => None,
203        }
204    }
205}
206
207impl StringSelector for fdiagnostics::StringSelector {
208    fn exact_match(&self) -> Option<&str> {
209        match self {
210            Self::ExactMatch(s) => Some(s),
211            _ => None,
212        }
213    }
214
215    fn pattern(&self) -> Option<&str> {
216        match self {
217            Self::StringPattern(s) => Some(s),
218            _ => None,
219        }
220    }
221}
222
223impl<'a> Selector for ir::Selector<'a> {
224    type Component = ir::ComponentSelector<'a>;
225    type Tree = ir::TreeSelector<'a>;
226
227    fn component(&self) -> Option<&Self::Component> {
228        Some(&self.component)
229    }
230
231    fn tree(&self) -> Option<&Self::Tree> {
232        Some(&self.tree)
233    }
234}
235
236impl<'a> ComponentSelector for ir::ComponentSelector<'a> {
237    type Segment = ir::Segment<'a>;
238
239    fn segments(&self) -> Option<&[Self::Segment]> {
240        Some(&self.segments[..])
241    }
242}
243
244impl<'a> TreeSelector for ir::TreeSelector<'a> {
245    type Segment = ir::Segment<'a>;
246
247    fn node_path(&self) -> Option<&[Self::Segment]> {
248        Some(&self.node)
249    }
250
251    fn property(&self) -> Option<&Self::Segment> {
252        self.property.as_ref()
253    }
254}
255
256impl StringSelector for ir::Segment<'_> {
257    fn exact_match(&self) -> Option<&str> {
258        match self {
259            Self::ExactMatch(s) => Some(s),
260            _ => None,
261        }
262    }
263
264    fn pattern(&self) -> Option<&str> {
265        match self {
266            Self::Pattern(s) => Some(s),
267            _ => None,
268        }
269    }
270}
271
272#[cfg(test)]
273mod tests {
274    use super::*;
275    use std::sync::LazyLock;
276
277    static SHARED_PASSING_TEST_CASES: LazyLock<Vec<(Vec<&'static str>, &'static str)>> =
278        LazyLock::new(|| {
279            vec![
280                (vec![r#"abc"#, r#"def"#, r#"g"#], r#"bob"#),
281                (vec![r#"\**"#], r#"\**"#),
282                (vec![r#"\/"#], r#"\/"#),
283                (vec![r#"\:"#], r#"\:"#),
284                (vec![r#"asda\\\:"#], r#"a"#),
285                (vec![r#"asda*"#], r#"a"#),
286            ]
287        });
288    static SHARED_FAILING_TEST_CASES: LazyLock<Vec<(Vec<&'static str>, &'static str)>> =
289        LazyLock::new(|| {
290            vec![
291                // Checking that path nodes ending with offlimits
292                // chars are still identified.
293                (vec![r#"a**"#], r#"a"#),
294                // Checking that path nodes starting with offlimits
295                // chars are still identified.
296                (vec![r#"**a"#], r#"a"#),
297                // Neither moniker segments nor node paths
298                // are allowed to be empty.
299                (vec![], r#"bob"#),
300            ]
301        });
302
303    #[fuchsia::test]
304    fn tree_selector_validator_test() {
305        let unique_failing_test_cases = [
306            // All failing validators due to property selectors are
307            // unique since the component validator doesn't look at them.
308            (vec![r#"a"#], r#"**"#),
309        ];
310
311        fn create_tree_selector(node_path: &[&str], property: &str) -> fdiagnostics::TreeSelector {
312            let node_path = node_path
313                .iter()
314                .map(|path_node_str| {
315                    fdiagnostics::StringSelector::StringPattern(path_node_str.to_string())
316                })
317                .collect::<Vec<fdiagnostics::StringSelector>>();
318            let target_properties =
319                fdiagnostics::StringSelector::StringPattern(property.to_string());
320            fdiagnostics::TreeSelector::PropertySelector(fdiagnostics::PropertySelector {
321                node_path,
322                target_properties,
323            })
324        }
325
326        for (node_path, property) in SHARED_PASSING_TEST_CASES.iter() {
327            let tree_selector = create_tree_selector(node_path, property);
328            assert!(tree_selector.validate().is_ok());
329        }
330
331        for (node_path, property) in SHARED_FAILING_TEST_CASES.iter() {
332            let tree_selector = create_tree_selector(node_path, property);
333            assert!(
334                ValidateTreeSelectorExt::validate(&tree_selector).is_err(),
335                "Failed to validate tree selector: {:?}",
336                tree_selector
337            );
338        }
339
340        for (node_path, property) in unique_failing_test_cases.iter() {
341            let tree_selector = create_tree_selector(node_path, property);
342            assert!(
343                ValidateTreeSelectorExt::validate(&tree_selector).is_err(),
344                "Failed to validate tree selector: {:?}",
345                tree_selector
346            );
347        }
348    }
349
350    #[fuchsia::test]
351    fn component_selector_validator_test() {
352        fn create_component_selector(
353            component_moniker: &[&str],
354        ) -> fdiagnostics::ComponentSelector {
355            fdiagnostics::ComponentSelector {
356                moniker_segments: Some(
357                    component_moniker
358                        .iter()
359                        .map(|path_node_str| {
360                            fdiagnostics::StringSelector::StringPattern(path_node_str.to_string())
361                        })
362                        .collect::<Vec<fdiagnostics::StringSelector>>(),
363                ),
364                ..fdiagnostics::ComponentSelector::default()
365            }
366        }
367
368        for (component_moniker, _) in SHARED_PASSING_TEST_CASES.iter() {
369            let component_selector = create_component_selector(component_moniker);
370
371            assert!(component_selector.validate().is_ok());
372        }
373
374        for (component_moniker, _) in SHARED_FAILING_TEST_CASES.iter() {
375            let component_selector = create_component_selector(component_moniker);
376
377            assert!(
378                component_selector.validate().is_err(),
379                "Failed to validate component selector: {:?}",
380                component_selector
381            );
382        }
383    }
384}