Vulnerability: Path traversal in mozwire An issue was discovered in the mozwire crate through 2020-08-18 for Rust. A ../ directory-traversal situation allows overwriting local files that have .conf at the end of the filename.
Vulnerability ID: GHSA-4vhw-4rw7-jfpv
CWE: CWE-22
Severity score: 9.1
Project: https://github.com/NilsIrl/MozWire
Published: 2021-08-25
File: src/main.rs
Commit message: Validate server hostnames to prevent path traversal

If Mozilla servers were compromised, hostnames could be used for path
traversal attacks. The impact would be very low as it would only be
possible to write wireguard configs.

Fix #14
Commit author: Nils

Vulnerable version (before fix):
 struct Server {     port_ranges: Vec<(u16, u16)>, }  // latitude and longitude omitted #[derive(serde::Deserialize)] struct City { struct ServerList {  impl ServerList {     fn new(client: reqwest::blocking::Client, token: &str) -> Self {         client             .get(&format!("{}/vpn/servers", BASE_URL))             .bearer_auth(token)             .send()             .unwrap()             .json::<ServerList>()             .unwrap()     } } 

Fixed version (after fix):
 struct Server {     port_ranges: Vec<(u16, u16)>, }  impl Server {     fn validate_hostname(&self) -> bool {         self.hostname             .chars()             .all(|c| c.is_ascii_alphanumeric() || c == '-')     } }  // latitude and longitude omitted #[derive(serde::Deserialize)] struct City { struct ServerList {  impl ServerList {     fn new(client: reqwest::blocking::Client, token: &str) -> Self {         let server_list = client             .get(&format!("{}/vpn/servers", BASE_URL))             .bearer_auth(token)             .send()             .unwrap()             .json::<ServerList>()             .unwrap();         if let Some(server) = server_list             .countries             .iter()             .flat_map(|country| country.cities.iter().flat_map(|city| city.servers.iter()))             .find(|server| !server.validate_hostname())         {             eprintln!(                 "A server contains invalid characters in its hostname: {}",                 server.hostname             );             std::process::exit(3);         }         server_list     } } 

Patch:
patch: @@ -106,6 +106,14 @@ struct Server {
     port_ranges: Vec<(u16, u16)>,
 }
 
+impl Server {
+    fn validate_hostname(&self) -> bool {
+        self.hostname
+            .chars()
+            .all(|c| c.is_ascii_alphanumeric() || c == '-')
+    }
+}
+
 // latitude and longitude omitted
 #[derive(serde::Deserialize)]
 struct City {
@@ -130,13 +138,26 @@ struct ServerList {
 
 impl ServerList {
     fn new(client: reqwest::blocking::Client, token: &str) -> Self {
-        client
+        let server_list = client
             .get(&format!("{}/vpn/servers", BASE_URL))
             .bearer_auth(token)
             .send()
             .unwrap()
             .json::<ServerList>()
-            .unwrap()
+            .unwrap();
+        if let Some(server) = server_list
+            .countries
+            .iter()
+            .flat_map(|country| country.cities.iter().flat_map(|city| city.servers.iter()))
+            .find(|server| !server.validate_hostname())
+        {
+            eprintln!(
+                "A server contains invalid characters in its hostname: {}",
+                server.hostname
+            );
+            std::process::exit(3);
+        }
+        server_list
     }
 }
path: src/main.rs