Ensure system parameters are not able to be overridden
This commit is contained in:
parent
ed18003223
commit
65f219ffe5
6 changed files with 81 additions and 11 deletions
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
41
lib/tests/param.rs
Normal 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(())
|
||||
}
|
Loading…
Reference in a new issue