From 3ec248cff8fff1a41c2b2ad5301e7aa3db00c6a8 Mon Sep 17 00:00:00 2001 From: Kitson Kelly Date: Mon, 24 Jan 2022 11:27:52 +1100 Subject: [PATCH] fix(lsp): respect DENO_CERT and other options related to TLS certs (#13467) Fixes #13437 --- cli/file_fetcher.rs | 80 ++++++++++++ cli/lsp/cache.rs | 6 + cli/lsp/config.rs | 16 +++ cli/lsp/language_server.rs | 62 ++++++--- cli/lsp/registries.rs | 54 +++++--- cli/lsp/repl.rs | 3 + cli/proc_state.rs | 73 +---------- cli/tests/integration/lsp_tests.rs | 118 ++++++++++++++++++ .../lsp/did_open_params_tls_cert.json | 8 ++ cli/tests/testdata/lsp/initialize_params.json | 3 + .../lsp/initialize_params_tls_cert.json | 71 +++++++++++ 11 files changed, 393 insertions(+), 101 deletions(-) create mode 100644 cli/tests/testdata/lsp/did_open_params_tls_cert.json create mode 100644 cli/tests/testdata/lsp/initialize_params_tls_cert.json diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs index 1ca04ed9a6..92c253e31f 100644 --- a/cli/file_fetcher.rs +++ b/cli/file_fetcher.rs @@ -12,6 +12,7 @@ use crate::version::get_user_agent; use data_url::DataUrl; use deno_ast::MediaType; +use deno_core::anyhow::anyhow; use deno_core::error::custom_error; use deno_core::error::generic_error; use deno_core::error::uri_error; @@ -22,7 +23,11 @@ use deno_core::parking_lot::Mutex; use deno_core::ModuleSpecifier; use deno_runtime::deno_fetch::create_http_client; use deno_runtime::deno_fetch::reqwest; +use deno_runtime::deno_tls::rustls; use deno_runtime::deno_tls::rustls::RootCertStore; +use deno_runtime::deno_tls::rustls_native_certs::load_native_certs; +use deno_runtime::deno_tls::rustls_pemfile; +use deno_runtime::deno_tls::webpki_roots; use deno_runtime::deno_web::BlobStore; use deno_runtime::permissions::Permissions; use log::debug; @@ -31,6 +36,7 @@ use std::collections::HashMap; use std::env; use std::fs; use std::future::Future; +use std::io::BufReader; use std::io::Read; use std::path::PathBuf; use std::pin::Pin; @@ -161,6 +167,80 @@ fn fetch_local(specifier: &ModuleSpecifier) -> Result { }) } +/// Create and populate a root cert store based on the passed options and +/// environment. +pub(crate) fn get_root_cert_store( + maybe_root_path: Option, + maybe_ca_stores: Option>, + maybe_ca_file: Option, +) -> Result { + let mut root_cert_store = RootCertStore::empty(); + let ca_stores: Vec = maybe_ca_stores + .or_else(|| { + let env_ca_store = env::var("DENO_TLS_CA_STORE").ok()?; + Some( + env_ca_store + .split(',') + .map(|s| s.trim().to_string()) + .filter(|s| !s.is_empty()) + .collect(), + ) + }) + .unwrap_or_else(|| vec!["mozilla".to_string()]); + + for store in ca_stores.iter() { + match store.as_str() { + "mozilla" => { + root_cert_store.add_server_trust_anchors( + webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { + rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( + ta.subject, + ta.spki, + ta.name_constraints, + ) + }), + ); + } + "system" => { + let roots = load_native_certs().expect("could not load platform certs"); + for root in roots { + root_cert_store + .add(&rustls::Certificate(root.0)) + .expect("Failed to add platform cert to root cert store"); + } + } + _ => { + return Err(anyhow!("Unknown certificate store \"{}\" specified (allowed: \"system,mozilla\")", store)); + } + } + } + + let ca_file = maybe_ca_file.or_else(|| env::var("DENO_CERT").ok()); + if let Some(ca_file) = ca_file { + let ca_file = if let Some(root) = &maybe_root_path { + root.join(&ca_file) + } else { + PathBuf::from(ca_file) + }; + let certfile = fs::File::open(&ca_file)?; + let mut reader = BufReader::new(certfile); + + match rustls_pemfile::certs(&mut reader) { + Ok(certs) => { + root_cert_store.add_parsable_certificates(&certs); + } + Err(e) => { + return Err(anyhow!( + "Unable to add pem file to certificate store: {}", + e + )); + } + } + } + + Ok(root_cert_store) +} + /// Returns the decoded body and content-type of a provided /// data URL. pub fn get_source_from_data_url( diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs index 562ad78b07..e21bcff2d4 100644 --- a/cli/lsp/cache.rs +++ b/cli/lsp/cache.rs @@ -33,6 +33,9 @@ impl CacheServer { maybe_cache_path: Option, maybe_import_map: Option>, maybe_config_file: Option, + maybe_ca_stores: Option>, + maybe_ca_file: Option, + unsafely_ignore_certificate_errors: Option>, ) -> Self { let (tx, mut rx) = mpsc::unbounded_channel::(); let _join_handle = thread::spawn(move || { @@ -40,6 +43,9 @@ impl CacheServer { runtime.block_on(async { let ps = ProcState::build(Flags { cache_path: maybe_cache_path, + ca_stores: maybe_ca_stores, + ca_file: maybe_ca_file, + unsafely_ignore_certificate_errors, ..Default::default() }) .await diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index 89924a22a8..37558e1b7a 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -154,6 +154,10 @@ pub struct WorkspaceSettings { /// cache/DENO_DIR for the language server. pub cache: Option, + /// Override the default stores used to validate certificates. This overrides + /// the environment variable `DENO_TLS_CA_STORE` if present. + pub certificate_stores: Option>, + /// An option that points to a path string of the config file to apply to /// code within the workspace. pub config: Option, @@ -179,6 +183,15 @@ pub struct WorkspaceSettings { #[serde(default)] pub suggest: CompletionSettings, + /// An option which sets the cert file to use when attempting to fetch remote + /// resources. This overrides `DENO_CERT` if present. + pub tls_certificate: Option, + + /// An option, if set, will unsafely ignore certificate errors when fetching + /// remote resources. + #[serde(default)] + pub unsafely_ignore_certificate_errors: Option>, + #[serde(default)] pub unstable: bool, } @@ -485,6 +498,7 @@ mod tests { WorkspaceSettings { enable: false, cache: None, + certificate_stores: None, config: None, import_map: None, code_lens: CodeLensSettings { @@ -505,6 +519,8 @@ mod tests { hosts: HashMap::new(), } }, + tls_certificate: None, + unsafely_ignore_certificate_errors: None, unstable: false, } ); diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index e33ffc93a2..5ad0c17a63 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -48,7 +48,8 @@ use super::lsp_custom; use super::parent_process_checker; use super::performance::Performance; use super::refactor; -use super::registries; +use super::registries::ModuleRegistry; +use super::registries::ModuleRegistryOptions; use super::text; use super::tsc; use super::tsc::AssetDocument; @@ -96,7 +97,7 @@ pub(crate) struct Inner { /// on disk or "open" within the client. pub(crate) documents: Documents, /// Handles module registries, which allow discovery of modules - module_registries: registries::ModuleRegistry, + module_registries: ModuleRegistry, /// The path to the module registries cache module_registries_location: PathBuf, /// An optional path to the DENO_DIR which has been specified in the client @@ -139,8 +140,11 @@ impl Inner { let dir = deno_dir::DenoDir::new(maybe_custom_root) .expect("could not access DENO_DIR"); let module_registries_location = dir.root.join(REGISTRIES_PATH); - let module_registries = - registries::ModuleRegistry::new(&module_registries_location); + let module_registries = ModuleRegistry::new( + &module_registries_location, + ModuleRegistryOptions::default(), + ) + .expect("could not create module registries"); let location = dir.root.join(CACHE_PATH); let documents = Documents::new(&location); let performance = Arc::new(Performance::default()); @@ -425,11 +429,23 @@ impl Inner { let maybe_custom_root = maybe_cache_path .clone() .or_else(|| env::var("DENO_DIR").map(String::into).ok()); - let dir = deno_dir::DenoDir::new(maybe_custom_root) - .expect("could not access DENO_DIR"); + let dir = deno_dir::DenoDir::new(maybe_custom_root)?; let module_registries_location = dir.root.join(REGISTRIES_PATH); - self.module_registries = - registries::ModuleRegistry::new(&module_registries_location); + let workspace_settings = self.config.get_workspace_settings(); + let maybe_root_path = self + .root_uri + .as_ref() + .and_then(|uri| fs_util::specifier_to_file_path(uri).ok()); + self.module_registries = ModuleRegistry::new( + &module_registries_location, + ModuleRegistryOptions { + maybe_root_path, + maybe_ca_stores: workspace_settings.certificate_stores.clone(), + maybe_ca_file: workspace_settings.tls_certificate.clone(), + unsafely_ignore_certificate_errors: workspace_settings + .unsafely_ignore_certificate_errors, + }, + )?; self.module_registries_location = module_registries_location; self.documents.set_location(dir.root.join(CACHE_PATH)); self.maybe_cache_path = maybe_cache_path; @@ -496,14 +512,23 @@ impl Inner { async fn update_registries(&mut self) -> Result<(), AnyError> { let mark = self.performance.mark("update_registries", None::<()>); - for (registry, enabled) in self - .config - .get_workspace_settings() - .suggest - .imports - .hosts - .iter() - { + let workspace_settings = self.config.get_workspace_settings(); + let maybe_root_path = self + .root_uri + .as_ref() + .and_then(|uri| fs_util::specifier_to_file_path(uri).ok()); + self.module_registries = ModuleRegistry::new( + &self.module_registries_location, + ModuleRegistryOptions { + maybe_root_path, + maybe_ca_stores: workspace_settings.certificate_stores.clone(), + maybe_ca_file: workspace_settings.tls_certificate.clone(), + unsafely_ignore_certificate_errors: workspace_settings + .unsafely_ignore_certificate_errors + .clone(), + }, + )?; + for (registry, enabled) in workspace_settings.suggest.imports.hosts.iter() { if *enabled { lsp_log!("Enabling import suggestions for: {}", registry); self.module_registries.enable(registry).await?; @@ -2583,6 +2608,9 @@ impl Inner { self.maybe_cache_path.clone(), self.maybe_import_map.clone(), self.maybe_config_file.clone(), + None, + None, + None, ) .await, ); @@ -2616,8 +2644,6 @@ impl Inner { error!("Unable to remove registries cache: {}", err); LspError::internal_error() })?; - self.module_registries = - registries::ModuleRegistry::new(&self.module_registries_location); self.update_registries().await.map_err(|err| { error!("Unable to update registries: {}", err); LspError::internal_error() diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs index 418c19fe33..0b8a4240be 100644 --- a/cli/lsp/registries.rs +++ b/cli/lsp/registries.rs @@ -12,6 +12,7 @@ use super::path_to_regex::StringOrVec; use super::path_to_regex::Token; use crate::deno_dir; +use crate::file_fetcher::get_root_cert_store; use crate::file_fetcher::CacheSetting; use crate::file_fetcher::FileFetcher; use crate::http_cache::HttpCache; @@ -37,6 +38,7 @@ use once_cell::sync::Lazy; use regex::Regex; use std::collections::HashMap; use std::path::Path; +use std::path::PathBuf; const CONFIG_PATH: &str = "/.well-known/deno-import-intellisense.json"; const COMPONENT: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS @@ -406,11 +408,19 @@ enum VariableItems { List(VariableItemsList), } +#[derive(Debug, Default)] +pub(crate) struct ModuleRegistryOptions { + pub maybe_root_path: Option, + pub maybe_ca_stores: Option>, + pub maybe_ca_file: Option, + pub unsafely_ignore_certificate_errors: Option>, +} + /// A structure which holds the information about currently configured module /// registries and can provide completion information for URLs that match /// one of the enabled registries. #[derive(Debug, Clone)] -pub struct ModuleRegistry { +pub(crate) struct ModuleRegistry { origins: HashMap>, file_fetcher: FileFetcher, } @@ -422,29 +432,35 @@ impl Default for ModuleRegistry { // custom root. let dir = deno_dir::DenoDir::new(None).unwrap(); let location = dir.root.join("registries"); - Self::new(&location) + Self::new(&location, ModuleRegistryOptions::default()).unwrap() } } impl ModuleRegistry { - pub fn new(location: &Path) -> Self { + pub fn new( + location: &Path, + options: ModuleRegistryOptions, + ) -> Result { let http_cache = HttpCache::new(location); + let root_cert_store = Some(get_root_cert_store( + options.maybe_root_path, + options.maybe_ca_stores, + options.maybe_ca_file, + )?); let mut file_fetcher = FileFetcher::new( http_cache, CacheSetting::RespectHeaders, true, - None, + root_cert_store, BlobStore::default(), - None, - ) - .context("Error creating file fetcher in module registry.") - .unwrap(); + options.unsafely_ignore_certificate_errors, + )?; file_fetcher.set_download_log_level(super::logging::lsp_log_level()); - Self { + Ok(Self { origins: HashMap::new(), file_fetcher, - } + }) } fn complete_literal( @@ -1200,7 +1216,8 @@ mod tests { let _g = test_util::http_server(); let temp_dir = TempDir::new().expect("could not create tmp"); let location = temp_dir.path().join("registries"); - let mut module_registry = ModuleRegistry::new(&location); + let mut module_registry = + ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); module_registry .enable("http://localhost:4545/") .await @@ -1260,7 +1277,8 @@ mod tests { let _g = test_util::http_server(); let temp_dir = TempDir::new().expect("could not create tmp"); let location = temp_dir.path().join("registries"); - let mut module_registry = ModuleRegistry::new(&location); + let mut module_registry = + ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); module_registry .enable("http://localhost:4545/") .await @@ -1482,7 +1500,8 @@ mod tests { let _g = test_util::http_server(); let temp_dir = TempDir::new().expect("could not create tmp"); let location = temp_dir.path().join("registries"); - let mut module_registry = ModuleRegistry::new(&location); + let mut module_registry = + ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); module_registry .enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-key-first.json") .await @@ -1551,7 +1570,8 @@ mod tests { let _g = test_util::http_server(); let temp_dir = TempDir::new().expect("could not create tmp"); let location = temp_dir.path().join("registries"); - let mut module_registry = ModuleRegistry::new(&location); + let mut module_registry = + ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); module_registry .enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-complex.json") .await @@ -1601,7 +1621,8 @@ mod tests { let _g = test_util::http_server(); let temp_dir = TempDir::new().expect("could not create tmp"); let location = temp_dir.path().join("registries"); - let module_registry = ModuleRegistry::new(&location); + let module_registry = + ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); let result = module_registry.check_origin("http://localhost:4545").await; assert!(result.is_ok()); } @@ -1611,7 +1632,8 @@ mod tests { let _g = test_util::http_server(); let temp_dir = TempDir::new().expect("could not create tmp"); let location = temp_dir.path().join("registries"); - let module_registry = ModuleRegistry::new(&location); + let module_registry = + ModuleRegistry::new(&location, ModuleRegistryOptions::default()).unwrap(); let result = module_registry.check_origin("https://deno.com").await; assert!(result.is_err()); let err = result.unwrap_err().to_string(); diff --git a/cli/lsp/repl.rs b/cli/lsp/repl.rs index b4bc1daf7e..0a26032983 100644 --- a/cli/lsp/repl.rs +++ b/cli/lsp/repl.rs @@ -277,11 +277,14 @@ pub fn get_repl_workspace_settings() -> WorkspaceSettings { WorkspaceSettings { enable: true, config: None, + certificate_stores: None, cache: None, import_map: None, code_lens: Default::default(), internal_debug: false, lint: false, + tls_certificate: None, + unsafely_ignore_certificate_errors: None, unstable: false, suggest: CompletionSettings { complete_function_calls: false, diff --git a/cli/proc_state.rs b/cli/proc_state.rs index 13a17b8fbd..4264e32e3e 100644 --- a/cli/proc_state.rs +++ b/cli/proc_state.rs @@ -8,6 +8,7 @@ use crate::config_file::ConfigFile; use crate::config_file::MaybeImportsResult; use crate::deno_dir; use crate::emit; +use crate::file_fetcher::get_root_cert_store; use crate::file_fetcher::CacheSetting; use crate::file_fetcher::FileFetcher; use crate::flags; @@ -42,11 +43,7 @@ use deno_graph::source::LoadFuture; use deno_graph::source::Loader; use deno_graph::MediaType; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; -use deno_runtime::deno_tls::rustls; use deno_runtime::deno_tls::rustls::RootCertStore; -use deno_runtime::deno_tls::rustls_native_certs::load_native_certs; -use deno_runtime::deno_tls::rustls_pemfile; -use deno_runtime::deno_tls::webpki_roots; use deno_runtime::deno_web::BlobStore; use deno_runtime::inspector_server::InspectorServer; use deno_runtime::permissions::Permissions; @@ -54,8 +51,6 @@ use import_map::ImportMap; use log::warn; use std::collections::HashSet; use std::env; -use std::fs::File; -use std::io::BufReader; use std::ops::Deref; use std::sync::Arc; @@ -101,67 +96,11 @@ impl ProcState { let deps_cache_location = dir.root.join("deps"); let http_cache = http_cache::HttpCache::new(&deps_cache_location); - let mut root_cert_store = RootCertStore::empty(); - let ca_stores: Vec = flags - .ca_stores - .clone() - .or_else(|| { - let env_ca_store = env::var("DENO_TLS_CA_STORE").ok()?; - Some( - env_ca_store - .split(',') - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) - .collect(), - ) - }) - .unwrap_or_else(|| vec!["mozilla".to_string()]); - - for store in ca_stores.iter() { - match store.as_str() { - "mozilla" => { - root_cert_store.add_server_trust_anchors( - webpki_roots::TLS_SERVER_ROOTS.0.iter().map(|ta| { - rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - }), - ); - } - "system" => { - let roots = - load_native_certs().expect("could not load platform certs"); - for root in roots { - root_cert_store - .add(&rustls::Certificate(root.0)) - .expect("Failed to add platform cert to root cert store"); - } - } - _ => { - return Err(anyhow!("Unknown certificate store \"{}\" specified (allowed: \"system,mozilla\")", store)); - } - } - } - - let ca_file = flags.ca_file.clone().or_else(|| env::var("DENO_CERT").ok()); - if let Some(ca_file) = ca_file { - let certfile = File::open(&ca_file)?; - let mut reader = BufReader::new(certfile); - - match rustls_pemfile::certs(&mut reader) { - Ok(certs) => { - root_cert_store.add_parsable_certificates(&certs); - } - Err(e) => { - return Err(anyhow!( - "Unable to add pem file to certificate store: {}", - e - )); - } - } - } + let root_cert_store = get_root_cert_store( + None, + flags.ca_stores.clone(), + flags.ca_file.clone(), + )?; if let Some(insecure_allowlist) = flags.unsafely_ignore_certificate_errors.as_ref() diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs index c992ca4cb3..e831e8627b 100644 --- a/cli/tests/integration/lsp_tests.rs +++ b/cli/tests/integration/lsp_tests.rs @@ -3216,6 +3216,124 @@ fn lsp_cache_location() { shutdown(&mut client); } +/// Sets the TLS root certificate on startup, which allows the LSP to connect to +/// the custom signed test server and be able to retrieve the registry config +/// and cache files. +#[test] +fn lsp_tls_cert() { + let _g = http_server(); + let mut params: lsp::InitializeParams = + serde_json::from_value(load_fixture("initialize_params_tls_cert.json")) + .unwrap(); + + params.root_uri = Some(Url::from_file_path(testdata_path()).unwrap()); + + let deno_exe = deno_exe_path(); + let mut client = LspClient::new(&deno_exe).unwrap(); + client + .write_request::<_, _, Value>("initialize", params) + .unwrap(); + + client.write_notification("initialized", json!({})).unwrap(); + did_open( + &mut client, + json!({ + "textDocument": { + "uri": "file:///a/file_01.ts", + "languageId": "typescript", + "version": 1, + "text": "export const a = \"a\";\n", + } + }), + ); + let diagnostics = + did_open(&mut client, load_fixture("did_open_params_tls_cert.json")); + let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics); + assert_eq!(diagnostics.count(), 14); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "deno/cache", + json!({ + "referrer": { + "uri": "file:///a/file.ts", + }, + "uris": [], + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert!(maybe_res.is_some()); + let (maybe_res, maybe_err) = client + .write_request( + "textDocument/hover", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + }, + "position": { + "line": 0, + "character": 28 + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "contents": { + "kind": "markdown", + "value": "**Resolved Dependency**\n\n**Code**: https​://localhost:5545/xTypeScriptTypes.js\n" + }, + "range": { + "start": { + "line": 0, + "character": 19 + }, + "end":{ + "line": 0, + "character": 63 + } + } + })) + ); + let (maybe_res, maybe_err) = client + .write_request::<_, _, Value>( + "textDocument/hover", + json!({ + "textDocument": { + "uri": "file:///a/file.ts", + }, + "position": { + "line": 7, + "character": 28 + } + }), + ) + .unwrap(); + assert!(maybe_err.is_none()); + assert_eq!( + maybe_res, + Some(json!({ + "contents": { + "kind": "markdown", + "value": "**Resolved Dependency**\n\n**Code**: http​://localhost:4545/x/a/mod.ts\n\n\n---\n\n**a**\n\nmod.ts" + }, + "range": { + "start": { + "line": 7, + "character": 19 + }, + "end": { + "line": 7, + "character": 53 + } + } + })) + ); + shutdown(&mut client); +} + #[test] fn lsp_diagnostics_warn() { let _g = http_server(); diff --git a/cli/tests/testdata/lsp/did_open_params_tls_cert.json b/cli/tests/testdata/lsp/did_open_params_tls_cert.json new file mode 100644 index 0000000000..2942bfcbeb --- /dev/null +++ b/cli/tests/testdata/lsp/did_open_params_tls_cert.json @@ -0,0 +1,8 @@ +{ + "textDocument": { + "uri": "file:///a/file.ts", + "languageId": "typescript", + "version": 1, + "text": "import * as a from \"https://localhost:5545/xTypeScriptTypes.js\";\n// @deno-types=\"https://localhost:5545/type_definitions/foo.d.ts\"\nimport * as b from \"https://localhost:5545/type_definitions/foo.js\";\nimport * as c from \"https://localhost:5545/subdir/type_reference.js\";\nimport * as d from \"https://localhost:5545/subdir/mod1.ts\";\nimport * as e from \"data:application/typescript;base64,ZXhwb3J0IGNvbnN0IGEgPSAiYSI7CgpleHBvcnQgZW51bSBBIHsKICBBLAogIEIsCiAgQywKfQo=\";\nimport * as f from \"./file_01.ts\";\nimport * as g from \"http://localhost:4545/x/a/mod.ts\";\n\nconsole.log(a, b, c, d, e, f, g);\n" + } +} diff --git a/cli/tests/testdata/lsp/initialize_params.json b/cli/tests/testdata/lsp/initialize_params.json index 3b99be4f0a..9fd197fe41 100644 --- a/cli/tests/testdata/lsp/initialize_params.json +++ b/cli/tests/testdata/lsp/initialize_params.json @@ -8,6 +8,7 @@ "initializationOptions": { "enable": true, "cache": null, + "certificateStores": null, "codeLens": { "implementations": true, "references": true, @@ -25,6 +26,8 @@ "hosts": {} } }, + "tlsCertificate": null, + "unsafelyIgnoreCertificateErrors": null, "unstable": false }, "capabilities": { diff --git a/cli/tests/testdata/lsp/initialize_params_tls_cert.json b/cli/tests/testdata/lsp/initialize_params_tls_cert.json new file mode 100644 index 0000000000..e231bf25fa --- /dev/null +++ b/cli/tests/testdata/lsp/initialize_params_tls_cert.json @@ -0,0 +1,71 @@ +{ + "processId": 0, + "clientInfo": { + "name": "test-harness", + "version": "1.0.0" + }, + "rootUri": null, + "initializationOptions": { + "enable": true, + "cache": null, + "certificateStores": null, + "codeLens": { + "implementations": true, + "references": true, + "test": true + }, + "config": "", + "importMap": null, + "lint": true, + "suggest": { + "autoImports": true, + "completeFunctionCalls": false, + "names": true, + "paths": true, + "imports": { + "hosts": { + "https://localhost:5545": true, + "http://localhost:4545": true + } + } + }, + "tlsCertificate": "tls/RootCA.pem", + "unsafelyIgnoreCertificateErrors": null, + "unstable": false + }, + "capabilities": { + "textDocument": { + "codeAction": { + "codeActionLiteralSupport": { + "codeActionKind": { + "valueSet": [ + "quickfix", + "refactor" + ] + } + }, + "isPreferredSupport": true, + "dataSupport": true, + "disabledSupport": true, + "resolveSupport": { + "properties": [ + "edit" + ] + } + }, + "foldingRange": { + "lineFoldingOnly": true + }, + "synchronization": { + "dynamicRegistration": true, + "willSave": true, + "willSaveWaitUntil": true, + "didSave": true + } + }, + "workspace": { + "configuration": true, + "workspaceFolders": true + } + } +}