diff --git a/Cargo.lock b/Cargo.lock index 9254773335..740d0b290b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3996,6 +3996,7 @@ dependencies = [ "rayon", "thiserror", "tracing", + "url", ] [[package]] diff --git a/crates/rover-std/Cargo.toml b/crates/rover-std/Cargo.toml index 3b623c3d22..ee95ce5d9f 100644 --- a/crates/rover-std/Cargo.toml +++ b/crates/rover-std/Cargo.toml @@ -14,4 +14,5 @@ crossbeam-channel = { workspace = true } notify = { workspace = true } rayon = { workspace = true } thiserror = { workspace = true } -tracing = { workspace = true } \ No newline at end of file +tracing = { workspace = true } +url = { workspace = true } \ No newline at end of file diff --git a/crates/rover-std/src/lib.rs b/crates/rover-std/src/lib.rs index 89121eb228..9184059d32 100644 --- a/crates/rover-std/src/lib.rs +++ b/crates/rover-std/src/lib.rs @@ -2,6 +2,7 @@ mod emoji; mod error; mod fs; mod style; +mod url; pub mod prompt; pub use emoji::Emoji; @@ -9,3 +10,4 @@ pub use error::RoverStdError; pub use fs::Fs; pub use style::is_no_color_set; pub use style::Style; +pub use url::sanitize_url; diff --git a/crates/rover-std/src/url.rs b/crates/rover-std/src/url.rs new file mode 100644 index 0000000000..16086c4f28 --- /dev/null +++ b/crates/rover-std/src/url.rs @@ -0,0 +1,46 @@ +use url::Url; + +pub fn sanitize_url(url: &str) -> Option { + Url::parse(url).ok().and_then(|mut parsed_url| { + if parsed_url.username() != "" { + if parsed_url.set_username("").is_err() { + return None; + } + } + + if parsed_url.set_password(None).is_err() { + return None; + } + + Some(parsed_url.to_string()) + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + const UNAUTHENTICATED_URL: &str = "https://rover.apollo.dev/nix/latest"; + const AUTHENTICATED_URL: &str = "https://username:password@customer.proxy/nix/latest"; + const SANITIZED_AUTHENTICATED_URL: &str = "https://customer.proxy/nix/latest"; + + const INVALID_URL: &str = "not-a-url"; + + #[test] + fn it_leaves_unauthenticated_url_alone() { + let sanitized_url = sanitize_url(UNAUTHENTICATED_URL); + assert_eq!(sanitized_url, Some(UNAUTHENTICATED_URL.to_string())); + } + + #[test] + fn it_sanitizes_authenticated_url() { + let sanitized_url = sanitize_url(AUTHENTICATED_URL); + assert_eq!(sanitized_url, Some(SANITIZED_AUTHENTICATED_URL.to_string())); + } + + #[test] + fn it_returns_none_for_invalid_url() { + let sanitized_url = sanitize_url(INVALID_URL); + assert_eq!(sanitized_url, None); + } +} diff --git a/examples/supergraph-demo/products/products.graphql b/examples/supergraph-demo/products/products.graphql index 946b1061a7..f24344fb53 100644 --- a/examples/supergraph-demo/products/products.graphql +++ b/examples/supergraph-demo/products/products.graphql @@ -2,7 +2,7 @@ extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@shareable", "@tag", "@inaccessible"]) -type Query { +type Query { allProducts: [ProductItf] product(id: ID!): ProductItf } diff --git a/src/command/install/plugin.rs b/src/command/install/plugin.rs index 1a60308d71..f152504010 100644 --- a/src/command/install/plugin.rs +++ b/src/command/install/plugin.rs @@ -4,7 +4,7 @@ use anyhow::{anyhow, Context}; use apollo_federation_types::config::{FederationVersion, PluginVersion, RouterVersion}; use binstall::Installer; use camino::Utf8PathBuf; -use rover_std::Fs; +use rover_std::{sanitize_url, Fs}; use semver::Version; use serde::{Deserialize, Serialize}; @@ -330,7 +330,12 @@ impl PluginInstaller { fn do_install(&self, plugin: &Plugin, is_latest: bool) -> RoverResult> { let plugin_name = plugin.get_name(); let plugin_tarball_url = plugin.get_tarball_url()?; - eprintln!("downloading the '{plugin_name}' plugin from {plugin_tarball_url}"); + // only print the download message if the username and password have been stripped from the URL + if let Some(sanitized_url) = sanitize_url(&plugin_tarball_url) { + eprintln!("downloading the '{plugin_name}' plugin from {sanitized_url}"); + } else { + eprintln!("downloading the '{plugin_name}' plugin"); + } Ok(self.rover_installer.install_plugin( &plugin_name, &plugin_tarball_url,