mirror of
https://github.com/denoland/deno.git
synced 2024-12-24 08:09:08 -05:00
fix(lsp): respect DENO_CERT and other options related to TLS certs (#13467)
Fixes #13437
This commit is contained in:
parent
ac8bdb1294
commit
47bdf164ee
11 changed files with 393 additions and 101 deletions
|
@ -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<File, AnyError> {
|
|||
})
|
||||
}
|
||||
|
||||
/// 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<PathBuf>,
|
||||
maybe_ca_stores: Option<Vec<String>>,
|
||||
maybe_ca_file: Option<String>,
|
||||
) -> Result<RootCertStore, AnyError> {
|
||||
let mut root_cert_store = RootCertStore::empty();
|
||||
let ca_stores: Vec<String> = 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(
|
||||
|
|
|
@ -33,6 +33,9 @@ impl CacheServer {
|
|||
maybe_cache_path: Option<PathBuf>,
|
||||
maybe_import_map: Option<Arc<ImportMap>>,
|
||||
maybe_config_file: Option<ConfigFile>,
|
||||
maybe_ca_stores: Option<Vec<String>>,
|
||||
maybe_ca_file: Option<String>,
|
||||
unsafely_ignore_certificate_errors: Option<Vec<String>>,
|
||||
) -> Self {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<Request>();
|
||||
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
|
||||
|
|
|
@ -154,6 +154,10 @@ pub struct WorkspaceSettings {
|
|||
/// cache/DENO_DIR for the language server.
|
||||
pub cache: Option<String>,
|
||||
|
||||
/// Override the default stores used to validate certificates. This overrides
|
||||
/// the environment variable `DENO_TLS_CA_STORE` if present.
|
||||
pub certificate_stores: Option<Vec<String>>,
|
||||
|
||||
/// An option that points to a path string of the config file to apply to
|
||||
/// code within the workspace.
|
||||
pub config: Option<String>,
|
||||
|
@ -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<String>,
|
||||
|
||||
/// An option, if set, will unsafely ignore certificate errors when fetching
|
||||
/// remote resources.
|
||||
#[serde(default)]
|
||||
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
|
||||
|
||||
#[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,
|
||||
}
|
||||
);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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<PathBuf>,
|
||||
pub maybe_ca_stores: Option<Vec<String>>,
|
||||
pub maybe_ca_file: Option<String>,
|
||||
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
/// 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<String, Vec<RegistryConfiguration>>,
|
||||
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<Self, AnyError> {
|
||||
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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<String> = 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()
|
||||
|
|
|
@ -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();
|
||||
|
|
8
cli/tests/testdata/lsp/did_open_params_tls_cert.json
vendored
Normal file
8
cli/tests/testdata/lsp/did_open_params_tls_cert.json
vendored
Normal file
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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": {
|
||||
|
|
71
cli/tests/testdata/lsp/initialize_params_tls_cert.json
vendored
Normal file
71
cli/tests/testdata/lsp/initialize_params_tls_cert.json
vendored
Normal file
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue