[security] Allow all functions by default (#2608)

This commit is contained in:
Salvador Girones Gil 2023-09-04 17:46:57 +02:00 committed by GitHub
parent 4c61e3a556
commit 7265136c0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 35 additions and 23 deletions

View file

@ -164,7 +164,7 @@ impl<T: Target + Hash + Eq + PartialEq + std::fmt::Display> std::fmt::Display fo
/// Capabilities are configured globally. By default, capabilities are configured as:
/// - Scripting: false
/// - Guest access: false
/// - Functions: No function is allowed nor denied, hence all functions are denied unless explicitly allowed
/// - Functions: All functions are allowed
/// - Network: No network address is allowed nor denied, hence all network addresses are denied unless explicitly allowed
///
/// The capabilities are defined using allow/deny lists for fine-grained control.
@ -235,23 +235,19 @@ impl std::fmt::Display for Capabilities {
impl Default for Capabilities {
fn default() -> Self {
Self::none()
}
}
impl Capabilities {
fn none() -> Self {
Self {
scripting: false,
guest_access: false,
allow_funcs: Arc::new(Targets::None),
allow_funcs: Arc::new(Targets::All),
deny_funcs: Arc::new(Targets::None),
allow_net: Arc::new(Targets::None),
deny_net: Arc::new(Targets::None),
}
}
}
impl Capabilities {
pub fn all() -> Self {
Self {
scripting: true,

View file

@ -1,6 +1,6 @@
use crate::cli::CF;
use crate::err::Error;
use clap::{ArgAction, Args};
use clap::Args;
use std::sync::OnceLock;
use std::time::Duration;
use surrealdb::dbs::capabilities::{Capabilities, FuncTarget, NetTarget, Targets};
@ -39,21 +39,15 @@ struct DbsCapabilities {
//
#[arg(help = "Allow all capabilities")]
#[arg(env = "SURREAL_CAPS_ALLOW_ALL", short = 'A', long, conflicts_with = "deny_all")]
#[arg(default_missing_value_os = "true", action = ArgAction::Set, num_args = 0..)]
#[arg(default_value_t = false, hide_default_value = true)]
allow_all: bool,
#[cfg(feature = "scripting")]
#[arg(help = "Allow execution of embedded scripting functions")]
#[arg(env = "SURREAL_CAPS_ALLOW_SCRIPT", long, conflicts_with = "allow_all")]
#[arg(default_missing_value_os = "true", action = ArgAction::Set, num_args = 0..)]
#[arg(default_value_t = false, hide_default_value = true)]
allow_scripting: bool,
#[arg(help = "Allow guest users to execute queries")]
#[arg(env = "SURREAL_CAPS_ALLOW_GUESTS", long, conflicts_with = "allow_all")]
#[arg(default_missing_value_os = "true", action = ArgAction::Set, num_args = 0..)]
#[arg(default_value_t = false, hide_default_value = true)]
allow_guests: bool,
#[arg(
@ -67,6 +61,7 @@ Function names must be in the form <family>[::<name>]. For example:
#[arg(env = "SURREAL_CAPS_ALLOW_FUNC", long, conflicts_with = "allow_all")]
// If the arg is provided without value, then assume it's "", which gets parsed into Targets::All
#[arg(default_missing_value_os = "", num_args = 0..)]
#[arg(default_value_os = "")] // Allow all functions by default
#[arg(value_parser = super::cli::validator::func_targets)]
allow_funcs: Option<Targets<FuncTarget>>,
@ -90,21 +85,15 @@ Targets must be in the form of <host>[:<port>], <ipv4|ipv6>[/<mask>]. For exampl
//
#[arg(help = "Deny all capabilities")]
#[arg(env = "SURREAL_CAPS_DENY_ALL", short = 'D', long, conflicts_with = "allow_all")]
#[arg(default_missing_value_os = "true", action = ArgAction::Set, num_args = 0..)]
#[arg(default_value_t = false, hide_default_value = true)]
deny_all: bool,
#[cfg(feature = "scripting")]
#[arg(help = "Deny execution of embedded scripting functions")]
#[arg(env = "SURREAL_CAPS_DENY_SCRIPT", long, conflicts_with = "deny_all")]
#[arg(default_missing_value_os = "true", action = ArgAction::Set, num_args = 0..)]
#[arg(default_value_t = false, hide_default_value = true)]
deny_scripting: bool,
#[arg(help = "Deny guest users to execute queries")]
#[arg(env = "SURREAL_CAPS_DENY_GUESTS", long, conflicts_with = "deny_all")]
#[arg(default_missing_value_os = "true", action = ArgAction::Set, num_args = 0..)]
#[arg(default_value_t = false, hide_default_value = true)]
deny_guests: bool,
#[arg(

View file

@ -504,8 +504,8 @@ mod cli_integration {
#[test(tokio::test)]
async fn test_capabilities() {
// Deny all, denies all users to execute functions and access any network address
info!("* When all capabilities are denied by default");
// Default capabilities only allow functions
info!("* When default capabilities");
{
let (addr, _server) = common::start_server(StartServerArguments {
args: "".to_owned(),
@ -516,6 +516,33 @@ mod cli_integration {
let cmd = format!("sql --conn ws://{addr} -u root -p root --ns N --db D --multi");
let query = "RETURN http::get('http://127.0.0.1/');\n\n";
let output = common::run(&cmd).input(query).output().unwrap();
assert!(
output.contains("Access to network target 'http://127.0.0.1/' is not allowed"),
"unexpected output: {output:?}"
);
let query = "RETURN function() { return '1' };";
let output = common::run(&cmd).input(query).output().unwrap();
assert!(
output.contains("Scripting functions are not allowed"),
"unexpected output: {output:?}"
);
}
// Deny all, denies all users to execute functions and access any network address
info!("* When all capabilities are denied");
{
let (addr, _server) = common::start_server(StartServerArguments {
args: "--deny-all".to_owned(),
..Default::default()
})
.await
.unwrap();
let cmd = format!("sql --conn ws://{addr} -u root -p root --ns N --db D --multi");
let query = format!("RETURN http::get('http://{}/version');\n\n", addr);
let output = common::run(&cmd).input(&query).output().unwrap();
assert!(