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 data_url::DataUrl;
|
||||||
use deno_ast::MediaType;
|
use deno_ast::MediaType;
|
||||||
|
use deno_core::anyhow::anyhow;
|
||||||
use deno_core::error::custom_error;
|
use deno_core::error::custom_error;
|
||||||
use deno_core::error::generic_error;
|
use deno_core::error::generic_error;
|
||||||
use deno_core::error::uri_error;
|
use deno_core::error::uri_error;
|
||||||
|
@ -22,7 +23,11 @@ use deno_core::parking_lot::Mutex;
|
||||||
use deno_core::ModuleSpecifier;
|
use deno_core::ModuleSpecifier;
|
||||||
use deno_runtime::deno_fetch::create_http_client;
|
use deno_runtime::deno_fetch::create_http_client;
|
||||||
use deno_runtime::deno_fetch::reqwest;
|
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::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::deno_web::BlobStore;
|
||||||
use deno_runtime::permissions::Permissions;
|
use deno_runtime::permissions::Permissions;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
@ -31,6 +36,7 @@ use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
|
use std::io::BufReader;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::pin::Pin;
|
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
|
/// Returns the decoded body and content-type of a provided
|
||||||
/// data URL.
|
/// data URL.
|
||||||
pub fn get_source_from_data_url(
|
pub fn get_source_from_data_url(
|
||||||
|
|
|
@ -33,6 +33,9 @@ impl CacheServer {
|
||||||
maybe_cache_path: Option<PathBuf>,
|
maybe_cache_path: Option<PathBuf>,
|
||||||
maybe_import_map: Option<Arc<ImportMap>>,
|
maybe_import_map: Option<Arc<ImportMap>>,
|
||||||
maybe_config_file: Option<ConfigFile>,
|
maybe_config_file: Option<ConfigFile>,
|
||||||
|
maybe_ca_stores: Option<Vec<String>>,
|
||||||
|
maybe_ca_file: Option<String>,
|
||||||
|
unsafely_ignore_certificate_errors: Option<Vec<String>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (tx, mut rx) = mpsc::unbounded_channel::<Request>();
|
let (tx, mut rx) = mpsc::unbounded_channel::<Request>();
|
||||||
let _join_handle = thread::spawn(move || {
|
let _join_handle = thread::spawn(move || {
|
||||||
|
@ -40,6 +43,9 @@ impl CacheServer {
|
||||||
runtime.block_on(async {
|
runtime.block_on(async {
|
||||||
let ps = ProcState::build(Flags {
|
let ps = ProcState::build(Flags {
|
||||||
cache_path: maybe_cache_path,
|
cache_path: maybe_cache_path,
|
||||||
|
ca_stores: maybe_ca_stores,
|
||||||
|
ca_file: maybe_ca_file,
|
||||||
|
unsafely_ignore_certificate_errors,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -154,6 +154,10 @@ pub struct WorkspaceSettings {
|
||||||
/// cache/DENO_DIR for the language server.
|
/// cache/DENO_DIR for the language server.
|
||||||
pub cache: Option<String>,
|
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
|
/// An option that points to a path string of the config file to apply to
|
||||||
/// code within the workspace.
|
/// code within the workspace.
|
||||||
pub config: Option<String>,
|
pub config: Option<String>,
|
||||||
|
@ -179,6 +183,15 @@ pub struct WorkspaceSettings {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub suggest: CompletionSettings,
|
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)]
|
#[serde(default)]
|
||||||
pub unstable: bool,
|
pub unstable: bool,
|
||||||
}
|
}
|
||||||
|
@ -485,6 +498,7 @@ mod tests {
|
||||||
WorkspaceSettings {
|
WorkspaceSettings {
|
||||||
enable: false,
|
enable: false,
|
||||||
cache: None,
|
cache: None,
|
||||||
|
certificate_stores: None,
|
||||||
config: None,
|
config: None,
|
||||||
import_map: None,
|
import_map: None,
|
||||||
code_lens: CodeLensSettings {
|
code_lens: CodeLensSettings {
|
||||||
|
@ -505,6 +519,8 @@ mod tests {
|
||||||
hosts: HashMap::new(),
|
hosts: HashMap::new(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
tls_certificate: None,
|
||||||
|
unsafely_ignore_certificate_errors: None,
|
||||||
unstable: false,
|
unstable: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -48,7 +48,8 @@ use super::lsp_custom;
|
||||||
use super::parent_process_checker;
|
use super::parent_process_checker;
|
||||||
use super::performance::Performance;
|
use super::performance::Performance;
|
||||||
use super::refactor;
|
use super::refactor;
|
||||||
use super::registries;
|
use super::registries::ModuleRegistry;
|
||||||
|
use super::registries::ModuleRegistryOptions;
|
||||||
use super::text;
|
use super::text;
|
||||||
use super::tsc;
|
use super::tsc;
|
||||||
use super::tsc::AssetDocument;
|
use super::tsc::AssetDocument;
|
||||||
|
@ -96,7 +97,7 @@ pub(crate) struct Inner {
|
||||||
/// on disk or "open" within the client.
|
/// on disk or "open" within the client.
|
||||||
pub(crate) documents: Documents,
|
pub(crate) documents: Documents,
|
||||||
/// Handles module registries, which allow discovery of modules
|
/// Handles module registries, which allow discovery of modules
|
||||||
module_registries: registries::ModuleRegistry,
|
module_registries: ModuleRegistry,
|
||||||
/// The path to the module registries cache
|
/// The path to the module registries cache
|
||||||
module_registries_location: PathBuf,
|
module_registries_location: PathBuf,
|
||||||
/// An optional path to the DENO_DIR which has been specified in the client
|
/// 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)
|
let dir = deno_dir::DenoDir::new(maybe_custom_root)
|
||||||
.expect("could not access DENO_DIR");
|
.expect("could not access DENO_DIR");
|
||||||
let module_registries_location = dir.root.join(REGISTRIES_PATH);
|
let module_registries_location = dir.root.join(REGISTRIES_PATH);
|
||||||
let module_registries =
|
let module_registries = ModuleRegistry::new(
|
||||||
registries::ModuleRegistry::new(&module_registries_location);
|
&module_registries_location,
|
||||||
|
ModuleRegistryOptions::default(),
|
||||||
|
)
|
||||||
|
.expect("could not create module registries");
|
||||||
let location = dir.root.join(CACHE_PATH);
|
let location = dir.root.join(CACHE_PATH);
|
||||||
let documents = Documents::new(&location);
|
let documents = Documents::new(&location);
|
||||||
let performance = Arc::new(Performance::default());
|
let performance = Arc::new(Performance::default());
|
||||||
|
@ -425,11 +429,23 @@ impl Inner {
|
||||||
let maybe_custom_root = maybe_cache_path
|
let maybe_custom_root = maybe_cache_path
|
||||||
.clone()
|
.clone()
|
||||||
.or_else(|| env::var("DENO_DIR").map(String::into).ok());
|
.or_else(|| env::var("DENO_DIR").map(String::into).ok());
|
||||||
let dir = deno_dir::DenoDir::new(maybe_custom_root)
|
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_location = dir.root.join(REGISTRIES_PATH);
|
||||||
self.module_registries =
|
let workspace_settings = self.config.get_workspace_settings();
|
||||||
registries::ModuleRegistry::new(&module_registries_location);
|
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.module_registries_location = module_registries_location;
|
||||||
self.documents.set_location(dir.root.join(CACHE_PATH));
|
self.documents.set_location(dir.root.join(CACHE_PATH));
|
||||||
self.maybe_cache_path = maybe_cache_path;
|
self.maybe_cache_path = maybe_cache_path;
|
||||||
|
@ -496,14 +512,23 @@ impl Inner {
|
||||||
|
|
||||||
async fn update_registries(&mut self) -> Result<(), AnyError> {
|
async fn update_registries(&mut self) -> Result<(), AnyError> {
|
||||||
let mark = self.performance.mark("update_registries", None::<()>);
|
let mark = self.performance.mark("update_registries", None::<()>);
|
||||||
for (registry, enabled) in self
|
let workspace_settings = self.config.get_workspace_settings();
|
||||||
.config
|
let maybe_root_path = self
|
||||||
.get_workspace_settings()
|
.root_uri
|
||||||
.suggest
|
.as_ref()
|
||||||
.imports
|
.and_then(|uri| fs_util::specifier_to_file_path(uri).ok());
|
||||||
.hosts
|
self.module_registries = ModuleRegistry::new(
|
||||||
.iter()
|
&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 {
|
if *enabled {
|
||||||
lsp_log!("Enabling import suggestions for: {}", registry);
|
lsp_log!("Enabling import suggestions for: {}", registry);
|
||||||
self.module_registries.enable(registry).await?;
|
self.module_registries.enable(registry).await?;
|
||||||
|
@ -2583,6 +2608,9 @@ impl Inner {
|
||||||
self.maybe_cache_path.clone(),
|
self.maybe_cache_path.clone(),
|
||||||
self.maybe_import_map.clone(),
|
self.maybe_import_map.clone(),
|
||||||
self.maybe_config_file.clone(),
|
self.maybe_config_file.clone(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
);
|
);
|
||||||
|
@ -2616,8 +2644,6 @@ impl Inner {
|
||||||
error!("Unable to remove registries cache: {}", err);
|
error!("Unable to remove registries cache: {}", err);
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
})?;
|
})?;
|
||||||
self.module_registries =
|
|
||||||
registries::ModuleRegistry::new(&self.module_registries_location);
|
|
||||||
self.update_registries().await.map_err(|err| {
|
self.update_registries().await.map_err(|err| {
|
||||||
error!("Unable to update registries: {}", err);
|
error!("Unable to update registries: {}", err);
|
||||||
LspError::internal_error()
|
LspError::internal_error()
|
||||||
|
|
|
@ -12,6 +12,7 @@ use super::path_to_regex::StringOrVec;
|
||||||
use super::path_to_regex::Token;
|
use super::path_to_regex::Token;
|
||||||
|
|
||||||
use crate::deno_dir;
|
use crate::deno_dir;
|
||||||
|
use crate::file_fetcher::get_root_cert_store;
|
||||||
use crate::file_fetcher::CacheSetting;
|
use crate::file_fetcher::CacheSetting;
|
||||||
use crate::file_fetcher::FileFetcher;
|
use crate::file_fetcher::FileFetcher;
|
||||||
use crate::http_cache::HttpCache;
|
use crate::http_cache::HttpCache;
|
||||||
|
@ -37,6 +38,7 @@ use once_cell::sync::Lazy;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
const CONFIG_PATH: &str = "/.well-known/deno-import-intellisense.json";
|
const CONFIG_PATH: &str = "/.well-known/deno-import-intellisense.json";
|
||||||
const COMPONENT: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
|
const COMPONENT: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
|
||||||
|
@ -406,11 +408,19 @@ enum VariableItems {
|
||||||
List(VariableItemsList),
|
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
|
/// A structure which holds the information about currently configured module
|
||||||
/// registries and can provide completion information for URLs that match
|
/// registries and can provide completion information for URLs that match
|
||||||
/// one of the enabled registries.
|
/// one of the enabled registries.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ModuleRegistry {
|
pub(crate) struct ModuleRegistry {
|
||||||
origins: HashMap<String, Vec<RegistryConfiguration>>,
|
origins: HashMap<String, Vec<RegistryConfiguration>>,
|
||||||
file_fetcher: FileFetcher,
|
file_fetcher: FileFetcher,
|
||||||
}
|
}
|
||||||
|
@ -422,29 +432,35 @@ impl Default for ModuleRegistry {
|
||||||
// custom root.
|
// custom root.
|
||||||
let dir = deno_dir::DenoDir::new(None).unwrap();
|
let dir = deno_dir::DenoDir::new(None).unwrap();
|
||||||
let location = dir.root.join("registries");
|
let location = dir.root.join("registries");
|
||||||
Self::new(&location)
|
Self::new(&location, ModuleRegistryOptions::default()).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleRegistry {
|
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 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(
|
let mut file_fetcher = FileFetcher::new(
|
||||||
http_cache,
|
http_cache,
|
||||||
CacheSetting::RespectHeaders,
|
CacheSetting::RespectHeaders,
|
||||||
true,
|
true,
|
||||||
None,
|
root_cert_store,
|
||||||
BlobStore::default(),
|
BlobStore::default(),
|
||||||
None,
|
options.unsafely_ignore_certificate_errors,
|
||||||
)
|
)?;
|
||||||
.context("Error creating file fetcher in module registry.")
|
|
||||||
.unwrap();
|
|
||||||
file_fetcher.set_download_log_level(super::logging::lsp_log_level());
|
file_fetcher.set_download_log_level(super::logging::lsp_log_level());
|
||||||
|
|
||||||
Self {
|
Ok(Self {
|
||||||
origins: HashMap::new(),
|
origins: HashMap::new(),
|
||||||
file_fetcher,
|
file_fetcher,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete_literal(
|
fn complete_literal(
|
||||||
|
@ -1200,7 +1216,8 @@ mod tests {
|
||||||
let _g = test_util::http_server();
|
let _g = test_util::http_server();
|
||||||
let temp_dir = TempDir::new().expect("could not create tmp");
|
let temp_dir = TempDir::new().expect("could not create tmp");
|
||||||
let location = temp_dir.path().join("registries");
|
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
|
module_registry
|
||||||
.enable("http://localhost:4545/")
|
.enable("http://localhost:4545/")
|
||||||
.await
|
.await
|
||||||
|
@ -1260,7 +1277,8 @@ mod tests {
|
||||||
let _g = test_util::http_server();
|
let _g = test_util::http_server();
|
||||||
let temp_dir = TempDir::new().expect("could not create tmp");
|
let temp_dir = TempDir::new().expect("could not create tmp");
|
||||||
let location = temp_dir.path().join("registries");
|
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
|
module_registry
|
||||||
.enable("http://localhost:4545/")
|
.enable("http://localhost:4545/")
|
||||||
.await
|
.await
|
||||||
|
@ -1482,7 +1500,8 @@ mod tests {
|
||||||
let _g = test_util::http_server();
|
let _g = test_util::http_server();
|
||||||
let temp_dir = TempDir::new().expect("could not create tmp");
|
let temp_dir = TempDir::new().expect("could not create tmp");
|
||||||
let location = temp_dir.path().join("registries");
|
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
|
module_registry
|
||||||
.enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-key-first.json")
|
.enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-key-first.json")
|
||||||
.await
|
.await
|
||||||
|
@ -1551,7 +1570,8 @@ mod tests {
|
||||||
let _g = test_util::http_server();
|
let _g = test_util::http_server();
|
||||||
let temp_dir = TempDir::new().expect("could not create tmp");
|
let temp_dir = TempDir::new().expect("could not create tmp");
|
||||||
let location = temp_dir.path().join("registries");
|
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
|
module_registry
|
||||||
.enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-complex.json")
|
.enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-complex.json")
|
||||||
.await
|
.await
|
||||||
|
@ -1601,7 +1621,8 @@ mod tests {
|
||||||
let _g = test_util::http_server();
|
let _g = test_util::http_server();
|
||||||
let temp_dir = TempDir::new().expect("could not create tmp");
|
let temp_dir = TempDir::new().expect("could not create tmp");
|
||||||
let location = temp_dir.path().join("registries");
|
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;
|
let result = module_registry.check_origin("http://localhost:4545").await;
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
@ -1611,7 +1632,8 @@ mod tests {
|
||||||
let _g = test_util::http_server();
|
let _g = test_util::http_server();
|
||||||
let temp_dir = TempDir::new().expect("could not create tmp");
|
let temp_dir = TempDir::new().expect("could not create tmp");
|
||||||
let location = temp_dir.path().join("registries");
|
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;
|
let result = module_registry.check_origin("https://deno.com").await;
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
let err = result.unwrap_err().to_string();
|
let err = result.unwrap_err().to_string();
|
||||||
|
|
|
@ -277,11 +277,14 @@ pub fn get_repl_workspace_settings() -> WorkspaceSettings {
|
||||||
WorkspaceSettings {
|
WorkspaceSettings {
|
||||||
enable: true,
|
enable: true,
|
||||||
config: None,
|
config: None,
|
||||||
|
certificate_stores: None,
|
||||||
cache: None,
|
cache: None,
|
||||||
import_map: None,
|
import_map: None,
|
||||||
code_lens: Default::default(),
|
code_lens: Default::default(),
|
||||||
internal_debug: false,
|
internal_debug: false,
|
||||||
lint: false,
|
lint: false,
|
||||||
|
tls_certificate: None,
|
||||||
|
unsafely_ignore_certificate_errors: None,
|
||||||
unstable: false,
|
unstable: false,
|
||||||
suggest: CompletionSettings {
|
suggest: CompletionSettings {
|
||||||
complete_function_calls: false,
|
complete_function_calls: false,
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::config_file::ConfigFile;
|
||||||
use crate::config_file::MaybeImportsResult;
|
use crate::config_file::MaybeImportsResult;
|
||||||
use crate::deno_dir;
|
use crate::deno_dir;
|
||||||
use crate::emit;
|
use crate::emit;
|
||||||
|
use crate::file_fetcher::get_root_cert_store;
|
||||||
use crate::file_fetcher::CacheSetting;
|
use crate::file_fetcher::CacheSetting;
|
||||||
use crate::file_fetcher::FileFetcher;
|
use crate::file_fetcher::FileFetcher;
|
||||||
use crate::flags;
|
use crate::flags;
|
||||||
|
@ -42,11 +43,7 @@ use deno_graph::source::LoadFuture;
|
||||||
use deno_graph::source::Loader;
|
use deno_graph::source::Loader;
|
||||||
use deno_graph::MediaType;
|
use deno_graph::MediaType;
|
||||||
use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
|
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::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::deno_web::BlobStore;
|
||||||
use deno_runtime::inspector_server::InspectorServer;
|
use deno_runtime::inspector_server::InspectorServer;
|
||||||
use deno_runtime::permissions::Permissions;
|
use deno_runtime::permissions::Permissions;
|
||||||
|
@ -54,8 +51,6 @@ use import_map::ImportMap;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
|
||||||
use std::io::BufReader;
|
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -101,67 +96,11 @@ impl ProcState {
|
||||||
let deps_cache_location = dir.root.join("deps");
|
let deps_cache_location = dir.root.join("deps");
|
||||||
let http_cache = http_cache::HttpCache::new(&deps_cache_location);
|
let http_cache = http_cache::HttpCache::new(&deps_cache_location);
|
||||||
|
|
||||||
let mut root_cert_store = RootCertStore::empty();
|
let root_cert_store = get_root_cert_store(
|
||||||
let ca_stores: Vec<String> = flags
|
None,
|
||||||
.ca_stores
|
flags.ca_stores.clone(),
|
||||||
.clone()
|
flags.ca_file.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
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(insecure_allowlist) =
|
if let Some(insecure_allowlist) =
|
||||||
flags.unsafely_ignore_certificate_errors.as_ref()
|
flags.unsafely_ignore_certificate_errors.as_ref()
|
||||||
|
|
|
@ -3216,6 +3216,124 @@ fn lsp_cache_location() {
|
||||||
shutdown(&mut client);
|
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]
|
#[test]
|
||||||
fn lsp_diagnostics_warn() {
|
fn lsp_diagnostics_warn() {
|
||||||
let _g = http_server();
|
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": {
|
"initializationOptions": {
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"cache": null,
|
"cache": null,
|
||||||
|
"certificateStores": null,
|
||||||
"codeLens": {
|
"codeLens": {
|
||||||
"implementations": true,
|
"implementations": true,
|
||||||
"references": true,
|
"references": true,
|
||||||
|
@ -25,6 +26,8 @@
|
||||||
"hosts": {}
|
"hosts": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tlsCertificate": null,
|
||||||
|
"unsafelyIgnoreCertificateErrors": null,
|
||||||
"unstable": false
|
"unstable": false
|
||||||
},
|
},
|
||||||
"capabilities": {
|
"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