Security - Limit output size of all string functions. (#2112)

This commit is contained in:
Finn Bear 2023-06-10 13:42:26 -07:00 committed by GitHub
parent a6e1bacee0
commit 38ca45849f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -2,8 +2,23 @@ use crate::err::Error;
use crate::fnc::util::string; use crate::fnc::util::string;
use crate::sql::value::Value; 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> { 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> { 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"), name: String::from("string::join"),
message: String::from("Expected at least one argument"), 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 // FIXME: Use intersperse to avoid intermediate allocation once stable
// https://github.com/rust-lang/rust/issues/79524 // https://github.com/rust-lang/rust/issues/79524
let val = args.collect::<Vec<_>>().join(&chr); Ok(strings.join(&chr).into())
Ok(val.into())
} }
pub fn len((string,): (String,)) -> Result<Value, Error> { 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> { pub fn repeat((val, num): (String, usize)) -> Result<Value, Error> {
const LIMIT: usize = 2usize.pow(20); limit("string::repeat", val.len().saturating_mul(num))?;
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()) Ok(val.repeat(num).into())
}
} }
pub fn replace((val, old, new): (String, String, String)) -> Result<Value, Error> { 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()) Ok(val.replace(&old, &new).into())
} }