Ensure system parameters are not able to be overridden

This commit is contained in:
Tobie Morgan Hitchcock 2022-11-01 23:55:33 +00:00
parent ed18003223
commit 65f219ffe5
6 changed files with 81 additions and 11 deletions

View file

@ -5,6 +5,9 @@ pub const MAX_CONCURRENT_TASKS: usize = 64;
/// Specifies how deep various forms of computation will go before the query fails.
pub const MAX_COMPUTATION_DEPTH: u8 = 30;
/// Specifies the names of parameters which can not be specified in a query.
pub const PROTECTED_PARAM_NAMES: &[&str] = &["auth", "scope", "token", "session"];
/// The characters which are supported in server record IDs.
pub const ID_CHARS: [char; 36] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',

View file

@ -1,3 +1,4 @@
use crate::cnf::PROTECTED_PARAM_NAMES;
use crate::ctx::Context;
use crate::dbs::response::Response;
use crate::dbs::Auth;
@ -229,9 +230,16 @@ impl<'a> Executor<'a> {
true => Err(Error::TxFailure),
// The transaction began successfully
false => {
// Process the statement
let res = stm.compute(&ctx, &opt, &self.txn(), None).await;
//
// Check if the variable is a protected variable
let res = match PROTECTED_PARAM_NAMES.contains(&stm.name.as_str()) {
// The variable isn't protected and can be stored
false => stm.compute(&ctx, &opt, &self.txn(), None).await,
// The user tried to set a protected variable
true => Err(Error::InvalidParam {
name: stm.name.to_owned(),
}),
};
// Check the statement
match res {
Ok(val) => {
// Set the parameter

View file

@ -1,23 +1,35 @@
use crate::cnf::PROTECTED_PARAM_NAMES;
use crate::ctx::Context;
use crate::err::Error;
use crate::sql::value::Value;
use std::collections::BTreeMap;
pub type Variables = Option<BTreeMap<String, Value>>;
pub(crate) trait Attach {
fn attach(self, ctx: Context) -> Context;
fn attach(self, ctx: Context) -> Result<Context, Error>;
}
impl Attach for Variables {
fn attach(self, mut ctx: Context) -> Context {
fn attach(self, mut ctx: Context) -> Result<Context, Error> {
match self {
Some(m) => {
for (key, val) in m {
ctx.add_value(key, val);
// Check if the variable is a protected variable
match PROTECTED_PARAM_NAMES.contains(&key.as_str()) {
// The variable isn't protected and can be stored
false => ctx.add_value(key, val),
// The user tried to set a protected variable
true => {
return Err(Error::InvalidParam {
name: key,
})
}
}
}
ctx
Ok(ctx)
}
None => ctx,
None => Ok(ctx),
}
}
}

View file

@ -79,6 +79,12 @@ pub enum Error {
#[error("Remote HTTP request functions are not enabled")]
HttpDisabled,
/// it is not possible to set a variable with the specified name
#[error("Found '{name}' but it is not possible to set a variable with this name")]
InvalidParam {
name: String,
},
/// The LIMIT clause must evaluate to a positive integer
#[error("Found {value} but the LIMIT clause must evaluate to a positive integer")]
InvalidLimit {

View file

@ -239,7 +239,7 @@ impl Datastore {
// Start an execution context
let ctx = sess.context(ctx);
// Store the query variables
let ctx = vars.attach(ctx);
let ctx = vars.attach(ctx)?;
// Parse the SQL query text
let ast = sql::parse(txt)?;
// Setup the auth options
@ -288,7 +288,7 @@ impl Datastore {
// Start an execution context
let ctx = sess.context(ctx);
// Store the query variables
let ctx = vars.attach(ctx);
let ctx = vars.attach(ctx)?;
// Setup the auth options
opt.auth = sess.au.clone();
// Setup the live options
@ -338,7 +338,7 @@ impl Datastore {
// Start an execution context
let ctx = sess.context(ctx);
// Store the query variables
let ctx = vars.attach(ctx);
let ctx = vars.attach(ctx)?;
// Setup the auth options
opt.auth = sess.au.clone();
// Set current NS and DB

41
lib/tests/param.rs Normal file
View file

@ -0,0 +1,41 @@
mod parse;
use parse::Parse;
use surrealdb::sql::Value;
use surrealdb::Datastore;
use surrealdb::Error;
use surrealdb::Session;
#[tokio::test]
async fn define_protected_param() -> Result<(), Error> {
let sql = "
LET $test = { some: 'thing', other: true };
SELECT * FROM $test WHERE some = 'thing';
LET $auth = { ID: admin:tester };
";
let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test");
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
assert_eq!(res.len(), 3);
//
let tmp = res.remove(0).result;
assert!(tmp.is_ok());
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
other: true,
some: 'thing'
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(matches!(
tmp.err(),
Some(e) if e.to_string() == r#"Found 'auth' but it is not possible to set a variable with this name"#
));
//
Ok(())
}