Skip to content

Commit

Permalink
Merge branch 'url-fix'
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Jan 3, 2024
2 parents e3c5a0f + 3c060cb commit 0e579a0
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 23 deletions.
14 changes: 14 additions & 0 deletions gix-url/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,17 @@ impl<'a> TryFrom<std::borrow::Cow<'a, BStr>> for Url {
Self::try_from(&*value)
}
}

impl std::fmt::Display for Url {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut storage;
let to_print = if self.password.is_some() {
storage = self.clone();
storage.password = Some("<redacted>".into());
&storage
} else {
self
};
to_print.to_bstring().fmt(f)
}
}
12 changes: 8 additions & 4 deletions gix-url/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,17 @@ pub fn expand_path(user: Option<&expand_path::ForUser>, path: &BStr) -> Result<P

/// A URL with support for specialized git related capabilities.
///
/// Additionally there is support for [deserialization](Url::from_bytes()) and serialization
/// (_see the [`std::fmt::Display::fmt()`] implementation_).
/// Additionally there is support for [deserialization](Url::from_bytes()) and [serialization](Url::to_bstring()).
///
/// # Security Warning
///
/// URLs may contain passwords and we serialize them when [formatting](std::fmt::Display) or
/// [serializing losslessly](Url::to_bstring()).
/// URLs may contain passwords and using standard [formatting](std::fmt::Display) will redact
/// such password, whereas [lossless serialization](Url::to_bstring()) will contain all parts of the
/// URL.
/// **Beware that some URls still print secrets if they use them outside of the designated password fields.**
///
/// Also note that URLs that fail to parse are typically stored in [the resulting error](parse::Error) type
/// and printed in full using its display implementation.
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Url {
Expand Down
20 changes: 20 additions & 0 deletions gix-url/tests/access/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,23 @@ fn path_argument_safe() -> crate::Result {
assert_eq!(url.path_argument_safe(), None);
Ok(())
}

#[test]
fn display() {
fn compare(input: &str, expected: &str, message: &str) {
let url = gix_url::parse(input.into()).expect("input is valid url");
assert_eq!(format!("{url}"), expected, "{message}");
}

compare(
"ssh://foo/-oProxyCommand=open$IFS-aCalculator",
"ssh://foo/-oProxyCommand=open$IFS-aCalculator",
"it round-trips with sane unicode and without password",
);
compare("/path/to/repo", "/path/to/repo", "same goes for simple paths");
compare(
"https://user:password@host/path",
"https://user:<redacted>@host/path",
"it visibly redacts passwords though",
);
}
21 changes: 2 additions & 19 deletions gix-url/tests/fuzzed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,11 @@ use std::{path::Path, time::Duration};

use bstr::ByteSlice;

#[test]
#[cfg_attr(debug_assertions, ignore = "fails due to integer overflow")]
fn abort_unfixed_in_debug_mode() {
for name in ["very-long-abort2", "very-long-abort"] {
let base = Path::new("tests").join("fixtures").join("fuzzed");
let location = base.join(Path::new(name).with_extension("url"));
let url = std::fs::read(&location).unwrap();
let start = std::time::Instant::now();
dbg!(name);
gix_url::parse(url.as_bstr()).ok();
assert!(
start.elapsed() < Duration::from_millis(100),
"URL at '{}' parsed too slowly, took {:.00}s",
location.display(),
start.elapsed().as_secs_f32()
);
}
}

#[test]
fn fuzzed() {
for name in [
"very-long-abort2",
"very-long-abort",
"very-long6",
"very-long5",
"very-long4",
Expand Down

0 comments on commit 0e579a0

Please sign in to comment.