summaryrefslogtreecommitdiffstats
path: root/browser/app/nmhproxy/src
diff options
context:
space:
mode:
Diffstat (limited to 'browser/app/nmhproxy/src')
-rw-r--r--browser/app/nmhproxy/src/commands.rs162
-rw-r--r--browser/app/nmhproxy/src/main.rs9
2 files changed, 168 insertions, 3 deletions
diff --git a/browser/app/nmhproxy/src/commands.rs b/browser/app/nmhproxy/src/commands.rs
index 29c86a0dd7..b26180e8f8 100644
--- a/browser/app/nmhproxy/src/commands.rs
+++ b/browser/app/nmhproxy/src/commands.rs
@@ -4,6 +4,7 @@
use serde::{Deserialize, Serialize};
use std::io::{self, Read, Write};
+use std::path::PathBuf;
use std::process::Command;
use url::Url;
@@ -23,6 +24,7 @@ pub enum FirefoxCommand {
LaunchFirefox { url: String },
LaunchFirefoxPrivate { url: String },
GetVersion {},
+ GetInstallId {},
}
#[derive(Serialize, Deserialize)]
// {
@@ -34,6 +36,14 @@ pub struct Response {
pub result_code: u32,
}
+#[derive(Serialize, Deserialize)]
+// {
+// "installation_id": "123ABC456",
+// }
+pub struct InstallationId {
+ pub installation_id: String,
+}
+
#[repr(u32)]
pub enum ResultCode {
Success = 0,
@@ -152,6 +162,28 @@ pub fn process_command(command: &FirefoxCommand) -> std::io::Result<bool> {
Ok(true)
}
FirefoxCommand::GetVersion {} => generate_response("1", ResultCode::Success.into()),
+ FirefoxCommand::GetInstallId {} => {
+ // config_dir() evaluates to ~/Library/Application Support on macOS
+ // and %RoamingAppData% on Windows.
+ let mut json_path = match dirs::config_dir() {
+ Some(path) => path,
+ None => {
+ return generate_response(
+ "Config dir could not be found",
+ ResultCode::Error.into(),
+ )
+ }
+ };
+ #[cfg(target_os = "windows")]
+ json_path.push("Mozilla\\Firefox");
+ #[cfg(target_os = "macos")]
+ json_path.push("Firefox");
+
+ json_path.push("install_id");
+ json_path.set_extension("json");
+ let mut install_id = String::new();
+ get_install_id(&mut json_path, &mut install_id)
+ }
}
}
@@ -228,10 +260,54 @@ fn launch_firefox<C: CommandRunner>(
command.to_string()
}
+fn get_install_id(json_path: &mut PathBuf, install_id: &mut String) -> std::io::Result<bool> {
+ if !json_path.exists() {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ "Install ID file does not exist",
+ ));
+ }
+ let json_size = std::fs::metadata(&json_path)
+ .map_err(|e| std::io::Error::new(std::io::ErrorKind::NotFound, e))?
+ .len();
+ // Set a 1 KB limit for the file size.
+ if json_size <= 0 || json_size > 1024 {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::InvalidData,
+ "Install ID file has invalid size",
+ ));
+ }
+ let mut file =
+ std::fs::File::open(json_path).or_else(|_| -> std::io::Result<std::fs::File> {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::NotFound,
+ "Failed to open file",
+ ));
+ })?;
+ let mut contents = String::new();
+ match file.read_to_string(&mut contents) {
+ Ok(_) => match serde_json::from_str::<InstallationId>(&contents) {
+ Ok(id) => {
+ *install_id = id.installation_id.clone();
+ generate_response(&id.installation_id, ResultCode::Success.into())
+ }
+ Err(_) => {
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::InvalidData,
+ "Failed to read installation ID",
+ ))
+ }
+ },
+ Err(_) => generate_response("Failed to read file", ResultCode::Error.into()),
+ }?;
+ Ok(true)
+}
+
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
+ use tempfile::NamedTempFile;
#[test]
fn test_validate_url() {
let valid_test_cases = vec![
@@ -347,4 +423,90 @@ mod tests {
let correct_url_format = format!("-osint -private-window {}", url);
assert!(command_line.contains(correct_url_format.as_str()));
}
+
+ #[test]
+ fn test_get_install_id_valid() -> std::io::Result<()> {
+ let mut tempfile = NamedTempFile::new().unwrap();
+ let installation_id = InstallationId {
+ installation_id: "123ABC456".to_string(),
+ };
+ let json_string = serde_json::to_string(&installation_id);
+ let _ = tempfile.write_all(json_string?.as_bytes());
+ let mut install_id = String::new();
+ let result = get_install_id(&mut tempfile.path().to_path_buf(), &mut install_id);
+ assert!(result.is_ok());
+ assert_eq!(install_id, "123ABC456");
+ Ok(())
+ }
+
+ #[test]
+ fn test_get_install_id_incorrect_var() -> std::io::Result<()> {
+ #[derive(Serialize, Deserialize)]
+ pub struct IncorrectJSON {
+ pub incorrect_var: String,
+ }
+ let mut tempfile = NamedTempFile::new().unwrap();
+ let incorrect_json = IncorrectJSON {
+ incorrect_var: "incorrect_val".to_string(),
+ };
+ let json_string = serde_json::to_string(&incorrect_json);
+ let _ = tempfile.write_all(json_string?.as_bytes());
+ let mut install_id = String::new();
+ let result = get_install_id(&mut tempfile.path().to_path_buf(), &mut install_id);
+ assert!(result.is_err());
+ let error = result.err().unwrap();
+ assert_eq!(error.kind(), std::io::ErrorKind::InvalidData);
+ Ok(())
+ }
+
+ #[test]
+ fn test_get_install_id_partially_correct_vars() -> std::io::Result<()> {
+ #[derive(Serialize, Deserialize)]
+ pub struct IncorrectJSON {
+ pub installation_id: String,
+ pub incorrect_var: String,
+ }
+ let mut tempfile = NamedTempFile::new().unwrap();
+ let incorrect_json = IncorrectJSON {
+ installation_id: "123ABC456".to_string(),
+ incorrect_var: "incorrect_val".to_string(),
+ };
+ let json_string = serde_json::to_string(&incorrect_json);
+ let _ = tempfile.write_all(json_string?.as_bytes());
+ let mut install_id = String::new();
+ let result = get_install_id(&mut tempfile.path().to_path_buf(), &mut install_id);
+ // This still succeeds as the installation_id field is present
+ assert!(result.is_ok());
+ Ok(())
+ }
+
+ #[test]
+ fn test_get_install_id_file_does_not_exist() -> std::io::Result<()> {
+ let tempfile = NamedTempFile::new().unwrap();
+ let mut path = tempfile.path().to_path_buf();
+ tempfile.close()?;
+ let mut install_id = String::new();
+ let result = get_install_id(&mut path, &mut install_id);
+ assert!(result.is_err());
+ let error = result.err().unwrap();
+ assert_eq!(error.kind(), std::io::ErrorKind::NotFound);
+ Ok(())
+ }
+
+ #[test]
+ fn test_get_install_id_file_too_large() -> std::io::Result<()> {
+ let mut tempfile = NamedTempFile::new().unwrap();
+ let installation_id = InstallationId {
+ // Create a ~10 KB file
+ installation_id: String::from_utf8(vec![b'X'; 10000]).unwrap(),
+ };
+ let json_string = serde_json::to_string(&installation_id);
+ let _ = tempfile.write_all(json_string?.as_bytes());
+ let mut install_id = String::new();
+ let result = get_install_id(&mut tempfile.path().to_path_buf(), &mut install_id);
+ assert!(result.is_err());
+ let error = result.err().unwrap();
+ assert_eq!(error.kind(), std::io::ErrorKind::InvalidData);
+ Ok(())
+ }
}
diff --git a/browser/app/nmhproxy/src/main.rs b/browser/app/nmhproxy/src/main.rs
index de9cd8c2a3..02351eb0f1 100644
--- a/browser/app/nmhproxy/src/main.rs
+++ b/browser/app/nmhproxy/src/main.rs
@@ -43,9 +43,12 @@ fn main() -> Result<(), Error> {
"Failed to deserialize message JSON",
));
})?;
- commands::process_command(&native_messaging_json).or_else(|_| -> Result<bool, _> {
- commands::generate_response("Failed to process command", ResultCode::Error.into())
- .expect("JSON error");
+ commands::process_command(&native_messaging_json).or_else(|e| -> Result<bool, _> {
+ commands::generate_response(
+ format!("Failed to process command: {}", e).as_str(),
+ ResultCode::Error.into(),
+ )
+ .expect("JSON error");
return Err(Error::new(
ErrorKind::InvalidInput,
"Failed to process command",