keymgr: Validate ArtiPath and ArtiPathComponent.

This commit is contained in:
Gabriela Moldovan 2023-06-19 13:13:39 +01:00
parent 2825dfc2bc
commit f56ed918b0
1 changed files with 45 additions and 12 deletions

View File

@ -1,6 +1,7 @@
//! The [`KeySpecifier`] trait and its implementations. //! The [`KeySpecifier`] trait and its implementations.
use crate::{KeystoreError, Result}; use crate::{KeystoreError, Result};
use std::path;
use tor_error::HasKind; use tor_error::HasKind;
/// The path of a key in the Arti key store. /// The path of a key in the Arti key store.
@ -32,13 +33,38 @@ impl KeystoreError for InvalidArtiPathError {}
impl ArtiPath { impl ArtiPath {
/// Create a new [`ArtiPath`]. /// Create a new [`ArtiPath`].
/// ///
/// This function returns an error if the specified string is not a valid Arti path. /// An `ArtiPath` may only consist of UTF-8 alphanumeric, dash (`-`), underscore (`_`), and
// TODO hs: restrict the character set and syntax for values of this type (it should not be /// path separator characters.
// possible to construct an ArtiPath out of a String that uses disallowed chars, or one that is in ///
// the wrong format (TBD exactly what this format is supposed to look like) /// The specified string is normalized by replacing any consecutive occurrences of the path
#[allow(clippy::unnecessary_wraps)] // TODO hs: remove /// separator character with a single path separator.
///
/// This function returns an error if `inner` contains any disallowed characters, or if it
/// consists solely of path sepatators.
pub fn new(inner: String) -> Result<Self> { pub fn new(inner: String) -> Result<Self> {
Ok(Self(inner)) let is_allowed =
|c: char| ArtiPathComponent::is_allowed_char(c) || c == path::MAIN_SEPARATOR;
if inner.chars().any(|c| !is_allowed(c)) {
Err(Box::new(InvalidArtiPathError(inner)))
} else {
Self::normalize_string(&inner).map(Self)
}
}
/// Remove all but the first of consecutive [MAIN_SEPARATOR](path::MAIN_SEPARATOR) elements
/// from `s`.
///
/// This function returns an error if `s` consists solely of path sepatators.
fn normalize_string(s: &str) -> Result<String> {
if s.chars().all(|c| c == path::MAIN_SEPARATOR) {
return Err(Box::new(InvalidArtiPathError(s.into())));
}
let mut chars = s.chars().collect::<Vec<_>>();
chars.dedup_by(|a, b| *a == path::MAIN_SEPARATOR && *b == path::MAIN_SEPARATOR);
Ok(chars.into_iter().collect::<String>())
} }
} }
@ -53,14 +79,21 @@ pub struct ArtiPathComponent(String);
impl ArtiPathComponent { impl ArtiPathComponent {
/// Create a new [`ArtiPathComponent`]. /// Create a new [`ArtiPathComponent`].
/// ///
/// This function returns an error if the specified string contains any disallowed characters. /// An `ArtiPathComponent` may only consist of UTF-8 alphanumeric, dash
/// (`-`), and underscore (`_`) characters.
/// ///
/// TODO hs: restrict the character set and syntax for values of this type (it should not be /// This function returns an error if `inner` contains any disallowed characters.
/// possible to construct an ArtiPathComponent out of a String that uses disallowed chars, or
/// one that is in the wrong format (TBD exactly what this format is supposed to look like)
#[allow(clippy::unnecessary_wraps)] // TODO hs: remove
pub fn new(inner: String) -> Result<Self> { pub fn new(inner: String) -> Result<Self> {
Ok(Self(inner)) if inner.chars().any(|c| !Self::is_allowed_char(c)) {
Err(Box::new(InvalidArtiPathError(inner)))
} else {
Ok(Self(inner))
}
}
/// Check whether `c` can be used within an `ArtiPathComponent`.
fn is_allowed_char(c: char) -> bool {
c.is_alphanumeric() || c == '_' || c == '-'
} }
} }