fix: disallow control characters in Database names (#684)
parent
0cfd543db2
commit
a967e2f1dd
|
@ -892,6 +892,7 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"serde",
|
"serde",
|
||||||
"snafu",
|
"snafu",
|
||||||
|
"test_helpers",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ percent-encoding = "2.1.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
|
test_helpers = { path = "../test_helpers" }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "benchmark"
|
name = "benchmark"
|
||||||
|
|
|
@ -18,10 +18,12 @@ pub enum DatabaseNameError {
|
||||||
LengthConstraint { name: String },
|
LengthConstraint { name: String },
|
||||||
|
|
||||||
#[snafu(display(
|
#[snafu(display(
|
||||||
"Database name {} contains invalid characters (allowed: alphanumeric, _, -, and %)",
|
"Database name '{}' contains invalid character. Character number {} is a control which is not allowed.", name, bad_char_offset
|
||||||
name
|
|
||||||
))]
|
))]
|
||||||
BadChars { name: String },
|
BadChars {
|
||||||
|
bad_char_offset: usize,
|
||||||
|
name: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A correctly formed database name.
|
/// A correctly formed database name.
|
||||||
|
@ -63,12 +65,13 @@ impl<'a> DatabaseName<'a> {
|
||||||
//
|
//
|
||||||
// NOTE: If changing these characters, please update the error message
|
// NOTE: If changing these characters, please update the error message
|
||||||
// above.
|
// above.
|
||||||
if !name
|
if let Some(bad_char_offset) = name.chars().position(|c| c.is_control()) {
|
||||||
.chars()
|
return BadChars {
|
||||||
.all(|c| c.is_alphanumeric() || c == '_' || c == '-' || c == '%')
|
bad_char_offset,
|
||||||
{
|
name,
|
||||||
return BadChars { name }.fail();
|
}
|
||||||
}
|
.fail();
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Self(name))
|
Ok(Self(name))
|
||||||
}
|
}
|
||||||
|
@ -118,6 +121,7 @@ impl<'a> std::fmt::Display for DatabaseName<'a> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
use test_helpers::assert_contains;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_deref() {
|
fn test_deref() {
|
||||||
|
@ -148,8 +152,32 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bad_chars() {
|
fn test_bad_chars_null() {
|
||||||
let got = DatabaseName::new("example!").unwrap_err();
|
let got = DatabaseName::new("example\x00").unwrap_err();
|
||||||
assert!(matches!(got, DatabaseNameError::BadChars { name: _n }));
|
assert_contains!(got.to_string() , "Database name 'example\x00' contains invalid character. Character number 7 is a control which is not allowed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bad_chars_high_control() {
|
||||||
|
let got = DatabaseName::new("\u{007f}example").unwrap_err();
|
||||||
|
assert_contains!(got.to_string() , "Database name '\u{007f}example' contains invalid character. Character number 0 is a control which is not allowed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bad_chars_tab() {
|
||||||
|
let got = DatabaseName::new("example\tdb").unwrap_err();
|
||||||
|
assert_contains!(got.to_string() , "Database name 'example\tdb' contains invalid character. Character number 7 is a control which is not allowed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bad_chars_newline() {
|
||||||
|
let got = DatabaseName::new("my_example\ndb").unwrap_err();
|
||||||
|
assert_contains!(got.to_string() , "Database name 'my_example\ndb' contains invalid character. Character number 10 is a control which is not allowed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ok_chars() {
|
||||||
|
let db = DatabaseName::new("my-example-db_with_underscores and spaces").unwrap();
|
||||||
|
assert_eq!(&*db, "my-example-db_with_underscores and spaces");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -615,15 +615,13 @@ mod tests {
|
||||||
let server = Server::new(manager, store);
|
let server = Server::new(manager, store);
|
||||||
server.set_id(1);
|
server.set_id(1);
|
||||||
|
|
||||||
let reject: [&str; 5] = [
|
let reject = vec![
|
||||||
"bananas!",
|
"bananas\t",
|
||||||
r#""bananas\"are\"great"#,
|
"bananas\"are\u{0099}\"great",
|
||||||
"bananas:good",
|
"bananas\nfoster",
|
||||||
"bananas/cavendish",
|
|
||||||
"bananas\n",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
for &name in &reject {
|
for name in reject {
|
||||||
let rules = DatabaseRules {
|
let rules = DatabaseRules {
|
||||||
store_locally: true,
|
store_locally: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
Loading…
Reference in New Issue