diff options
Diffstat (limited to 'cli/file_fetcher.rs')
-rw-r--r-- | cli/file_fetcher.rs | 278 |
1 files changed, 238 insertions, 40 deletions
diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index c4e0e4fec..2e75517e6 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -6,6 +6,7 @@ use crate::http_util::create_http_client; use crate::http_util::FetchOnceResult; use crate::msg; use crate::op_error::OpError; +use crate::permissions::Permissions; use deno_core::ErrBox; use deno_core::ModuleSpecifier; use futures::future::FutureExt; @@ -109,6 +110,7 @@ impl SourceFileFetcher { pub fn fetch_cached_source_file( &self, specifier: &ModuleSpecifier, + permissions: Permissions, ) -> Option<SourceFile> { let maybe_source_file = self.source_file_cache.get(specifier.to_string()); @@ -123,7 +125,7 @@ impl SourceFileFetcher { // future, because it doesn't actually do any asynchronous // action in that path. if let Ok(maybe_source_file) = - self.get_source_file_from_local_cache(specifier.as_url()) + self.get_source_file_from_local_cache(specifier.as_url(), &permissions) { return maybe_source_file; } @@ -148,6 +150,7 @@ impl SourceFileFetcher { &self, specifier: &ModuleSpecifier, maybe_referrer: Option<ModuleSpecifier>, + permissions: Permissions, ) -> Result<SourceFile, ErrBox> { let module_url = specifier.as_url().to_owned(); debug!("fetch_source_file specifier: {} ", &module_url); @@ -167,6 +170,7 @@ impl SourceFileFetcher { self.use_disk_cache, self.no_remote, self.cached_only, + &permissions, ) .await; @@ -222,6 +226,7 @@ impl SourceFileFetcher { fn get_source_file_from_local_cache( &self, module_url: &Url, + permissions: &Permissions, ) -> Result<Option<SourceFile>, ErrBox> { let url_scheme = module_url.scheme(); let is_local_file = url_scheme == "file"; @@ -229,7 +234,7 @@ impl SourceFileFetcher { // Local files are always fetched from disk bypassing cache entirely. if is_local_file { - return self.fetch_local_file(&module_url).map(Some); + return self.fetch_local_file(&module_url, permissions).map(Some); } self.fetch_cached_remote_source(&module_url) @@ -252,6 +257,7 @@ impl SourceFileFetcher { use_disk_cache: bool, no_remote: bool, cached_only: bool, + permissions: &Permissions, ) -> Result<SourceFile, ErrBox> { let url_scheme = module_url.scheme(); let is_local_file = url_scheme == "file"; @@ -259,7 +265,7 @@ impl SourceFileFetcher { // Local files are always fetched from disk bypassing cache entirely. if is_local_file { - return self.fetch_local_file(&module_url); + return self.fetch_local_file(&module_url, permissions); } // The file is remote, fail if `no_remote` is true. @@ -276,18 +282,29 @@ impl SourceFileFetcher { // Fetch remote file and cache on-disk for subsequent access self - .fetch_remote_source(&module_url, use_disk_cache, cached_only, 10) + .fetch_remote_source( + &module_url, + use_disk_cache, + cached_only, + 10, + permissions, + ) .await } /// Fetch local source file. - fn fetch_local_file(&self, module_url: &Url) -> Result<SourceFile, ErrBox> { + fn fetch_local_file( + &self, + module_url: &Url, + permissions: &Permissions, + ) -> Result<SourceFile, ErrBox> { let filepath = module_url.to_file_path().map_err(|()| { ErrBox::from(OpError::uri_error( "File URL contains invalid path".to_owned(), )) })?; + permissions.check_read(&filepath)?; let source_code = match fs::read(filepath.clone()) { Ok(c) => c, Err(e) => return Err(e.into()), @@ -390,12 +407,17 @@ impl SourceFileFetcher { use_disk_cache: bool, cached_only: bool, redirect_limit: i64, + permissions: &Permissions, ) -> Pin<Box<dyn Future<Output = Result<SourceFile, ErrBox>>>> { if redirect_limit < 0 { let e = OpError::http("too many redirects".to_string()); return futures::future::err(e.into()).boxed_local(); } + if let Err(e) = permissions.check_net_url(&module_url) { + return futures::future::err(e.into()).boxed_local(); + } + let is_blacklisted = check_cache_blacklist(module_url, self.cache_blacklist.as_ref()); // First try local cache @@ -441,6 +463,7 @@ impl SourceFileFetcher { Ok((_, headers)) => headers.get("etag").map(String::from), Err(_) => None, }; + let permissions = permissions.clone(); let http_client = self.http_client.clone(); // Single pass fetch, either yields code or yields redirect. let f = async move { @@ -463,6 +486,7 @@ impl SourceFileFetcher { use_disk_cache, cached_only, redirect_limit - 1, + &permissions, ) .await } @@ -752,11 +776,15 @@ mod tests { if cfg!(windows) { // Should fail: missing drive letter. let u = Url::parse("file:///etc/passwd").unwrap(); - fetcher.fetch_local_file(&u).unwrap_err(); + fetcher + .fetch_local_file(&u, &Permissions::allow_all()) + .unwrap_err(); } else { // Should fail: local network paths are not supported on unix. let u = Url::parse("file://server/etc/passwd").unwrap(); - fetcher.fetch_local_file(&u).unwrap_err(); + fetcher + .fetch_local_file(&u, &Permissions::allow_all()) + .unwrap_err(); } } @@ -774,7 +802,13 @@ mod tests { let cache_filename = fetcher.http_cache.get_cache_filename(&module_url); let result = fetcher - .get_source_file(&module_url, true, false, false) + .get_source_file( + &module_url, + true, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let r = result.unwrap(); @@ -795,7 +829,13 @@ mod tests { metadata.write(&cache_filename).unwrap(); let result2 = fetcher_1 - .get_source_file(&module_url, true, false, false) + .get_source_file( + &module_url, + true, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result2.is_ok()); let r2 = result2.unwrap(); @@ -818,7 +858,13 @@ mod tests { metadata.write(&cache_filename).unwrap(); let result3 = fetcher_2 - .get_source_file(&module_url_1, true, false, false) + .get_source_file( + &module_url_1, + true, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result3.is_ok()); let r3 = result3.unwrap(); @@ -839,7 +885,13 @@ mod tests { // and don't use cache let fetcher = setup_file_fetcher(temp_dir.path()); let result4 = fetcher - .get_source_file(&module_url_2, false, false, false) + .get_source_file( + &module_url_2, + false, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result4.is_ok()); let r4 = result4.unwrap(); @@ -863,7 +915,13 @@ mod tests { let cache_filename = fetcher.http_cache.get_cache_filename(&module_url); let result = fetcher - .get_source_file(&module_url, true, false, false) + .get_source_file( + &module_url, + true, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let r = result.unwrap(); @@ -883,7 +941,13 @@ mod tests { metadata.write(&cache_filename).unwrap(); let result2 = fetcher - .get_source_file(&module_url, true, false, false) + .get_source_file( + &module_url, + true, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result2.is_ok()); let r2 = result2.unwrap(); @@ -903,7 +967,13 @@ mod tests { // process) and don't use cache let fetcher = setup_file_fetcher(temp_dir.path()); let result3 = fetcher - .get_source_file(&module_url_1, false, false, false) + .get_source_file( + &module_url_1, + false, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result3.is_ok()); let r3 = result3.unwrap(); @@ -930,7 +1000,9 @@ mod tests { fetcher.http_cache.get_cache_filename(&specifier.as_url()); // first download - let r = fetcher.fetch_source_file(&specifier, None).await; + let r = fetcher + .fetch_source_file(&specifier, None, Permissions::allow_all()) + .await; assert!(r.is_ok()); let headers_file_name = @@ -946,7 +1018,9 @@ mod tests { // `use_disk_cache` is set to false, this can be verified using source // header file creation timestamp (should be the same as after first // download) - let r = fetcher.fetch_source_file(&specifier, None).await; + let r = fetcher + .fetch_source_file(&specifier, None, Permissions::allow_all()) + .await; assert!(r.is_ok()); let result = fs::File::open(&headers_file_name); @@ -984,7 +1058,13 @@ mod tests { // Test basic follow and headers recording let result = fetcher - .get_source_file(&redirect_module_url, true, false, false) + .get_source_file( + &redirect_module_url, + true, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let mod_meta = result.unwrap(); @@ -1033,7 +1113,13 @@ mod tests { // Test double redirects and headers recording let result = fetcher - .get_source_file(&double_redirect_url, true, false, false) + .get_source_file( + &double_redirect_url, + true, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let mod_meta = result.unwrap(); @@ -1080,7 +1166,13 @@ mod tests { // Test that redirect target is not downloaded twice for different redirect source. let result = fetcher - .get_source_file(&double_redirect_url, true, false, false) + .get_source_file( + &double_redirect_url, + true, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let result = fs::File::open(&target_path); @@ -1095,7 +1187,13 @@ mod tests { // using source header file creation timestamp (should be the same as // after first `get_source_file`) let result = fetcher - .get_source_file(&redirect_url, true, false, false) + .get_source_file( + &redirect_url, + true, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let result = fs::File::open(&target_path_); @@ -1121,12 +1219,24 @@ mod tests { // Test that redirections can be limited let result = fetcher - .fetch_remote_source(&double_redirect_url, false, false, 2) + .fetch_remote_source( + &double_redirect_url, + false, + false, + 2, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let result = fetcher - .fetch_remote_source(&double_redirect_url, false, false, 1) + .fetch_remote_source( + &double_redirect_url, + false, + false, + 1, + &Permissions::allow_all(), + ) .await; assert!(result.is_err()); // FIXME(bartlomieju): @@ -1161,7 +1271,13 @@ mod tests { // Test basic follow and headers recording let result = fetcher - .get_source_file(&redirect_module_url, true, false, false) + .get_source_file( + &redirect_module_url, + true, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let mod_meta = result.unwrap(); @@ -1193,7 +1309,13 @@ mod tests { Url::parse("http://localhost:4545/cli/tests/002_hello.ts").unwrap(); // Remote modules are not allowed let result = fetcher - .get_source_file(&module_url, true, true, false) + .get_source_file( + &module_url, + true, + true, + false, + &Permissions::allow_all(), + ) .await; assert!(result.is_err()); // FIXME(bartlomieju): @@ -1216,7 +1338,13 @@ mod tests { // file hasn't been cached before let result = fetcher - .get_source_file(&module_url, true, false, true) + .get_source_file( + &module_url, + true, + false, + true, + &Permissions::allow_all(), + ) .await; assert!(result.is_err()); // FIXME(bartlomieju): @@ -1225,12 +1353,24 @@ mod tests { // download and cache file let result = fetcher_1 - .get_source_file(&module_url_1, true, false, false) + .get_source_file( + &module_url_1, + true, + false, + false, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); // module is already cached, should be ok even with `cached_only` let result = fetcher_2 - .get_source_file(&module_url_2, true, false, true) + .get_source_file( + &module_url_2, + true, + false, + true, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); drop(http_server_guard); @@ -1244,7 +1384,13 @@ mod tests { Url::parse("http://127.0.0.1:4545/cli/tests/subdir/mt_video_mp2t.t3.ts") .unwrap(); let result = fetcher - .fetch_remote_source(&module_url, false, false, 10) + .fetch_remote_source( + &module_url, + false, + false, + 10, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let r = result.unwrap(); @@ -1289,7 +1435,13 @@ mod tests { let module_url_3_ = module_url_3.clone(); let result = fetcher - .fetch_remote_source(&module_url, false, false, 10) + .fetch_remote_source( + &module_url, + false, + false, + 10, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let r = result.unwrap(); @@ -1298,7 +1450,13 @@ mod tests { let (_, headers) = fetcher.http_cache.get(&module_url).unwrap(); assert_eq!(headers.get("content-type").unwrap(), "text/typescript"); let result = fetcher_1 - .fetch_remote_source(&module_url_2, false, false, 10) + .fetch_remote_source( + &module_url_2, + false, + false, + 10, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let r2 = result.unwrap(); @@ -1309,7 +1467,13 @@ mod tests { // test unknown extension let result = fetcher_2 - .fetch_remote_source(&module_url_3, false, false, 10) + .fetch_remote_source( + &module_url_3, + false, + false, + 10, + &Permissions::allow_all(), + ) .await; assert!(result.is_ok()); let r3 = result.unwrap(); @@ -1328,14 +1492,18 @@ mod tests { // Test failure case. let specifier = ModuleSpecifier::resolve_url(file_url!("/baddir/hello.ts")).unwrap(); - let r = fetcher.fetch_source_file(&specifier, None).await; + let r = fetcher + .fetch_source_file(&specifier, None, Permissions::allow_all()) + .await; assert!(r.is_err()); let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("js/main.ts"); let specifier = ModuleSpecifier::resolve_url_or_path(p.to_str().unwrap()).unwrap(); - let r = fetcher.fetch_source_file(&specifier, None).await; + let r = fetcher + .fetch_source_file(&specifier, None, Permissions::allow_all()) + .await; assert!(r.is_ok()); } @@ -1347,14 +1515,18 @@ mod tests { // Test failure case. let specifier = ModuleSpecifier::resolve_url(file_url!("/baddir/hello.ts")).unwrap(); - let r = fetcher.fetch_source_file(&specifier, None).await; + let r = fetcher + .fetch_source_file(&specifier, None, Permissions::allow_all()) + .await; assert!(r.is_err()); let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("js/main.ts"); let specifier = ModuleSpecifier::resolve_url_or_path(p.to_str().unwrap()).unwrap(); - let r = fetcher.fetch_source_file(&specifier, None).await; + let r = fetcher + .fetch_source_file(&specifier, None, Permissions::allow_all()) + .await; assert!(r.is_ok()); } @@ -1367,7 +1539,9 @@ mod tests { .join("tests/001_hello.js"); let specifier = ModuleSpecifier::resolve_url_or_path(p.to_str().unwrap()).unwrap(); - let r = fetcher.fetch_source_file(&specifier, None).await; + let r = fetcher + .fetch_source_file(&specifier, None, Permissions::allow_all()) + .await; assert!(r.is_ok()); } @@ -1611,7 +1785,13 @@ mod tests { Url::parse("http://127.0.0.1:4545/etag_script.ts").unwrap(); let source = fetcher - .fetch_remote_source(&module_url, false, false, 1) + .fetch_remote_source( + &module_url, + false, + false, + 1, + &Permissions::allow_all(), + ) .await; assert!(source.is_ok()); let source = source.unwrap(); @@ -1633,7 +1813,13 @@ mod tests { let file_name = fetcher.http_cache.get_cache_filename(&module_url); let _ = fs::write(&file_name, "changed content"); let cached_source = fetcher - .fetch_remote_source(&module_url, false, false, 1) + .fetch_remote_source( + &module_url, + false, + false, + 1, + &Permissions::allow_all(), + ) .await .unwrap(); assert_eq!(cached_source.source_code, b"changed content"); @@ -1732,7 +1918,13 @@ mod tests { let module_url = Url::parse("http://127.0.0.1:4545/xTypeScriptTypes.js").unwrap(); let source = fetcher - .fetch_remote_source(&module_url, false, false, 1) + .fetch_remote_source( + &module_url, + false, + false, + 1, + &Permissions::allow_all(), + ) .await; assert!(source.is_ok()); let source = source.unwrap(); @@ -1752,7 +1944,13 @@ mod tests { let module_url = Url::parse("http://127.0.0.1:4545/referenceTypes.js").unwrap(); let source = fetcher - .fetch_remote_source(&module_url, false, false, 1) + .fetch_remote_source( + &module_url, + false, + false, + 1, + &Permissions::allow_all(), + ) .await; assert!(source.is_ok()); let source = source.unwrap(); |