nom_xml/
config.rs

1use crate::warnln;
2
3use std::io::Write;
4/// Config is currently for setting up the parser to allow or prevent external entity parsing.
5/// It's important for preventing some security risks, but it's has not been thoroughly tested for all XML attacks.
6/// It prevents parsing of external entities by default, warns the user if it's enabled, and asks the user to confirm proceeding with external parsing in the terminal.
7
8#[derive(Clone, Default, Debug)]
9pub struct Config {
10    pub external_parse_config: ExternalEntityParseConfig,
11
12    #[cfg(feature = "experimental")]
13    pub targeted_parsing: Option<TargetedParsingConfig>,
14}
15
16#[derive(Clone, Default, Debug)]
17pub struct ExternalEntityParseConfig {
18    pub allow_ext_parse: bool,
19    pub ignore_ext_parse_warning: bool,
20    pub base_directory: Option<String>,
21}
22
23#[cfg(feature = "experimental")]
24#[derive(Clone, Debug)]
25pub struct TargetedParsingConfig {
26    pub tag_name: String,
27    pub parse_multiple: bool, // True to parse multiple elements, false for a single element
28}
29
30pub(crate) fn check_config(config: &Config) -> Result<(), Box<dyn std::error::Error>> {
31    match config {
32        Config {
33            external_parse_config:
34                ExternalEntityParseConfig {
35                    allow_ext_parse: true,
36                    ignore_ext_parse_warning: false,
37                    ..
38                },
39        } => {
40            warnln!("The configuration `{:?}` allows external entity parsing which might expose the system to an XML External Entity (XXE) attack.\nThis crate makes no guarantees for security in this regard so make sure you trust your sources.\nVerification of all `.ent` files is strongly recommended.", config);
41
42            loop {
43                print!("Do you wish to proceed? [y/n]: ");
44                std::io::stdout().flush().unwrap();
45
46                let mut decision = String::new();
47                std::io::stdin().read_line(&mut decision).unwrap();
48
49                match decision.trim().to_lowercase().as_str() {
50                    "y" | "Y" | "yes" => break,
51                    "n" | "N" | "no" => {
52                        return Err(nom::Err::Error(
53                            "User decided to stop due to potential XXE attack",
54                        )
55                        .into());
56                    }
57                    _ => eprintln!("Invalid input. Please type 'y' or 'n'"),
58                }
59            }
60        }
61        Config {
62            external_parse_config:
63                ExternalEntityParseConfig {
64                    allow_ext_parse: false,
65                    ignore_ext_parse_warning: true,
66                    ..
67                },
68        } => {
69            warnln!("The configuration `{:?}` may allow for unexpected parsing if `allow_ext_parse` is changed to true in the future", config);
70        }
71        _ => (),
72    }
73    Ok(())
74}