Security - Limit output size of all string functions. (#2112)
This commit is contained in:
parent
a6e1bacee0
commit
38ca45849f
1 changed files with 36 additions and 12 deletions
|
@ -2,8 +2,23 @@ use crate::err::Error;
|
|||
use crate::fnc::util::string;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
/// Returns `true` if a string of this length is too much to allocate.
|
||||
fn limit(name: &str, n: usize) -> Result<(), Error> {
|
||||
const LIMIT: usize = 2usize.pow(20);
|
||||
if n > LIMIT {
|
||||
Err(Error::InvalidArguments {
|
||||
name: name.to_owned(),
|
||||
message: format!("Output must not exceed {LIMIT} bytes."),
|
||||
})
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn concat(args: Vec<Value>) -> Result<Value, Error> {
|
||||
Ok(args.into_iter().map(|x| x.as_string()).collect::<Vec<_>>().concat().into())
|
||||
let strings = args.into_iter().map(Value::as_string).collect::<Vec<_>>();
|
||||
limit("string::concat", strings.iter().map(String::len).sum::<usize>())?;
|
||||
Ok(strings.concat().into())
|
||||
}
|
||||
|
||||
pub fn contains((val, check): (String, String)) -> Result<Value, Error> {
|
||||
|
@ -20,10 +35,19 @@ pub fn join(args: Vec<Value>) -> Result<Value, Error> {
|
|||
name: String::from("string::join"),
|
||||
message: String::from("Expected at least one argument"),
|
||||
})?;
|
||||
|
||||
let strings = args.collect::<Vec<_>>();
|
||||
limit(
|
||||
"string::join",
|
||||
strings
|
||||
.len()
|
||||
.saturating_mul(chr.len())
|
||||
.saturating_add(strings.iter().map(String::len).sum::<usize>()),
|
||||
)?;
|
||||
|
||||
// FIXME: Use intersperse to avoid intermediate allocation once stable
|
||||
// https://github.com/rust-lang/rust/issues/79524
|
||||
let val = args.collect::<Vec<_>>().join(&chr);
|
||||
Ok(val.into())
|
||||
Ok(strings.join(&chr).into())
|
||||
}
|
||||
|
||||
pub fn len((string,): (String,)) -> Result<Value, Error> {
|
||||
|
@ -36,18 +60,18 @@ pub fn lowercase((string,): (String,)) -> Result<Value, Error> {
|
|||
}
|
||||
|
||||
pub fn repeat((val, num): (String, usize)) -> Result<Value, Error> {
|
||||
const LIMIT: usize = 2usize.pow(20);
|
||||
if val.len().saturating_mul(num) > LIMIT {
|
||||
Err(Error::InvalidArguments {
|
||||
name: String::from("string::repeat"),
|
||||
message: format!("Output must not exceed {LIMIT} bytes."),
|
||||
})
|
||||
} else {
|
||||
Ok(val.repeat(num).into())
|
||||
}
|
||||
limit("string::repeat", val.len().saturating_mul(num))?;
|
||||
Ok(val.repeat(num).into())
|
||||
}
|
||||
|
||||
pub fn replace((val, old, new): (String, String, String)) -> Result<Value, Error> {
|
||||
if new.len() > old.len() {
|
||||
let increase = new.len() - old.len();
|
||||
limit(
|
||||
"string::replace",
|
||||
val.len().saturating_add(val.matches(&old).count().saturating_mul(increase)),
|
||||
)?;
|
||||
}
|
||||
Ok(val.replace(&old, &new).into())
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue