diff --git a/src/tests/cli.rs b/src/tests/cli.rs index a376499..9e9527b 100644 --- a/src/tests/cli.rs +++ b/src/tests/cli.rs @@ -2,8 +2,15 @@ use assert_cmd::prelude::*; use std::env; use std::process::Command; +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + #[test] -fn print_version() -> Result<(), Box> { +fn passing_print_version() -> Result<(), Box> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let out = cmd.arg("-V").output().unwrap(); @@ -23,7 +30,7 @@ fn print_version() -> Result<(), Box> { } #[test] -fn bad_input_empty_target() -> Result<(), Box> { +fn passing_bad_input_empty_target() -> Result<(), Box> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let out = cmd.arg("").output().unwrap(); @@ -43,7 +50,7 @@ fn bad_input_empty_target() -> Result<(), Box> { } #[test] -fn bad_input_data_url() -> Result<(), Box> { +fn passing_bad_input_data_url() -> Result<(), Box> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let out = cmd.arg("data:,Hello%2C%20World!").output().unwrap(); @@ -63,7 +70,7 @@ fn bad_input_data_url() -> Result<(), Box> { } #[test] -fn isolate_data_url() -> Result<(), Box> { +fn passing_isolate_data_url() -> Result<(), Box> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let out = cmd .arg("-I") @@ -89,7 +96,7 @@ fn isolate_data_url() -> Result<(), Box> { } #[test] -fn remove_css_from_data_url() -> Result<(), Box> { +fn passing_remove_css_from_data_url() -> Result<(), Box> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let out = cmd .arg("-c") @@ -116,7 +123,7 @@ fn remove_css_from_data_url() -> Result<(), Box> { } #[test] -fn remove_frames_from_data_url() -> Result<(), Box> { +fn passing_remove_frames_from_data_url() -> Result<(), Box> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let out = cmd .arg("-f") @@ -142,7 +149,7 @@ fn remove_frames_from_data_url() -> Result<(), Box> { } #[test] -fn remove_images_from_data_url() -> Result<(), Box> { +fn passing_remove_images_from_data_url() -> Result<(), Box> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let out = cmd .arg("-i") @@ -174,7 +181,7 @@ Hi\ } #[test] -fn remove_js_from_data_url() -> Result<(), Box> { +fn passing_remove_js_from_data_url() -> Result<(), Box> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let out = cmd .arg("-j") @@ -203,7 +210,7 @@ fn remove_js_from_data_url() -> Result<(), Box> { } #[test] -fn local_file_target_input() -> Result<(), Box> { +fn passing_local_file_target_input() -> Result<(), Box> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let cwd_normalized: String = str!(env::current_dir().unwrap().to_str().unwrap()).replace("\\", "/"); @@ -251,7 +258,8 @@ fn local_file_target_input() -> Result<(), Box> { } #[test] -fn local_file_target_input_absolute_target_path() -> Result<(), Box> { +fn passing_local_file_target_input_absolute_target_path() -> Result<(), Box> +{ let cwd = env::current_dir().unwrap(); let cwd_normalized: String = str!(env::current_dir().unwrap().to_str().unwrap()).replace("\\", "/"); @@ -306,7 +314,7 @@ fn local_file_target_input_absolute_target_path() -> Result<(), Box Result<(), Box> { +fn passing_local_file_url_target_input() -> Result<(), Box> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let cwd = env::current_dir().unwrap(); let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" }; @@ -369,8 +377,8 @@ fn local_file_url_target_input() -> Result<(), Box> { } #[test] -fn security_disallow_local_assets_within_data_url_targets() -> Result<(), Box> -{ +fn passing_security_disallow_local_assets_within_data_url_targets( +) -> Result<(), Box> { let mut cmd = Command::cargo_bin(env!("CARGO_PKG_NAME"))?; let out = cmd .arg("data:text/html,%3Cscript%20src=\"src/tests/data/local-script.js\"%3E%3C/script%3E") diff --git a/src/tests/html/get_node_name.rs b/src/tests/html/get_node_name.rs new file mode 100644 index 0000000..2fbdc35 --- /dev/null +++ b/src/tests/html/get_node_name.rs @@ -0,0 +1,49 @@ +use crate::html; +use html5ever::rcdom::{Handle, NodeData}; + +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn get_node_name() { + let html = "

"; + let dom = html::html_to_dom(&html); + let mut count = 0; + + fn test_walk(node: &Handle, i: &mut i8) { + *i += 1; + + match &node.data { + NodeData::Document => { + for child in node.children.borrow().iter() { + test_walk(child, &mut *i); + } + } + NodeData::Element { ref name, .. } => { + let node_name = name.local.as_ref().to_string(); + let parent = html::get_parent_node(node); + let parent_node_name = html::get_node_name(&parent); + if node_name == "head" || node_name == "body" { + assert_eq!(parent_node_name, "html"); + } else if node_name == "div" { + assert_eq!(parent_node_name, "body"); + } else if node_name == "p" { + assert_eq!(parent_node_name, "div"); + } + + for child in node.children.borrow().iter() { + test_walk(child, &mut *i); + } + } + _ => (), + }; + } + + test_walk(&dom.document, &mut count); + + assert_eq!(count, 7); +} diff --git a/src/tests/html/is_icon.rs b/src/tests/html/is_icon.rs new file mode 100644 index 0000000..334cec4 --- /dev/null +++ b/src/tests/html/is_icon.rs @@ -0,0 +1,50 @@ +use crate::html; + +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn passing_icon() { + assert!(html::is_icon("icon")); +} + +#[test] +fn passing_shortcut_icon_capitalized() { + assert!(html::is_icon("Shortcut Icon")); +} + +#[test] +fn passing_icon_uppercase() { + assert!(html::is_icon("ICON")); +} + +#[test] +fn passing_mask_icon() { + assert!(html::is_icon("mask-icon")); +} + +#[test] +fn passing_fluid_icon() { + assert!(html::is_icon("fluid-icon")); +} + +// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗ +// ██╔════╝██╔══██╗██║██║ ██║████╗ ██║██╔════╝ +// █████╗ ███████║██║██║ ██║██╔██╗ ██║██║ ███╗ +// ██╔══╝ ██╔══██║██║██║ ██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn failing_stylesheet() { + assert!(!html::is_icon("stylesheet")); +} + +#[test] +fn failing_empty_string() { + assert!(!html::is_icon("")); +} diff --git a/src/tests/html/mod.rs b/src/tests/html/mod.rs new file mode 100644 index 0000000..bacc18a --- /dev/null +++ b/src/tests/html/mod.rs @@ -0,0 +1,4 @@ +mod get_node_name; +mod is_icon; +mod stringify_document; +mod walk_and_embed_assets; diff --git a/src/tests/html/stringify_document.rs b/src/tests/html/stringify_document.rs new file mode 100644 index 0000000..2089996 --- /dev/null +++ b/src/tests/html/stringify_document.rs @@ -0,0 +1,188 @@ +use crate::html; + +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn passing_div_as_root_element() { + let html = "
"; + let dom = html::html_to_dom(&html); + + let opt_no_css: bool = false; + let opt_no_frames: bool = false; + let opt_no_js: bool = false; + let opt_no_images: bool = false; + let opt_isolate: bool = false; + + assert_eq!( + html::stringify_document( + &dom.document, + opt_no_css, + opt_no_frames, + opt_no_js, + opt_no_images, + opt_isolate, + ), + "
" + ); +} + +#[test] +fn passing_full_page_with_no_html_head_or_body() { + let html = "Isolated document\ + \ + \ +
"; + let dom = html::html_to_dom(&html); + + let opt_no_css: bool = false; + let opt_no_frames: bool = false; + let opt_no_js: bool = false; + let opt_no_images: bool = false; + let opt_isolate: bool = true; + + assert_eq!( + html::stringify_document( + &dom.document, + opt_no_css, + opt_no_frames, + opt_no_js, + opt_no_images, + opt_isolate, + ), + "\ + \ + \ + Isolated document\ + \ + \ + \ + \ +
\ + \ +
\ + \ + " + ); +} + +#[test] +fn passing_doctype_and_the_rest_no_html_head_or_body() { + let html = "\ + Unstyled document\ + \ +
"; + let dom = html::html_to_dom(&html); + + let opt_no_css: bool = true; + let opt_no_frames: bool = false; + let opt_no_js: bool = false; + let opt_no_images: bool = false; + let opt_isolate: bool = false; + + assert_eq!( + html::stringify_document( + &dom.document, + opt_no_css, + opt_no_frames, + opt_no_js, + opt_no_images, + opt_isolate, + ), + "\ + \ + \ + \ + Unstyled document\ + \ + \ +
\ + " + ); +} + +#[test] +fn passing_doctype_and_the_rest_no_html_head_or_body_forbid_frames() { + let html = "\ + Frameless document\ + \ +
"; + let dom = html::html_to_dom(&html); + + let opt_no_css: bool = false; + let opt_no_frames: bool = true; + let opt_no_js: bool = false; + let opt_no_images: bool = false; + let opt_isolate: bool = false; + + assert_eq!( + html::stringify_document( + &dom.document, + opt_no_css, + opt_no_frames, + opt_no_js, + opt_no_images, + opt_isolate, + ), + "\ + \ + \ + \ + Frameless document\ + \ + \ +
\ + " + ); +} + +#[test] +fn passing_doctype_and_the_rest_all_forbidden() { + let html = "\ + no-frame no-css no-js no-image isolated document\ + \ + \ +
\ + \ + \ + \ +
"; + let dom = html::html_to_dom(&html); + + let opt_isolate: bool = true; + let opt_no_css: bool = true; + let opt_no_frames: bool = true; + let opt_no_js: bool = true; + let opt_no_images: bool = true; + + assert_eq!( + html::stringify_document( + &dom.document, + opt_no_css, + opt_no_frames, + opt_no_js, + opt_no_images, + opt_isolate, + ), + "\ + \ + \ + \ + no-frame no-css no-js no-image isolated document\ + \ + \ + \ + \ +
\ + \ + \ + \ +
\ + \ + " + ); +} diff --git a/src/tests/html.rs b/src/tests/html/walk_and_embed_assets.rs similarity index 54% rename from src/tests/html.rs rename to src/tests/html/walk_and_embed_assets.rs index fe68833..716e6c8 100644 --- a/src/tests/html.rs +++ b/src/tests/html/walk_and_embed_assets.rs @@ -1,64 +1,17 @@ use crate::html; -use html5ever::rcdom::{Handle, NodeData}; use html5ever::serialize::{serialize, SerializeOpts}; use reqwest::blocking::Client; use std::collections::HashMap; -#[test] -fn is_icon() { - assert_eq!(html::is_icon("icon"), true); - assert_eq!(html::is_icon("Shortcut Icon"), true); - assert_eq!(html::is_icon("ICON"), true); - assert_eq!(html::is_icon("mask-icon"), true); - assert_eq!(html::is_icon("fluid-icon"), true); - assert_eq!(html::is_icon("stylesheet"), false); - assert_eq!(html::is_icon(""), false); -} +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ #[test] -fn get_parent_node_get_node_name() { - let html = "

