|
|
… |
406 unchanged lines |
| 407 |
407 |
|
Ok(if bad > 0 { 1 } else { 0 }) |
| 408 |
408 |
|
} |
| 409 |
409 |
|
|
|
410 |
+ |
/// Whether a configuration key holds a secret, never to be printed. |
|
411 |
+ |
fn secret_key(key: &str) -> bool { |
|
412 |
+ |
let k = key.to_ascii_lowercase(); |
|
413 |
+ |
k.ends_with(".token") || k.contains("secret") || k.contains("password") |
|
414 |
+ |
} |
|
415 |
+ |
|
|
416 |
+ |
/// A secret as a confirmation may show it (oxygen#113): enough of its start |
|
417 |
+ |
/// to tell which one it is, never the rest. |
|
418 |
+ |
fn mask(value: &str) -> String { |
|
419 |
+ |
let shown: String = value.chars().take(10).collect(); |
|
420 |
+ |
let rest = value.chars().count().saturating_sub(10); |
|
421 |
+ |
if rest == 0 { |
|
422 |
+ |
"…".to_string() |
|
423 |
+ |
} else { |
|
424 |
+ |
format!("{shown}…({rest} more)") |
|
425 |
+ |
} |
|
426 |
+ |
} |
|
427 |
+ |
|
| 410 |
428 |
|
pub fn config(key: &str, value: &str, user: bool, unset: bool) -> Result<i32> { |
| 411 |
429 |
|
// An empty value removes the key, which is what --unset says plainly. |
| 412 |
|
- |
let value = if unset { "" } else { value }; |
|
430 |
+ |
// `-` reads the value from standard input, so a token never sits in |
|
431 |
+ |
// the shell's history (oxygen#113). |
|
432 |
+ |
let read; |
|
433 |
+ |
let value = if unset { |
|
434 |
+ |
"" |
|
435 |
+ |
} else if value == "-" { |
|
436 |
+ |
let mut line = String::new(); |
|
437 |
+ |
std::io::stdin().read_line(&mut line)?; |
|
438 |
+ |
read = line.trim().to_string(); |
|
439 |
+ |
read.as_str() |
|
440 |
+ |
} else { |
|
441 |
+ |
value |
|
442 |
+ |
}; |
| 413 |
443 |
|
let said = if unset { |
| 414 |
444 |
|
format!("{} {key}", ui::done("Unset")) |
|
445 |
+ |
} else if secret_key(key) { |
|
446 |
+ |
format!("{} {key} = {}", ui::done("Set"), mask(value)) |
| 415 |
447 |
|
} else { |
| 416 |
448 |
|
format!("{} {key} = {value}", ui::done("Set")) |
| 417 |
449 |
|
}; |
|
|
… |
471 unchanged lines |
| 889 |
921 |
|
|
| 890 |
922 |
|
fn hex_string(b: &[u8]) -> String { |
| 891 |
923 |
|
b.iter().map(|x| format!("{x:02x}")).collect() |
|
924 |
+ |
} |
|
925 |
+ |
|
|
926 |
+ |
#[cfg(test)] |
|
927 |
+ |
mod tests { |
|
928 |
+ |
#[test] |
|
929 |
+ |
fn secrets_are_never_printed_whole() { |
|
930 |
+ |
assert!( |
|
931 |
+ |
super::secret_key("remote.earth.token") |
|
932 |
+ |
&& super::secret_key("auth.secret") |
|
933 |
+ |
&& !super::secret_key("remote.earth.url") |
|
934 |
+ |
); |
|
935 |
+ |
assert_eq!( |
|
936 |
+ |
super::mask("oxyhub_6b7dce13409b634d"), |
|
937 |
+ |
"oxyhub_6b7…(13 more)" |
|
938 |
+ |
); |
|
939 |
+ |
assert_eq!(super::mask("short"), "…"); |
|
940 |
+ |
} |
| 892 |
941 |
|
} |