From e5e4783e3fef5845603679c2b20a0e0c63b1a833 Mon Sep 17 00:00:00 2001 From: Mees Delzenne Date: Tue, 17 Sep 2024 12:12:10 +0200 Subject: [PATCH] Make query depth limits configurable (#4787) --- core/src/cnf/mod.rs | 14 ++++++++++++-- core/src/syn/mod.rs | 37 ++++++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/core/src/cnf/mod.rs b/core/src/cnf/mod.rs index effd6573..e55610d8 100644 --- a/core/src/cnf/mod.rs +++ b/core/src/cnf/mod.rs @@ -21,6 +21,14 @@ pub static MAX_CONCURRENT_TASKS: LazyLock = pub static MAX_COMPUTATION_DEPTH: LazyLock = lazy_env_parse!("SURREAL_MAX_COMPUTATION_DEPTH", u32, 120); +/// Specifies how deep the parser will parse nested objects and arrays in a query. +pub static MAX_OBJECT_PARSING_DEPTH: LazyLock = + lazy_env_parse!("SURREAL_MAX_OBJECT_PARSING_DEPTH", u32, 100); + +/// Specifies how deep the parser will parse recursive queries (queries within queries). +pub static MAX_QUERY_PARSING_DEPTH: LazyLock = + lazy_env_parse!("SURREAL_MAX_QUERY_PARSING_DEPTH", u32, 20); + /// Specifies the number of items which can be cached within a single transaction. pub static TRANSACTION_CACHE_SIZE: LazyLock = lazy_env_parse!("SURREAL_TRANSACTION_CACHE_SIZE", usize, 10_000); @@ -61,8 +69,10 @@ pub static EXTERNAL_SORTING_BUFFER_LIMIT: LazyLock = pub static GRAPHQL_ENABLE: LazyLock = lazy_env_parse!("SURREAL_EXPERIMENTAL_GRAPHQL", bool, false); -/// Enable experimental bearer access and stateful access grant management. Still under active development. -/// Using this experimental feature may introduce risks related to breaking changes and security issues. +/// Enable experimental bearer access and stateful access grant management. +/// +/// Still under active development. Using this experimental feature may introduce risks related +/// to breaking changes and security issues. #[cfg(not(test))] pub static EXPERIMENTAL_BEARER_ACCESS: LazyLock = lazy_env_parse!("SURREAL_EXPERIMENTAL_BEARER_ACCESS", bool, false); diff --git a/core/src/syn/mod.rs b/core/src/syn/mod.rs index 1dd7fee0..76d79eb8 100644 --- a/core/src/syn/mod.rs +++ b/core/src/syn/mod.rs @@ -1,6 +1,7 @@ //! Module containing the implementation of the surrealql tokens, lexer, and parser. use crate::{ + cnf::{MAX_OBJECT_PARSING_DEPTH, MAX_QUERY_PARSING_DEPTH}, err::Error, sql::{Block, Datetime, Duration, Idiom, Query, Range, Subquery, Thing, Value}, }; @@ -43,7 +44,9 @@ pub fn could_be_reserved_keyword(s: &str) -> bool { #[instrument(level = "trace", target = "surrealdb::core::syn", fields(length = input.len()))] pub fn parse(input: &str) -> Result { trace!(target: TARGET, "Parsing SurrealQL query"); - let mut parser = Parser::new(input.as_bytes()); + let mut parser = Parser::new(input.as_bytes()) + .with_object_recursion_limit(*MAX_OBJECT_PARSING_DEPTH as usize) + .with_query_recursion_limit(*MAX_QUERY_PARSING_DEPTH as usize); let mut stack = Stack::new(); stack .enter(|stk| parser.parse_query(stk)) @@ -56,7 +59,9 @@ pub fn parse(input: &str) -> Result { #[instrument(level = "trace", target = "surrealdb::core::syn", fields(length = input.len()))] pub fn value(input: &str) -> Result { trace!(target: TARGET, "Parsing SurrealQL value"); - let mut parser = Parser::new(input.as_bytes()); + let mut parser = Parser::new(input.as_bytes()) + .with_object_recursion_limit(*MAX_OBJECT_PARSING_DEPTH as usize) + .with_query_recursion_limit(*MAX_QUERY_PARSING_DEPTH as usize); let mut stack = Stack::new(); stack .enter(|stk| parser.parse_value_table(stk)) @@ -70,7 +75,9 @@ pub fn value(input: &str) -> Result { #[instrument(level = "trace", target = "surrealdb::core::syn", fields(length = input.len()))] pub fn json(input: &str) -> Result { trace!(target: TARGET, "Parsing inert JSON value"); - let mut parser = Parser::new(input.as_bytes()); + let mut parser = Parser::new(input.as_bytes()) + .with_object_recursion_limit(*MAX_OBJECT_PARSING_DEPTH as usize) + .with_query_recursion_limit(*MAX_QUERY_PARSING_DEPTH as usize); let mut stack = Stack::new(); stack .enter(|stk| parser.parse_json(stk)) @@ -84,7 +91,9 @@ pub fn json(input: &str) -> Result { #[instrument(level = "trace", target = "surrealdb::core::syn", fields(length = input.len()))] pub fn subquery(input: &str) -> Result { trace!(target: TARGET, "Parsing SurrealQL subquery"); - let mut parser = Parser::new(input.as_bytes()); + let mut parser = Parser::new(input.as_bytes()) + .with_object_recursion_limit(*MAX_OBJECT_PARSING_DEPTH as usize) + .with_query_recursion_limit(*MAX_QUERY_PARSING_DEPTH as usize); let mut stack = Stack::new(); stack .enter(|stk| parser.parse_full_subquery(stk)) @@ -98,7 +107,9 @@ pub fn subquery(input: &str) -> Result { #[instrument(level = "trace", target = "surrealdb::core::syn", fields(length = input.len()))] pub fn idiom(input: &str) -> Result { trace!(target: TARGET, "Parsing SurrealQL idiom"); - let mut parser = Parser::new(input.as_bytes()); + let mut parser = Parser::new(input.as_bytes()) + .with_object_recursion_limit(*MAX_OBJECT_PARSING_DEPTH as usize) + .with_query_recursion_limit(*MAX_QUERY_PARSING_DEPTH as usize); parser.table_as_field = true; let mut stack = Stack::new(); stack @@ -151,7 +162,9 @@ pub fn range(input: &str) -> Result { #[instrument(level = "trace", target = "surrealdb::core::syn", fields(length = input.len()))] pub fn thing(input: &str) -> Result { trace!(target: TARGET, "Parsing SurrealQL thing"); - let mut parser = Parser::new(input.as_bytes()); + let mut parser = Parser::new(input.as_bytes()) + .with_object_recursion_limit(*MAX_OBJECT_PARSING_DEPTH as usize) + .with_query_recursion_limit(*MAX_QUERY_PARSING_DEPTH as usize); let mut stack = Stack::new(); stack .enter(|stk| parser.parse_thing(stk)) @@ -165,7 +178,9 @@ pub fn thing(input: &str) -> Result { #[instrument(level = "trace", target = "surrealdb::core::syn", fields(length = input.len()))] pub fn block(input: &str) -> Result { trace!(target: TARGET, "Parsing SurrealQL block"); - let mut parser = Parser::new(input.as_bytes()); + let mut parser = Parser::new(input.as_bytes()) + .with_object_recursion_limit(*MAX_OBJECT_PARSING_DEPTH as usize) + .with_query_recursion_limit(*MAX_QUERY_PARSING_DEPTH as usize); let mut stack = Stack::new(); let token = parser.peek(); match token.kind { @@ -190,7 +205,9 @@ pub fn block(input: &str) -> Result { #[instrument(level = "trace", target = "surrealdb::core::syn", fields(length = input.len()))] pub fn value_legacy_strand(input: &str) -> Result { trace!(target: TARGET, "Parsing SurrealQL value, with legacy strings"); - let mut parser = Parser::new(input.as_bytes()); + let mut parser = Parser::new(input.as_bytes()) + .with_object_recursion_limit(*MAX_OBJECT_PARSING_DEPTH as usize) + .with_query_recursion_limit(*MAX_QUERY_PARSING_DEPTH as usize); let mut stack = Stack::new(); parser.allow_legacy_strand(true); stack @@ -205,7 +222,9 @@ pub fn value_legacy_strand(input: &str) -> Result { #[instrument(level = "trace", target = "surrealdb::core::syn", fields(length = input.len()))] pub fn json_legacy_strand(input: &str) -> Result { trace!(target: TARGET, "Parsing inert JSON value, with legacy strings"); - let mut parser = Parser::new(input.as_bytes()); + let mut parser = Parser::new(input.as_bytes()) + .with_object_recursion_limit(*MAX_OBJECT_PARSING_DEPTH as usize) + .with_query_recursion_limit(*MAX_QUERY_PARSING_DEPTH as usize); let mut stack = Stack::new(); parser.allow_legacy_strand(true); stack