"; - let dom = html::html_to_dom(&html); - let mut count = 0; - - fn test_walk(node: &Handle, i: &mut i8) { - *i += 1; - - match &node.data { - NodeData::Document => { - for child in node.children.borrow().iter() { - test_walk(child, &mut *i); - } - } - NodeData::Element { ref name, .. } => { - let node_name = name.local.as_ref().to_string(); - let parent = html::get_parent_node(node); - let parent_node_name = html::get_node_name(&parent); - if node_name == "head" || node_name == "body" { - assert_eq!(parent_node_name, "html"); - } else if node_name == "div" { - assert_eq!(parent_node_name, "body"); - } else if node_name == "p" { - assert_eq!(parent_node_name, "div"); - } - - println!("{}", node_name); - - for child in node.children.borrow().iter() { - test_walk(child, &mut *i); - } - } - _ => (), - }; - } - - test_walk(&dom.document, &mut count); - - assert_eq!(count, 7); -} - -#[test] -fn walk_and_embed_assets() { +fn passing_basic() { let cache = &mut HashMap::new(); let html = "

"; @@ -95,7 +48,7 @@ fn walk_and_embed_assets() { } #[test] -fn walk_and_embed_assets_ensure_no_recursive_iframe() { +fn passing_ensure_no_recursive_iframe() { let html = "

