tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

command.rs (11374B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 use crate::logging;
      6 use base64::prelude::BASE64_STANDARD;
      7 use base64::Engine;
      8 use hyper::Method;
      9 use serde::de::{self, Deserialize, Deserializer};
     10 use serde_json::Value;
     11 use webdriver::command::{WebDriverCommand, WebDriverExtensionCommand};
     12 use webdriver::error::WebDriverResult;
     13 use webdriver::httpapi::WebDriverExtensionRoute;
     14 use webdriver::Parameters;
     15 
     16 pub fn extension_routes() -> Vec<(Method, &'static str, GeckoExtensionRoute)> {
     17    vec![
     18        (
     19            Method::GET,
     20            "/session/{sessionId}/moz/context",
     21            GeckoExtensionRoute::GetContext,
     22        ),
     23        (
     24            Method::POST,
     25            "/session/{sessionId}/moz/context",
     26            GeckoExtensionRoute::SetContext,
     27        ),
     28        (
     29            Method::POST,
     30            "/session/{sessionId}/moz/addon/install",
     31            GeckoExtensionRoute::InstallAddon,
     32        ),
     33        (
     34            Method::POST,
     35            "/session/{sessionId}/moz/addon/uninstall",
     36            GeckoExtensionRoute::UninstallAddon,
     37        ),
     38        (
     39            Method::GET,
     40            "/session/{sessionId}/moz/screenshot/full",
     41            GeckoExtensionRoute::TakeFullScreenshot,
     42        ),
     43    ]
     44 }
     45 
     46 #[derive(Clone, PartialEq, Eq)]
     47 pub enum GeckoExtensionRoute {
     48    GetContext,
     49    SetContext,
     50    InstallAddon,
     51    UninstallAddon,
     52    TakeFullScreenshot,
     53 }
     54 
     55 impl WebDriverExtensionRoute for GeckoExtensionRoute {
     56    type Command = GeckoExtensionCommand;
     57 
     58    fn command(
     59        &self,
     60        _params: &Parameters,
     61        body_data: &Value,
     62    ) -> WebDriverResult<WebDriverCommand<GeckoExtensionCommand>> {
     63        use self::GeckoExtensionRoute::*;
     64 
     65        let command = match *self {
     66            GetContext => GeckoExtensionCommand::GetContext,
     67            SetContext => {
     68                GeckoExtensionCommand::SetContext(serde_json::from_value(body_data.clone())?)
     69            }
     70            InstallAddon => {
     71                GeckoExtensionCommand::InstallAddon(serde_json::from_value(body_data.clone())?)
     72            }
     73            UninstallAddon => {
     74                GeckoExtensionCommand::UninstallAddon(serde_json::from_value(body_data.clone())?)
     75            }
     76            TakeFullScreenshot => GeckoExtensionCommand::TakeFullScreenshot,
     77        };
     78 
     79        Ok(WebDriverCommand::Extension(command))
     80    }
     81 }
     82 
     83 #[derive(Clone)]
     84 pub enum GeckoExtensionCommand {
     85    GetContext,
     86    SetContext(GeckoContextParameters),
     87    InstallAddon(AddonInstallParameters),
     88    UninstallAddon(AddonUninstallParameters),
     89    TakeFullScreenshot,
     90 }
     91 
     92 impl WebDriverExtensionCommand for GeckoExtensionCommand {
     93    fn parameters_json(&self) -> Option<Value> {
     94        use self::GeckoExtensionCommand::*;
     95        match self {
     96            GetContext => None,
     97            InstallAddon(x) => Some(serde_json::to_value(x).unwrap()),
     98            SetContext(x) => Some(serde_json::to_value(x).unwrap()),
     99            UninstallAddon(x) => Some(serde_json::to_value(x).unwrap()),
    100            TakeFullScreenshot => None,
    101        }
    102    }
    103 }
    104 
    105 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
    106 #[serde(deny_unknown_fields)]
    107 pub struct AddonBase64 {
    108    #[serde(deserialize_with = "deserialize_base64")]
    109    pub addon: Vec<u8>,
    110    pub temporary: Option<bool>,
    111    #[serde(rename = "allowPrivateBrowsing")]
    112    pub allow_private_browsing: Option<bool>,
    113 }
    114 
    115 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
    116 #[serde(deny_unknown_fields)]
    117 pub struct AddonPath {
    118    pub path: String,
    119    pub temporary: Option<bool>,
    120    #[serde(rename = "allowPrivateBrowsing")]
    121    pub allow_private_browsing: Option<bool>,
    122 }
    123 
    124 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
    125 #[serde(untagged)]
    126 pub enum AddonInstallParameters {
    127    AddonBase64(AddonBase64),
    128    AddonPath(AddonPath),
    129 }
    130 
    131 fn deserialize_base64<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
    132 where
    133    D: Deserializer<'de>,
    134 {
    135    let encoded_str = String::deserialize(deserializer)?;
    136    let decoded_str = BASE64_STANDARD
    137        .decode(encoded_str)
    138        .map_err(de::Error::custom)?;
    139 
    140    Ok(decoded_str.clone())
    141 }
    142 
    143 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
    144 pub struct AddonUninstallParameters {
    145    pub id: String,
    146 }
    147 
    148 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
    149 #[serde(rename_all = "lowercase")]
    150 pub enum GeckoContext {
    151    Content,
    152    Chrome,
    153 }
    154 
    155 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
    156 pub struct GeckoContextParameters {
    157    pub context: GeckoContext,
    158 }
    159 
    160 #[derive(Default, Debug, PartialEq, Eq)]
    161 pub struct LogOptions {
    162    pub level: Option<logging::Level>,
    163 }
    164 
    165 #[cfg(test)]
    166 mod tests {
    167    use serde_json::json;
    168 
    169    use super::*;
    170    use crate::test::assert_de;
    171 
    172    #[test]
    173    fn test_json_addon_install_parameters_invalid() {
    174        assert!(serde_json::from_str::<AddonInstallParameters>("").is_err());
    175        assert!(serde_json::from_value::<AddonInstallParameters>(json!(null)).is_err());
    176        assert!(serde_json::from_value::<AddonInstallParameters>(json!({})).is_err());
    177    }
    178 
    179    #[test]
    180    fn test_json_addon_install_parameters_with_path_and_temporary() {
    181        let params = AddonPath {
    182            path: "/path/to.xpi".to_string(),
    183            temporary: Some(true),
    184            allow_private_browsing: None,
    185        };
    186        assert_de(
    187            &AddonInstallParameters::AddonPath(params),
    188            json!({"path": "/path/to.xpi", "temporary": true}),
    189        );
    190    }
    191 
    192    #[test]
    193    fn test_json_addon_install_parameters_with_path() {
    194        let params = AddonPath {
    195            path: "/path/to.xpi".to_string(),
    196            temporary: None,
    197            allow_private_browsing: None,
    198        };
    199        assert_de(
    200            &AddonInstallParameters::AddonPath(params),
    201            json!({"path": "/path/to.xpi"}),
    202        );
    203    }
    204 
    205    #[test]
    206    fn test_json_addon_install_parameters_with_path_and_allow_private_browsing() {
    207        let params = AddonPath {
    208            path: "/path/to.xpi".to_string(),
    209            temporary: None,
    210            allow_private_browsing: Some(true),
    211        };
    212        assert_de(
    213            &AddonInstallParameters::AddonPath(params),
    214            json!({"path": "/path/to.xpi", "allowPrivateBrowsing": true}),
    215        );
    216    }
    217 
    218    #[test]
    219    fn test_json_addon_install_parameters_with_path_invalid_type() {
    220        let json = json!({"path": true, "temporary": true});
    221        assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err());
    222    }
    223 
    224    #[test]
    225    fn test_json_addon_install_parameters_with_path_and_allow_private_browsing_invalid_type() {
    226        let json = json!({"path": "/path/to.xpi", "allowPrivateBrowsing": "foo"});
    227        assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err());
    228    }
    229 
    230    #[test]
    231    fn test_json_addon_install_parameters_with_path_and_temporary_invalid_type() {
    232        let json = json!({"path": "/path/to.xpi", "temporary": "foo"});
    233        assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err());
    234    }
    235 
    236    #[test]
    237    fn test_json_addon_install_parameters_with_addon() {
    238        let json = json!({"addon": "aGVsbG8=", "temporary": true});
    239        let data = serde_json::from_value::<AddonInstallParameters>(json).unwrap();
    240 
    241        if let AddonInstallParameters::AddonBase64(data) = data {
    242            assert_eq!(data.temporary, Some(true));
    243            assert_eq!(String::from_utf8(data.addon).unwrap(), "hello");
    244        }
    245    }
    246 
    247    #[test]
    248    fn test_json_addon_install_parameters_with_addon_and_allow_private_browsing() {
    249        let json = json!({"addon": "aGVsbG8=", "allowPrivateBrowsing": true});
    250        let data = serde_json::from_value::<AddonInstallParameters>(json).unwrap();
    251 
    252        if let AddonInstallParameters::AddonBase64(data) = data {
    253            assert_eq!(data.allow_private_browsing, Some(true));
    254            assert_eq!(String::from_utf8(data.addon).unwrap(), "hello");
    255        }
    256    }
    257 
    258    #[test]
    259    fn test_json_addon_install_parameters_with_addon_only() {
    260        let json = json!({"addon": "aGVsbG8="});
    261        let data = serde_json::from_value::<AddonInstallParameters>(json).unwrap();
    262 
    263        if let AddonInstallParameters::AddonBase64(data) = data {
    264            assert_eq!(data.temporary, None);
    265            assert_eq!(String::from_utf8(data.addon).unwrap(), "hello");
    266        }
    267    }
    268 
    269    #[test]
    270    fn test_json_addon_install_parameters_with_addon_invalid_type() {
    271        let json = json!({"addon": true, "temporary": true});
    272        assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err());
    273    }
    274 
    275    #[test]
    276    fn test_json_addon_install_parameters_with_addon_and_temporary_invalid_type() {
    277        let json = json!({"addon": "aGVsbG8=", "temporary": "foo"});
    278        assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err());
    279    }
    280 
    281    #[test]
    282    fn test_json_addon_install_parameters_with_addon_and_allow_private_browsing_invalid_type() {
    283        let json = json!({"addon": "aGVsbG8=", "allowPrivateBrowsing": "foo"});
    284        assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err());
    285    }
    286 
    287    #[test]
    288    fn test_json_install_parameters_with_temporary_only() {
    289        let json = json!({"temporary": true});
    290        assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err());
    291    }
    292 
    293    #[test]
    294    fn test_json_addon_install_parameters_with_both_path_and_addon() {
    295        let json = json!({
    296            "path": "/path/to.xpi",
    297            "addon": "aGVsbG8=",
    298            "temporary": true,
    299        });
    300        assert!(serde_json::from_value::<AddonInstallParameters>(json).is_err());
    301    }
    302 
    303    #[test]
    304    fn test_json_addon_uninstall_parameters_invalid() {
    305        assert!(serde_json::from_str::<AddonUninstallParameters>("").is_err());
    306        assert!(serde_json::from_value::<AddonUninstallParameters>(json!(null)).is_err());
    307        assert!(serde_json::from_value::<AddonUninstallParameters>(json!({})).is_err());
    308    }
    309 
    310    #[test]
    311    fn test_json_addon_uninstall_parameters() {
    312        let params = AddonUninstallParameters {
    313            id: "foo".to_string(),
    314        };
    315        assert_de(&params, json!({"id": "foo"}));
    316    }
    317 
    318    #[test]
    319    fn test_json_addon_uninstall_parameters_id_invalid_type() {
    320        let json = json!({"id": true});
    321        assert!(serde_json::from_value::<AddonUninstallParameters>(json).is_err());
    322    }
    323 
    324    #[test]
    325    fn test_json_gecko_context_parameters_content() {
    326        let params = GeckoContextParameters {
    327            context: GeckoContext::Content,
    328        };
    329        assert_de(&params, json!({"context": "content"}));
    330    }
    331 
    332    #[test]
    333    fn test_json_gecko_context_parameters_chrome() {
    334        let params = GeckoContextParameters {
    335            context: GeckoContext::Chrome,
    336        };
    337        assert_de(&params, json!({"context": "chrome"}));
    338    }
    339 
    340    #[test]
    341    fn test_json_gecko_context_parameters_context_invalid() {
    342        type P = GeckoContextParameters;
    343        assert!(serde_json::from_value::<P>(json!({})).is_err());
    344        assert!(serde_json::from_value::<P>(json!({ "context": null })).is_err());
    345        assert!(serde_json::from_value::<P>(json!({"context": "foo"})).is_err());
    346    }
    347 }