diff --git a/lib/src/cnf/mod.rs b/lib/src/cnf/mod.rs index 8ee207b3..f6d27d7f 100644 --- a/lib/src/cnf/mod.rs +++ b/lib/src/cnf/mod.rs @@ -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', diff --git a/lib/src/dbs/executor.rs b/lib/src/dbs/executor.rs index 16727e79..dc80c4e9 100644 --- a/lib/src/dbs/executor.rs +++ b/lib/src/dbs/executor.rs @@ -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 diff --git a/lib/src/dbs/variables.rs b/lib/src/dbs/variables.rs index a046f9f9..e555a333 100644 --- a/lib/src/dbs/variables.rs +++ b/lib/src/dbs/variables.rs @@ -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>; pub(crate) trait Attach { - fn attach(self, ctx: Context) -> Context; + fn attach(self, ctx: Context) -> Result; } impl Attach for Variables { - fn attach(self, mut ctx: Context) -> Context { + fn attach(self, mut ctx: Context) -> Result { 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), } } } diff --git a/lib/src/err/mod.rs b/lib/src/err/mod.rs index cbdd586a..839629db 100644 --- a/lib/src/err/mod.rs +++ b/lib/src/err/mod.rs @@ -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 { diff --git a/lib/src/kvs/ds.rs b/lib/src/kvs/ds.rs index 979e79ed..06d424ce 100644 --- a/lib/src/kvs/ds.rs +++ b/lib/src/kvs/ds.rs @@ -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 diff --git a/lib/tests/param.rs b/lib/tests/param.rs new file mode 100644 index 00000000..3af6c3b2 --- /dev/null +++ b/lib/tests/param.rs @@ -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(()) +}