"; let dom = html::html_to_dom(&html); let url = "http://localhost"; @@ -131,7 +84,7 @@ fn walk_and_embed_assets_ensure_no_recursive_iframe() { } #[test] -fn walk_and_embed_assets_ensure_no_recursive_frame() { +fn passing_ensure_no_recursive_frame() { let html = ""; let dom = html::html_to_dom(&html); let url = "http://localhost"; @@ -167,7 +120,7 @@ fn walk_and_embed_assets_ensure_no_recursive_frame() { } #[test] -fn walk_and_embed_assets_no_css() { +fn passing_no_css() { let html = "\ \
"; @@ -212,7 +165,7 @@ fn walk_and_embed_assets_no_css() { } #[test] -fn walk_and_embed_assets_no_images() { +fn passing_no_images() { let html = "\
"; let dom = html::html_to_dom(&html); @@ -260,7 +213,7 @@ fn walk_and_embed_assets_no_images() { } #[test] -fn walk_and_embed_assets_no_body_background_images() { +fn passing_no_body_background_images() { let html = ""; let dom = html::html_to_dom(&html); let url = "http://localhost"; @@ -296,7 +249,7 @@ fn walk_and_embed_assets_no_body_background_images() { } #[test] -fn walk_and_embed_assets_no_frames() { +fn passing_no_frames() { let html = ""; let dom = html::html_to_dom(&html); let url = "http://localhost"; @@ -331,7 +284,7 @@ fn walk_and_embed_assets_no_frames() { } #[test] -fn walk_and_embed_assets_no_iframes() { +fn passing_no_iframes() { let html = ""; let dom = html::html_to_dom(&html); let url = "http://localhost"; @@ -366,7 +319,7 @@ fn walk_and_embed_assets_no_iframes() { } #[test] -fn walk_and_embed_assets_no_js() { +fn passing_no_js() { let html = "
\ \ \ @@ -406,7 +359,7 @@ fn walk_and_embed_assets_no_js() { } #[test] -fn walk_and_embed_with_no_integrity() { +fn passing_with_no_integrity() { let html = "No integrity\ \ "; @@ -443,183 +396,3 @@ fn walk_and_embed_with_no_integrity() { " ); } - -#[test] -fn stringify_document() { - let html = "
"; - let dom = html::html_to_dom(&html); - - let opt_no_css: bool = false; - let opt_no_frames: bool = false; - let opt_no_js: bool = false; - let opt_no_images: bool = false; - let opt_isolate: bool = false; - - assert_eq!( - html::stringify_document( - &dom.document, - opt_no_css, - opt_no_frames, - opt_no_js, - opt_no_images, - opt_isolate, - ), - "
" - ); -} - -#[test] -fn stringify_document_isolate() { - let html = "Isolated document\ - \ - \ -
"; - let dom = html::html_to_dom(&html); - - let opt_no_css: bool = false; - let opt_no_frames: bool = false; - let opt_no_js: bool = false; - let opt_no_images: bool = false; - let opt_isolate: bool = true; - - assert_eq!( - html::stringify_document( - &dom.document, - opt_no_css, - opt_no_frames, - opt_no_js, - opt_no_images, - opt_isolate, - ), - "\ - \ - \ - Isolated document\ - \ - \ - \ - \ -
\ - \ -
\ - \ - " - ); -} - -#[test] -fn stringify_document_no_css() { - let html = "\ - Unstyled document\ - \ -
"; - let dom = html::html_to_dom(&html); - - let opt_no_css: bool = true; - let opt_no_frames: bool = false; - let opt_no_js: bool = false; - let opt_no_images: bool = false; - let opt_isolate: bool = false; - - assert_eq!( - html::stringify_document( - &dom.document, - opt_no_css, - opt_no_frames, - opt_no_js, - opt_no_images, - opt_isolate, - ), - "\ - \ - \ - \ - Unstyled document\ - \ - \ -
\ - " - ); -} - -#[test] -fn stringify_document_no_frames() { - let html = "\ - Frameless document\ - \ -
"; - let dom = html::html_to_dom(&html); - - let opt_no_css: bool = false; - let opt_no_frames: bool = true; - let opt_no_js: bool = false; - let opt_no_images: bool = false; - let opt_isolate: bool = false; - - assert_eq!( - html::stringify_document( - &dom.document, - opt_no_css, - opt_no_frames, - opt_no_js, - opt_no_images, - opt_isolate, - ), - "\ - \ - \ - \ - Frameless document\ - \ - \ -
\ - " - ); -} - -#[test] -fn stringify_document_isolate_no_frames_no_js_no_css_no_images() { - let html = "\ - no-frame no-css no-js no-image isolated document\ - \ - \ -
\ - \ - \ - \ -
"; - let dom = html::html_to_dom(&html); - - let opt_isolate: bool = true; - let opt_no_css: bool = true; - let opt_no_frames: bool = true; - let opt_no_js: bool = true; - let opt_no_images: bool = true; - - assert_eq!( - html::stringify_document( - &dom.document, - opt_no_css, - opt_no_frames, - opt_no_js, - opt_no_images, - opt_isolate, - ), - "\ - \ - \ - \ - no-frame no-css no-js no-image isolated document\ - \ - \ - \ - \ -
\ - \ - \ - \ -
\ - \ - " - ); -} diff --git a/src/tests/js.rs b/src/tests/js.rs deleted file mode 100644 index 63cf76d..0000000 --- a/src/tests/js.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::js::attr_is_event_handler; - -#[test] -fn attr_is_event_handler() { - // Passing - assert!(attr_is_event_handler("onBlur")); - assert!(attr_is_event_handler("onclick")); - assert!(attr_is_event_handler("onClick")); - - // Failing - assert!(!attr_is_event_handler("href")); - assert!(!attr_is_event_handler("")); - assert!(!attr_is_event_handler("class")); -} diff --git a/src/tests/js/attr_is_event_handler.rs b/src/tests/js/attr_is_event_handler.rs new file mode 100644 index 0000000..fbc2146 --- /dev/null +++ b/src/tests/js/attr_is_event_handler.rs @@ -0,0 +1,45 @@ +use crate::js; + +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn passing_onblur_camelcase() { + assert!(js::attr_is_event_handler("onBlur")); +} + +#[test] +fn passing_onclick_lowercase() { + assert!(js::attr_is_event_handler("onclick")); +} + +#[test] +fn passing_onclick_camelcase() { + assert!(js::attr_is_event_handler("onClick")); +} + +// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗ +// ██╔════╝██╔══██╗██║██║ ██║████╗ ██║██╔════╝ +// █████╗ ███████║██║██║ ██║██╔██╗ ██║██║ ███╗ +// ██╔══╝ ██╔══██║██║██║ ██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn failing_href() { + assert!(!js::attr_is_event_handler("href")); +} + +#[test] +fn failing_empty_string() { + assert!(!js::attr_is_event_handler("")); +} + +#[test] +fn failing_class() { + assert!(!js::attr_is_event_handler("class")); +} diff --git a/src/tests/js/mod.rs b/src/tests/js/mod.rs new file mode 100644 index 0000000..46bcf24 --- /dev/null +++ b/src/tests/js/mod.rs @@ -0,0 +1 @@ +mod attr_is_event_handler; diff --git a/src/tests/utils.rs b/src/tests/utils.rs deleted file mode 100644 index fece56c..0000000 --- a/src/tests/utils.rs +++ /dev/null @@ -1,383 +0,0 @@ -use crate::utils; -use reqwest::blocking::Client; -use std::collections::HashMap; -use std::env; -use url::ParseError; - -#[test] -fn data_to_data_url() { - let mime = "application/javascript"; - let data = "var word = 'hello';\nalert(word);\n"; - let datauri = utils::data_to_data_url(mime, data.as_bytes()); - assert_eq!( - &datauri, - "data:application/javascript;base64,dmFyIHdvcmQgPSAnaGVsbG8nOwphbGVydCh3b3JkKTsK" - ); -} - -#[test] -fn detect_mimetype() { - // Image - assert_eq!(utils::detect_mimetype(b"GIF87a"), "image/gif"); - assert_eq!(utils::detect_mimetype(b"GIF89a"), "image/gif"); - assert_eq!(utils::detect_mimetype(b"\xFF\xD8\xFF"), "image/jpeg"); - assert_eq!( - utils::detect_mimetype(b"\x89PNG\x0D\x0A\x1A\x0A"), - "image/png" - ); - assert_eq!(utils::detect_mimetype(b" Result<(), ParseError> { - let resolved_url = utils::resolve_url("https://www.kernel.org", "../category/signatures.html")?; - assert_eq!( - resolved_url.as_str(), - "https://www.kernel.org/category/signatures.html" - ); - - let resolved_url = utils::resolve_url("https://www.kernel.org", "category/signatures.html")?; - assert_eq!( - resolved_url.as_str(), - "https://www.kernel.org/category/signatures.html" - ); - - let resolved_url = utils::resolve_url( - "saved_page.htm", - "https://www.kernel.org/category/signatures.html", - )?; - assert_eq!( - resolved_url.as_str(), - "https://www.kernel.org/category/signatures.html" - ); - - let resolved_url = utils::resolve_url( - "https://www.kernel.org", - "//www.kernel.org/theme/images/logos/tux.png", - )?; - assert_eq!( - resolved_url.as_str(), - "https://www.kernel.org/theme/images/logos/tux.png" - ); - - let resolved_url = utils::resolve_url( - "https://www.kernel.org", - "//another-host.org/theme/images/logos/tux.png", - )?; - assert_eq!( - resolved_url.as_str(), - "https://another-host.org/theme/images/logos/tux.png" - ); - - let resolved_url = utils::resolve_url( - "https://www.kernel.org/category/signatures.html", - "/theme/images/logos/tux.png", - )?; - assert_eq!( - resolved_url.as_str(), - "https://www.kernel.org/theme/images/logos/tux.png" - ); - - let resolved_url = utils::resolve_url( - "https://www.w3schools.com/html/html_iframe.asp", - "default.asp", - )?; - assert_eq!( - resolved_url.as_str(), - "https://www.w3schools.com/html/default.asp" - ); - - let resolved_url = utils::resolve_url( - "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h", - "https://www.kernel.org/category/signatures.html", - )?; - assert_eq!( - resolved_url.as_str(), - "https://www.kernel.org/category/signatures.html" - ); - - let resolved_url = utils::resolve_url( - "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h", - "//www.w3schools.com/html/html_iframe.asp", - ) - .unwrap_or(str!()); - assert_eq!(resolved_url.as_str(), ""); - - let resolved_url = utils::resolve_url( - "file:///home/user/Websites/my-website/index.html", - "assets/images/logo.png", - ) - .unwrap_or(str!()); - assert_eq!( - resolved_url.as_str(), - "file:///home/user/Websites/my-website/assets/images/logo.png" - ); - - let resolved_url = utils::resolve_url( - "file:\\\\\\home\\user\\Websites\\my-website\\index.html", - "assets\\images\\logo.png", - ) - .unwrap_or(str!()); - assert_eq!( - resolved_url.as_str(), - "file:///home/user/Websites/my-website/assets/images/logo.png" - ); - - Ok(()) -} - -#[test] -fn is_data_url() { - // Passing - assert!(utils::is_data_url( - "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h" - )); - - // Failing - assert!(!utils::is_data_url("https://kernel.org")); - assert!(!utils::is_data_url("//kernel.org")); - assert!(!utils::is_data_url("")); -} - -#[test] -fn clean_url() { - assert_eq!( - utils::clean_url("https://somewhere.com/font.eot#iefix"), - "https://somewhere.com/font.eot" - ); - assert_eq!( - utils::clean_url("https://somewhere.com/font.eot#"), - "https://somewhere.com/font.eot" - ); - assert_eq!( - utils::clean_url("https://somewhere.com/font.eot?#"), - "https://somewhere.com/font.eot" - ); -} - -#[test] -fn data_url_to_text() { - assert_eq!( - utils::data_url_to_text("data:text/html;base64,V29yayBleHBhbmRzIHNvIGFzIHRvIGZpbGwgdGhlIHRpbWUgYXZhaWxhYmxlIGZvciBpdHMgY29tcGxldGlvbg=="), - "Work expands so as to fill the time available for its completion" - ); - - assert_eq!( - utils::data_url_to_text( - "data:text/html;utf8,Work expands so as to fill the time available for its completion" - ), - "Work expands so as to fill the time available for its completion" - ); - - assert_eq!( - utils::data_url_to_text( - "data:text/html,Work expands so as to fill the time available for its completion" - ), - "Work expands so as to fill the time available for its completion" - ); - - assert_eq!( - utils::data_url_to_text( - " data:text/html;charset=utf-8,Work expands so as to fill the time available for its completion " - ), - "Work expands so as to fill the time available for its completion" - ); -} - -#[test] -fn decode_url() { - assert_eq!( - utils::decode_url(str!( - "%E6%A4%9C%E3%83%92%E3%83%A0%E8%A7%A3%E5%A1%97%E3%82%83%E3%83%83%20%3D%20%E3%82%B5" - )), - "検ヒム解塗ゃッ = サ" - ); - - assert_eq!(utils::decode_url(str!("%20 %20")), " "); -} - -#[test] -fn retrieve_asset() { - let cache = &mut HashMap::new(); - let client = Client::new(); - - let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" }; - - // If both source and target are data URLs, - // ensure the result contains target data URL - let (data, final_url) = utils::retrieve_asset( - cache, - &client, - "data:text/html;base64,SoUrCe", - "data:text/html;base64,TaRgEt", - true, - "", - false, - ) - .unwrap(); - assert_eq!(&data, "data:text/html;base64,TaRgEt"); - assert_eq!(&final_url, "data:text/html;base64,TaRgEt"); - - // Media type parameter should not influence data URLs - let (data, final_url) = utils::retrieve_asset( - cache, - &client, - "data:text/html;base64,SoUrCe", - "data:text/html;base64,TaRgEt", - true, - "image/png", - false, - ) - .unwrap(); - assert_eq!(&data, "data:text/html;base64,TaRgEt"); - assert_eq!(&final_url, "data:text/html;base64,TaRgEt"); - - // Inclusion of local assets from data URL sources should not be allowed - let (data, final_url) = utils::retrieve_asset( - cache, - &client, - "data:text/html;base64,SoUrCe", - "file:///etc/passwd", - true, - "", - false, - ) - .unwrap(); - assert_eq!(&data, ""); - assert_eq!(&final_url, ""); - - // Inclusion of local assets from remote sources should not be allowed - let (data, final_url) = utils::retrieve_asset( - cache, - &client, - "https://kernel.org/", - "file:///etc/passwd", - true, - "", - false, - ) - .unwrap(); - assert_eq!(&data, ""); - assert_eq!(&final_url, ""); - - // Inclusion of local assets from local sources should be allowed - let cwd = env::current_dir().unwrap(); - let (data, final_url) = utils::retrieve_asset( - cache, - &client, - &format!( - "{file}{cwd}/src/tests/data/local-file.html", - file = file_url_protocol, - cwd = cwd.to_str().unwrap() - ), - &format!( - "{file}{cwd}/src/tests/data/local-script.js", - file = file_url_protocol, - cwd = cwd.to_str().unwrap() - ), - true, - "application/javascript", - false, - ) - .unwrap(); - assert_eq!(&data, "data:application/javascript;base64,ZG9jdW1lbnQuYm9keS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSAiZ3JlZW4iOwpkb2N1bWVudC5ib2R5LnN0eWxlLmNvbG9yID0gInJlZCI7Cg=="); - assert_eq!( - &final_url, - &format!( - "{file}{cwd}/src/tests/data/local-script.js", - file = file_url_protocol, - cwd = cwd.to_str().unwrap() - ) - ); -} diff --git a/src/tests/utils/clean_url.rs b/src/tests/utils/clean_url.rs new file mode 100644 index 0000000..7e794be --- /dev/null +++ b/src/tests/utils/clean_url.rs @@ -0,0 +1,32 @@ +use crate::utils; + +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn passing_removes_fragment() { + assert_eq!( + utils::clean_url("https://somewhere.com/font.eot#iefix"), + "https://somewhere.com/font.eot" + ); +} + +#[test] +fn passing_removes_empty_fragment() { + assert_eq!( + utils::clean_url("https://somewhere.com/font.eot#"), + "https://somewhere.com/font.eot" + ); +} + +#[test] +fn passing_removes_empty_query_and_empty_fragment() { + assert_eq!( + utils::clean_url("https://somewhere.com/font.eot?#"), + "https://somewhere.com/font.eot" + ); +} diff --git a/src/tests/utils/data_to_data_url.rs b/src/tests/utils/data_to_data_url.rs new file mode 100644 index 0000000..bccdf77 --- /dev/null +++ b/src/tests/utils/data_to_data_url.rs @@ -0,0 +1,20 @@ +use crate::utils; + +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn passing_encode_string_with_specific_media_type() { + let mime = "application/javascript"; + let data = "var word = 'hello';\nalert(word);\n"; + let data_url = utils::data_to_data_url(mime, data.as_bytes()); + + assert_eq!( + &data_url, + "data:application/javascript;base64,dmFyIHdvcmQgPSAnaGVsbG8nOwphbGVydCh3b3JkKTsK" + ); +} diff --git a/src/tests/utils/data_url_to_text.rs b/src/tests/utils/data_url_to_text.rs new file mode 100644 index 0000000..b7d50b2 --- /dev/null +++ b/src/tests/utils/data_url_to_text.rs @@ -0,0 +1,58 @@ +use crate::utils; + +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn passing_parse_text_html_base64() { + assert_eq!( + utils::data_url_to_text("data:text/html;base64,V29yayBleHBhbmRzIHNvIGFzIHRvIGZpbGwgdGhlIHRpbWUgYXZhaWxhYmxlIGZvciBpdHMgY29tcGxldGlvbg=="), + "Work expands so as to fill the time available for its completion" + ); +} + +#[test] +fn passing_parse_text_html_utf8() { + assert_eq!( + utils::data_url_to_text( + "data:text/html;utf8,Work expands so as to fill the time available for its completion" + ), + "Work expands so as to fill the time available for its completion" + ); +} + +#[test] +fn passing_parse_text_html_plaintext() { + assert_eq!( + utils::data_url_to_text( + "data:text/html,Work expands so as to fill the time available for its completion" + ), + "Work expands so as to fill the time available for its completion" + ); +} + +#[test] +fn passing_parse_text_html_charset_utf_8_between_two_whitespaces() { + assert_eq!( + utils::data_url_to_text( + " data:text/html;charset=utf-8,Work expands so as to fill the time available for its completion " + ), + "Work expands so as to fill the time available for its completion" + ); +} + +// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗ +// ██╔════╝██╔══██╗██║██║ ██║████╗ ██║██╔════╝ +// █████╗ ███████║██║██║ ██║██╔██╗ ██║██║ ███╗ +// ██╔══╝ ██╔══██║██║██║ ██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn failing_just_word_data() { + assert_eq!(utils::data_url_to_text("data"), ""); +} diff --git a/src/tests/utils/decode_url.rs b/src/tests/utils/decode_url.rs new file mode 100644 index 0000000..e839ac1 --- /dev/null +++ b/src/tests/utils/decode_url.rs @@ -0,0 +1,23 @@ +use crate::utils; + +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn passing_decode_unicode_characters() { + assert_eq!( + utils::decode_url(str!( + "%E6%A4%9C%E3%83%92%E3%83%A0%E8%A7%A3%E5%A1%97%E3%82%83%E3%83%83%20%3D%20%E3%82%B5" + )), + "検ヒム解塗ゃッ = サ" + ); +} + +#[test] +fn passing_decode_whitespaces() { + assert_eq!(utils::decode_url(str!("%20 %20")), " "); +} diff --git a/src/tests/utils/detect_media_type.rs b/src/tests/utils/detect_media_type.rs new file mode 100644 index 0000000..99dc51f --- /dev/null +++ b/src/tests/utils/detect_media_type.rs @@ -0,0 +1,57 @@ +use crate::utils; + +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn passing_image_media_types() { + assert_eq!(utils::detect_media_type(b"GIF87a"), "image/gif"); + assert_eq!(utils::detect_media_type(b"GIF89a"), "image/gif"); + assert_eq!(utils::detect_media_type(b"\xFF\xD8\xFF"), "image/jpeg"); + assert_eq!( + utils::detect_media_type(b"\x89PNG\x0D\x0A\x1A\x0A"), + "image/png" + ); + assert_eq!(utils::detect_media_type(b" Result<(), ParseError> { + let resolved_url = utils::resolve_url("https://www.kernel.org", "../category/signatures.html")?; + + assert_eq!( + resolved_url.as_str(), + "https://www.kernel.org/category/signatures.html" + ); + + Ok(()) +} + +#[test] +fn passing_from_just_filename_to_full_https_url() -> Result<(), ParseError> { + let resolved_url = utils::resolve_url( + "saved_page.htm", + "https://www.kernel.org/category/signatures.html", + )?; + + assert_eq!( + resolved_url.as_str(), + "https://www.kernel.org/category/signatures.html" + ); + + Ok(()) +} + +#[test] +fn passing_from_https_url_to_url_with_no_protocol() -> Result<(), ParseError> { + let resolved_url = utils::resolve_url( + "https://www.kernel.org", + "//www.kernel.org/theme/images/logos/tux.png", + )?; + + assert_eq!( + resolved_url.as_str(), + "https://www.kernel.org/theme/images/logos/tux.png" + ); + + Ok(()) +} + +#[test] +fn passing_from_https_url_to_url_with_no_protocol_and_on_different_hostname( +) -> Result<(), ParseError> { + let resolved_url = utils::resolve_url( + "https://www.kernel.org", + "//another-host.org/theme/images/logos/tux.png", + )?; + + assert_eq!( + resolved_url.as_str(), + "https://another-host.org/theme/images/logos/tux.png" + ); + + Ok(()) +} + +#[test] +fn passing_from_https_url_to_relative_root_path() -> Result<(), ParseError> { + let resolved_url = utils::resolve_url( + "https://www.kernel.org/category/signatures.html", + "/theme/images/logos/tux.png", + )?; + + assert_eq!( + resolved_url.as_str(), + "https://www.kernel.org/theme/images/logos/tux.png" + ); + + Ok(()) +} + +#[test] +fn passing_from_https_to_just_filename() -> Result<(), ParseError> { + let resolved_url = utils::resolve_url( + "https://www.w3schools.com/html/html_iframe.asp", + "default.asp", + )?; + + assert_eq!( + resolved_url.as_str(), + "https://www.w3schools.com/html/default.asp" + ); + + Ok(()) +} + +#[test] +fn passing_from_data_url_to_https() -> Result<(), ParseError> { + let resolved_url = utils::resolve_url( + "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h", + "https://www.kernel.org/category/signatures.html", + )?; + + assert_eq!( + resolved_url.as_str(), + "https://www.kernel.org/category/signatures.html" + ); + + Ok(()) +} + +#[test] +fn passing_from_file_url_to_relative_path() -> Result<(), ParseError> { + let resolved_url = utils::resolve_url( + "file:///home/user/Websites/my-website/index.html", + "assets/images/logo.png", + ) + .unwrap_or(str!()); + + assert_eq!( + resolved_url.as_str(), + "file:///home/user/Websites/my-website/assets/images/logo.png" + ); + + Ok(()) +} + +#[test] +fn passing_from_file_url_to_relative_path_with_backslashes() -> Result<(), ParseError> { + let resolved_url = utils::resolve_url( + "file:\\\\\\home\\user\\Websites\\my-website\\index.html", + "assets\\images\\logo.png", + ) + .unwrap_or(str!()); + + assert_eq!( + resolved_url.as_str(), + "file:///home/user/Websites/my-website/assets/images/logo.png" + ); + + Ok(()) +} + +#[test] +fn passing_from_data_url_to_file_url() -> Result<(), ParseError> { + let resolved_url = utils::resolve_url( + "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h", + "file:///etc/passwd", + ) + .unwrap_or(str!()); + + assert_eq!(resolved_url.as_str(), "file:///etc/passwd"); + + Ok(()) +} + +// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗ +// ██╔════╝██╔══██╗██║██║ ██║████╗ ██║██╔════╝ +// █████╗ ███████║██║██║ ██║██╔██╗ ██║██║ ███╗ +// ██╔══╝ ██╔══██║██║██║ ██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn failing_from_data_url_to_url_with_no_protocol() -> Result<(), ParseError> { + let resolved_url = utils::resolve_url( + "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h", + "//www.w3schools.com/html/html_iframe.asp", + ) + .unwrap_or(str!()); + + assert_eq!(resolved_url.as_str(), ""); + + Ok(()) +} diff --git a/src/tests/utils/retrieve_asset.rs b/src/tests/utils/retrieve_asset.rs new file mode 100644 index 0000000..bfa4dc2 --- /dev/null +++ b/src/tests/utils/retrieve_asset.rs @@ -0,0 +1,137 @@ +use crate::utils; +use reqwest::blocking::Client; +use std::collections::HashMap; +use std::env; + +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn passing_read_data_url() { + let cache = &mut HashMap::new(); + let client = Client::new(); + + // If both source and target are data URLs, + // ensure the result contains target data URL + let (retrieved_data, final_url) = utils::retrieve_asset( + cache, + &client, + "data:text/html;base64,SoUrCe", + "data:text/html;base64,TaRgEt", + true, + "", + false, + ) + .unwrap(); + assert_eq!(&retrieved_data, "data:text/html;base64,TaRgEt"); + assert_eq!(&final_url, "data:text/html;base64,TaRgEt"); +} + +#[test] +fn passing_read_data_url_ignore_suggested_media_type() { + let cache = &mut HashMap::new(); + let client = Client::new(); + + // Media type parameter should not influence data URLs + let (data, final_url) = utils::retrieve_asset( + cache, + &client, + "data:text/html;base64,SoUrCe", + "data:text/html;base64,TaRgEt", + true, + "image/png", + false, + ) + .unwrap(); + assert_eq!(&data, "data:text/html;base64,TaRgEt"); + assert_eq!(&final_url, "data:text/html;base64,TaRgEt"); +} + +#[test] +fn passing_read_local_file_with_file_url_parent() { + let cache = &mut HashMap::new(); + let client = Client::new(); + + let file_url_protocol: &str = if cfg!(windows) { "file:///" } else { "file://" }; + + // Inclusion of local assets from local sources should be allowed + let cwd = env::current_dir().unwrap(); + let (data, final_url) = utils::retrieve_asset( + cache, + &client, + &format!( + "{file}{cwd}/src/tests/data/local-file.html", + file = file_url_protocol, + cwd = cwd.to_str().unwrap() + ), + &format!( + "{file}{cwd}/src/tests/data/local-script.js", + file = file_url_protocol, + cwd = cwd.to_str().unwrap() + ), + true, + "application/javascript", + false, + ) + .unwrap(); + assert_eq!(&data, "data:application/javascript;base64,ZG9jdW1lbnQuYm9keS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSAiZ3JlZW4iOwpkb2N1bWVudC5ib2R5LnN0eWxlLmNvbG9yID0gInJlZCI7Cg=="); + assert_eq!( + &final_url, + &format!( + "{file}{cwd}/src/tests/data/local-script.js", + file = file_url_protocol, + cwd = cwd.to_str().unwrap() + ) + ); +} + +// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗ +// ██╔════╝██╔══██╗██║██║ ██║████╗ ██║██╔════╝ +// █████╗ ███████║██║██║ ██║██╔██╗ ██║██║ ███╗ +// ██╔══╝ ██╔══██║██║██║ ██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn failing_read_local_file_with_data_url_parent() { + let cache = &mut HashMap::new(); + let client = Client::new(); + + // Inclusion of local assets from data URL sources should not be allowed + let (data, final_url) = utils::retrieve_asset( + cache, + &client, + "data:text/html;base64,SoUrCe", + "file:///etc/passwd", + true, + "", + false, + ) + .unwrap(); + assert_eq!(&data, ""); + assert_eq!(&final_url, ""); +} + +#[test] +fn failing_read_local_file_with_https_parent() { + let cache = &mut HashMap::new(); + let client = Client::new(); + + // Inclusion of local assets from remote sources should not be allowed + let (data, final_url) = utils::retrieve_asset( + cache, + &client, + "https://kernel.org/", + "file:///etc/passwd", + true, + "", + false, + ) + .unwrap(); + assert_eq!(&data, ""); + assert_eq!(&final_url, ""); +} diff --git a/src/tests/utils/url_has_protocol.rs b/src/tests/utils/url_has_protocol.rs new file mode 100644 index 0000000..1c6e213 --- /dev/null +++ b/src/tests/utils/url_has_protocol.rs @@ -0,0 +1,83 @@ +use crate::utils; + +// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗ +// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝ +// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗ +// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn passing_mailto() { + assert!(utils::url_has_protocol( + "mailto:somebody@somewhere.com?subject=hello" + )); +} + +#[test] +fn passing_tel() { + assert!(utils::url_has_protocol("tel:5551234567")); +} + +#[test] +fn passing_ftp_no_slashes() { + assert!(utils::url_has_protocol("ftp:some-ftp-server.com")); +} + +#[test] +fn passing_ftp_with_credentials() { + assert!(utils::url_has_protocol( + "ftp://user:password@some-ftp-server.com" + )); +} + +#[test] +fn passing_javascript() { + assert!(utils::url_has_protocol("javascript:void(0)")); +} + +#[test] +fn passing_http() { + assert!(utils::url_has_protocol("http://news.ycombinator.com")); +} + +#[test] +fn passing_https() { + assert!(utils::url_has_protocol("https://github.com")); +} + +#[test] +fn passing_mailto_uppercase() { + assert!(utils::url_has_protocol( + "MAILTO:somebody@somewhere.com?subject=hello" + )); +} + +// ███████╗ █████╗ ██╗██╗ ██╗███╗ ██╗ ██████╗ +// ██╔════╝██╔══██╗██║██║ ██║████╗ ██║██╔════╝ +// █████╗ ███████║██║██║ ██║██╔██╗ ██║██║ ███╗ +// ██╔══╝ ██╔══██║██║██║ ██║██║╚██╗██║██║ ██║ +// ██║ ██║ ██║██║███████╗██║██║ ╚████║╚██████╔╝ +// ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + +#[test] +fn failing_url_with_no_protocol() { + assert!(!utils::url_has_protocol( + "//some-hostname.com/some-file.html" + )); +} + +#[test] +fn failing_relative_path() { + assert!(!utils::url_has_protocol("some-hostname.com/some-file.html")); +} + +#[test] +fn failing_relative_to_root_path() { + assert!(!utils::url_has_protocol("/some-file.html")); +} + +#[test] +fn failing_empty_string() { + assert!(!utils::url_has_protocol("")); +} diff --git a/src/utils.rs b/src/utils.rs index fb38116..13ba70f 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -68,15 +68,15 @@ const MAGIC: [[&[u8]; 2]; 19] = [ ]; pub fn data_to_data_url(mime: &str, data: &[u8]) -> String { - let mimetype = if mime.is_empty() { - detect_mimetype(data) + let media_type = if mime.is_empty() { + detect_media_type(data) } else { mime.to_string() }; - format!("data:{};base64,{}", mimetype, base64::encode(data)) + format!("data:{};base64,{}", media_type, base64::encode(data)) } -pub fn detect_mimetype(data: &[u8]) -> String { +pub fn detect_media_type(data: &[u8]) -> String { for item in MAGIC.iter() { if data.starts_with(item[0]) { return String::from_utf8(item[1].to_vec()).unwrap(); @@ -356,7 +356,7 @@ pub fn retrieve_asset( response.copy_to(&mut data)?; // Attempt to obtain MIME type by reading the Content-Type header - let mimetype = if mime == "" { + let media_type = if mime == "" { response .headers() .get(CONTENT_TYPE) @@ -365,7 +365,7 @@ pub fn retrieve_asset( } else { mime }; - let data_url = data_to_data_url(&mimetype, &data); + let data_url = data_to_data_url(&media_type, &data); // Add to cache cache.insert(new_cache_key, data_url.clone()); Ok((data_url, res_url))