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::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 {
|
Ok(val.repeat(num).into())
|
||||||
Err(Error::InvalidArguments {
|
|
||||||
name: String::from("string::repeat"),
|
|
||||||
message: format!("Output must not exceed {LIMIT} bytes."),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
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())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue