1use crate::error::{Error, Result};
4use crate::expressions::Expression;
5use crate::tokens::{ParserToken, Span, Token, TokenType};
6use serde::{Deserialize, Serialize};
7
8const DEFAULT_MAX_INPUT_BYTES: usize = 16 * 1024 * 1024;
9const DEFAULT_MAX_TOKENS: usize = 1_000_000;
10const DEFAULT_MAX_AST_NODES: usize = 1_000_000;
11const DEFAULT_MAX_AST_DEPTH: usize = 512;
12const DEFAULT_MAX_PARENTHESES_DEPTH: usize = 512;
13const DEFAULT_MAX_FUNCTION_CALL_DEPTH: usize = 64;
14#[cfg(target_arch = "wasm32")]
17const DEFAULT_MAX_PARSER_DEPTH: usize = 32;
18#[cfg(not(target_arch = "wasm32"))]
19const DEFAULT_MAX_PARSER_DEPTH: usize = 1_024;
20
21#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
23#[serde(rename_all = "camelCase", deny_unknown_fields)]
24pub struct ParseOptions {
25 #[serde(default)]
26 pub complexity_guard: Option<ComplexityGuardOptions>,
27}
28
29fn default_max_parser_depth() -> Option<usize> {
30 Some(DEFAULT_MAX_PARSER_DEPTH)
31}
32
33pub(crate) fn is_guard_error(error: &Error) -> bool {
35 matches!(error, Error::Parse { message, .. } if message.starts_with("E_GUARD_"))
36}
37
38fn default_max_input_bytes() -> Option<usize> {
39 Some(DEFAULT_MAX_INPUT_BYTES)
40}
41
42fn default_max_tokens() -> Option<usize> {
43 Some(DEFAULT_MAX_TOKENS)
44}
45
46fn default_max_ast_nodes() -> Option<usize> {
47 Some(DEFAULT_MAX_AST_NODES)
48}
49
50fn default_max_ast_depth() -> Option<usize> {
51 Some(DEFAULT_MAX_AST_DEPTH)
52}
53
54fn default_max_parenthesis_depth() -> Option<usize> {
55 Some(DEFAULT_MAX_PARENTHESES_DEPTH)
56}
57
58fn default_max_function_call_depth() -> Option<usize> {
59 Some(DEFAULT_MAX_FUNCTION_CALL_DEPTH)
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
67#[serde(rename_all = "camelCase", deny_unknown_fields)]
68pub struct ComplexityGuardOptions {
69 #[serde(default = "default_max_parser_depth")]
73 pub max_parser_depth: Option<usize>,
74 #[serde(default = "default_max_input_bytes")]
77 pub max_input_bytes: Option<usize>,
78 #[serde(default = "default_max_tokens")]
81 pub max_tokens: Option<usize>,
82 #[serde(default = "default_max_ast_nodes")]
85 pub max_ast_nodes: Option<usize>,
86 #[serde(default = "default_max_ast_depth")]
89 pub max_ast_depth: Option<usize>,
90 #[serde(default = "default_max_parenthesis_depth")]
93 pub max_parenthesis_depth: Option<usize>,
94 #[serde(default = "default_max_function_call_depth")]
97 pub max_function_call_depth: Option<usize>,
98}
99
100#[derive(Debug, Default)]
101pub(crate) struct TokenGuardStats {
102 pub token_count: usize,
103 parenthesis_depth_spans: Vec<Span>,
104 function_depth_spans: Vec<Span>,
105 parenthesis_stack: Vec<bool>,
106 parenthesis_depth: usize,
107 function_depth: usize,
108 previous_significant: Option<TokenType>,
109}
110
111impl TokenGuardStats {
112 pub(crate) fn observe(&mut self, token_type: TokenType, span: Span) {
113 self.token_count += 1;
114 match token_type {
115 TokenType::LParen => {
116 self.parenthesis_depth += 1;
117 if self.parenthesis_depth_spans.len() < self.parenthesis_depth {
118 self.parenthesis_depth_spans.push(span);
119 }
120
121 let is_function_call = self
122 .previous_significant
123 .map(is_function_call_name_token)
124 .unwrap_or(false);
125 self.parenthesis_stack.push(is_function_call);
126 if is_function_call {
127 self.function_depth += 1;
128 if self.function_depth_spans.len() < self.function_depth {
129 self.function_depth_spans.push(span);
130 }
131 }
132 }
133 TokenType::RParen => {
134 self.parenthesis_depth = self.parenthesis_depth.saturating_sub(1);
135 if self.parenthesis_stack.pop().unwrap_or(false) {
136 self.function_depth = self.function_depth.saturating_sub(1);
137 }
138 }
139 _ => {}
140 }
141
142 if !is_trivia_token(token_type) {
143 self.previous_significant = Some(token_type);
144 }
145 }
146}
147
148impl Default for ComplexityGuardOptions {
149 fn default() -> Self {
150 Self {
151 max_parser_depth: default_max_parser_depth(),
152 max_input_bytes: default_max_input_bytes(),
153 max_tokens: default_max_tokens(),
154 max_ast_nodes: default_max_ast_nodes(),
155 max_ast_depth: default_max_ast_depth(),
156 max_parenthesis_depth: default_max_parenthesis_depth(),
157 max_function_call_depth: default_max_function_call_depth(),
158 }
159 }
160}
161
162fn parse_guard_error(code: &str, actual: usize, limit: usize, span: Option<Span>) -> Error {
163 let message = format!("{code}: value {actual} exceeds configured limit {limit}");
164 if let Some(span) = span {
165 Error::parse(message, span.line, span.column, span.start, span.end)
166 } else {
167 Error::parse(message, 0, 0, 0, 0)
168 }
169}
170
171fn generate_guard_error(code: &str, actual: usize, limit: usize) -> Error {
172 Error::generate(format!(
173 "{code}: value {actual} exceeds configured limit {limit}"
174 ))
175}
176
177pub fn enforce_input(sql: &str, options: &ComplexityGuardOptions) -> Result<()> {
179 if let Some(max) = options.max_input_bytes {
180 let input_bytes = sql.len();
181 if input_bytes > max {
182 return Err(parse_guard_error(
183 "E_GUARD_INPUT_TOO_LARGE",
184 input_bytes,
185 max,
186 None,
187 ));
188 }
189 }
190
191 Ok(())
192}
193
194trait GuardToken {
196 fn token_type(&self) -> TokenType;
197 fn span(&self) -> Span;
198}
199
200impl GuardToken for Token {
201 fn token_type(&self) -> TokenType {
202 self.token_type
203 }
204
205 fn span(&self) -> Span {
206 self.span
207 }
208}
209
210impl GuardToken for ParserToken {
211 fn token_type(&self) -> TokenType {
212 self.token_type
213 }
214
215 fn span(&self) -> Span {
216 self.span
217 }
218}
219
220fn enforce_token_slice<T: GuardToken>(
221 tokens: &[T],
222 options: &ComplexityGuardOptions,
223) -> Result<()> {
224 if let Some(max) = options.max_tokens {
225 let token_count = tokens.len();
226 if token_count > max {
227 let span = tokens
228 .get(max)
229 .or_else(|| tokens.last())
230 .map(GuardToken::span);
231 return Err(parse_guard_error(
232 "E_GUARD_TOKEN_BUDGET_EXCEEDED",
233 token_count,
234 max,
235 span,
236 ));
237 }
238 }
239
240 if options.max_parenthesis_depth.is_some() || options.max_function_call_depth.is_some() {
241 let mut paren_depth = 0usize;
242 let mut function_depth = 0usize;
243 let mut paren_stack = Vec::new();
244 let mut previous_significant: Option<TokenType> = None;
245
246 for token in tokens {
247 match token.token_type() {
248 TokenType::LParen => {
249 paren_depth += 1;
250 if let Some(max) = options.max_parenthesis_depth {
251 if paren_depth > max {
252 return Err(parse_guard_error(
253 "E_GUARD_NESTING_DEPTH_EXCEEDED",
254 paren_depth,
255 max,
256 Some(token.span()),
257 ));
258 }
259 }
260
261 let is_function_call = previous_significant
262 .map(is_function_call_name_token)
263 .unwrap_or(false);
264 paren_stack.push(is_function_call);
265 if is_function_call {
266 function_depth += 1;
267 if let Some(max) = options.max_function_call_depth {
268 if function_depth > max {
269 return Err(parse_guard_error(
270 "E_GUARD_FUNCTION_NESTING_DEPTH_EXCEEDED",
271 function_depth,
272 max,
273 Some(token.span()),
274 ));
275 }
276 }
277 }
278 }
279 TokenType::RParen => {
280 paren_depth = paren_depth.saturating_sub(1);
281 if paren_stack.pop().unwrap_or(false) {
282 function_depth = function_depth.saturating_sub(1);
283 }
284 }
285 _ => {}
286 }
287
288 if !is_trivia_token(token.token_type()) {
289 previous_significant = Some(token.token_type());
290 }
291 }
292 }
293
294 Ok(())
295}
296
297pub fn enforce_tokens(tokens: &[Token], options: &ComplexityGuardOptions) -> Result<()> {
298 enforce_token_slice(tokens, options)
299}
300
301pub(crate) fn enforce_parser_tokens(
302 tokens: &[ParserToken],
303 options: &ComplexityGuardOptions,
304) -> Result<()> {
305 enforce_token_slice(tokens, options)
306}
307
308pub(crate) fn enforce_parser_token_stats(
309 tokens: &[ParserToken],
310 stats: &TokenGuardStats,
311 options: &ComplexityGuardOptions,
312) -> Result<()> {
313 if let Some(max) = options.max_tokens {
314 if stats.token_count > max {
315 let span = tokens
316 .get(max)
317 .or_else(|| tokens.last())
318 .map(|token| token.span);
319 return Err(parse_guard_error(
320 "E_GUARD_TOKEN_BUDGET_EXCEEDED",
321 stats.token_count,
322 max,
323 span,
324 ));
325 }
326 }
327
328 let parenthesis_error = options.max_parenthesis_depth.and_then(|max| {
329 stats
330 .parenthesis_depth_spans
331 .get(max)
332 .copied()
333 .map(|span| (span, max))
334 });
335 let function_error = options.max_function_call_depth.and_then(|max| {
336 stats
337 .function_depth_spans
338 .get(max)
339 .copied()
340 .map(|span| (span, max))
341 });
342
343 match (parenthesis_error, function_error) {
344 (Some((paren_span, _paren_max)), Some((function_span, function_max)))
345 if function_span.start < paren_span.start =>
346 {
347 Err(parse_guard_error(
348 "E_GUARD_FUNCTION_NESTING_DEPTH_EXCEEDED",
349 function_max + 1,
350 function_max,
351 Some(function_span),
352 ))
353 }
354 (Some((span, max)), _) => Err(parse_guard_error(
355 "E_GUARD_NESTING_DEPTH_EXCEEDED",
356 max + 1,
357 max,
358 Some(span),
359 )),
360 (None, Some((span, max))) => Err(parse_guard_error(
361 "E_GUARD_FUNCTION_NESTING_DEPTH_EXCEEDED",
362 max + 1,
363 max,
364 Some(span),
365 )),
366 (None, None) => Ok(()),
367 }
368}
369
370fn is_trivia_token(token_type: TokenType) -> bool {
371 matches!(
372 token_type,
373 TokenType::Space | TokenType::Break | TokenType::LineComment | TokenType::BlockComment
374 )
375}
376
377fn is_function_call_name_token(token_type: TokenType) -> bool {
378 matches!(
379 token_type,
380 TokenType::Identifier
381 | TokenType::Var
382 | TokenType::QuotedIdentifier
383 | TokenType::CurrentDate
384 | TokenType::CurrentDateTime
385 | TokenType::CurrentTime
386 | TokenType::CurrentTimestamp
387 | TokenType::CurrentUser
388 | TokenType::If
389 | TokenType::Index
390 | TokenType::Insert
391 | TokenType::Left
392 | TokenType::Replace
393 | TokenType::Right
394 | TokenType::Row
395 )
396}
397
398#[cfg(test)]
399mod token_guard_tests {
400 use super::*;
401 use crate::tokens::Tokenizer;
402 use std::sync::Arc;
403
404 #[test]
405 fn parser_depth_json_contract() {
406 let omitted: ComplexityGuardOptions = serde_json::from_str("{}").unwrap();
407 assert_eq!(omitted.max_parser_depth, Some(DEFAULT_MAX_PARSER_DEPTH));
408 assert_eq!(
409 omitted.max_parser_depth,
410 ComplexityGuardOptions::default().max_parser_depth
411 );
412 for limit in [Some(0), Some(1), Some(2048), None] {
413 let guard = ComplexityGuardOptions {
414 max_parser_depth: limit,
415 ..Default::default()
416 };
417 let json = serde_json::to_string(&guard).unwrap();
418 assert_eq!(
419 serde_json::from_str::<ComplexityGuardOptions>(&json).unwrap(),
420 guard
421 );
422 }
423 for value in ["-1", "1.5", "true", "\"10\""] {
424 assert!(serde_json::from_str::<ComplexityGuardOptions>(&format!(
425 "{{\"maxParserDepth\":{value}}}"
426 ))
427 .is_err());
428 }
429 }
430
431 #[test]
432 fn collected_parser_stats_match_full_token_guard_pass() {
433 let sql: Arc<str> = Arc::from("SELECT outer(inner((value))), other(1, 2) FROM t");
434 let tokenizer = Tokenizer::default();
435 let public_tokens = tokenizer.tokenize(&sql).unwrap();
436 let (parser_tokens, stats) = tokenizer.tokenize_for_parser(&sql).unwrap();
437 let options = [
438 ComplexityGuardOptions::default(),
439 ComplexityGuardOptions {
440 max_tokens: Some(5),
441 ..Default::default()
442 },
443 ComplexityGuardOptions {
444 max_parenthesis_depth: Some(1),
445 ..Default::default()
446 },
447 ComplexityGuardOptions {
448 max_function_call_depth: Some(1),
449 ..Default::default()
450 },
451 ComplexityGuardOptions {
452 max_parenthesis_depth: Some(2),
453 max_function_call_depth: Some(1),
454 ..Default::default()
455 },
456 ];
457
458 for option in options {
459 let full_pass =
460 enforce_tokens(&public_tokens, &option).map_err(|error| error.to_string());
461 let collected = enforce_parser_token_stats(&parser_tokens, &stats, &option)
462 .map_err(|error| error.to_string());
463 assert_eq!(collected, full_pass);
464 }
465 }
466}
467
468pub fn enforce_ast(expressions: &[Expression], options: &ComplexityGuardOptions) -> Result<()> {
470 let Some(max_nodes) = options.max_ast_nodes else {
471 return enforce_ast_depth_only(expressions, options);
472 };
473
474 let stats = ast_stats(expressions, Some(max_nodes), options.max_ast_depth)?;
475 if stats.node_count > max_nodes {
476 return Err(parse_guard_error(
477 "E_GUARD_AST_BUDGET_EXCEEDED",
478 stats.node_count,
479 max_nodes,
480 None,
481 ));
482 }
483
484 if let Some(max_depth) = options.max_ast_depth {
485 if stats.max_depth > max_depth {
486 return Err(parse_guard_error(
487 "E_GUARD_AST_DEPTH_EXCEEDED",
488 stats.max_depth,
489 max_depth,
490 None,
491 ));
492 }
493 }
494
495 Ok(())
496}
497
498pub fn enforce_generate_ast(
500 expression: &Expression,
501 options: &ComplexityGuardOptions,
502) -> Result<()> {
503 let stats = ast_stats(
504 std::slice::from_ref(expression),
505 options.max_ast_nodes,
506 options.max_ast_depth,
507 )?;
508
509 if let Some(max_nodes) = options.max_ast_nodes {
510 if stats.node_count > max_nodes {
511 return Err(generate_guard_error(
512 "E_GUARD_AST_BUDGET_EXCEEDED",
513 stats.node_count,
514 max_nodes,
515 ));
516 }
517 }
518
519 if let Some(max_depth) = options.max_ast_depth {
520 if stats.max_depth > max_depth {
521 return Err(generate_guard_error(
522 "E_GUARD_AST_DEPTH_EXCEEDED",
523 stats.max_depth,
524 max_depth,
525 ));
526 }
527 }
528
529 Ok(())
530}
531
532fn enforce_ast_depth_only(
533 expressions: &[Expression],
534 options: &ComplexityGuardOptions,
535) -> Result<()> {
536 let Some(max_depth) = options.max_ast_depth else {
537 return Ok(());
538 };
539
540 let stats = ast_stats(expressions, None, Some(max_depth))?;
541 if stats.max_depth > max_depth {
542 return Err(parse_guard_error(
543 "E_GUARD_AST_DEPTH_EXCEEDED",
544 stats.max_depth,
545 max_depth,
546 None,
547 ));
548 }
549
550 Ok(())
551}
552
553#[derive(Debug, Clone, Copy, Default)]
554struct AstStats {
555 node_count: usize,
556 max_depth: usize,
557}
558
559fn ast_stats(
560 expressions: &[Expression],
561 max_nodes: Option<usize>,
562 max_depth: Option<usize>,
563) -> Result<AstStats> {
564 let mut stats = AstStats::default();
565 let mut stack: Vec<(&Expression, usize)> = expressions.iter().rev().map(|e| (e, 0)).collect();
566
567 while let Some((expr, depth)) = stack.pop() {
568 stats.node_count += 1;
569 stats.max_depth = stats.max_depth.max(depth);
570
571 if let Some(max) = max_nodes {
572 if stats.node_count > max {
573 return Ok(stats);
574 }
575 }
576
577 if let Some(max) = max_depth {
578 if stats.max_depth > max {
579 return Ok(stats);
580 }
581 }
582
583 push_ast_children(&mut stack, expr, depth);
584 }
585
586 Ok(stats)
587}
588
589fn push_ast_children<'a>(
590 stack: &mut Vec<(&'a Expression, usize)>,
591 expr: &'a Expression,
592 depth: usize,
593) {
594 match expr {
595 Expression::And(op) if is_commentless_binary_op(op) => {
596 push_connector_child(stack, &op.right, depth, ConnectorKind::And);
597 push_connector_child(stack, &op.left, depth, ConnectorKind::And);
598 }
599 Expression::Or(op) if is_commentless_binary_op(op) => {
600 push_connector_child(stack, &op.right, depth, ConnectorKind::Or);
601 push_connector_child(stack, &op.left, depth, ConnectorKind::Or);
602 }
603 _ => {
604 let child_start = stack.len();
605 crate::ast_children::for_each_child(expr, |_, child| {
606 stack.push((child, depth + 1));
607 });
608 stack[child_start..].reverse();
609 }
610 }
611}
612
613fn push_connector_child<'a>(
614 stack: &mut Vec<(&'a Expression, usize)>,
615 child: &'a Expression,
616 depth: usize,
617 kind: ConnectorKind,
618) {
619 let child_depth = if is_commentless_connector(child, kind) {
620 depth
621 } else {
622 depth + 1
623 };
624 stack.push((child, child_depth));
625}
626
627fn is_commentless_connector(expr: &Expression, kind: ConnectorKind) -> bool {
628 match (kind, expr) {
629 (ConnectorKind::And, Expression::And(op)) | (ConnectorKind::Or, Expression::Or(op)) => {
630 is_commentless_binary_op(op)
631 }
632 _ => false,
633 }
634}
635
636fn is_commentless_binary_op(op: &crate::expressions::BinaryOp) -> bool {
637 op.left_comments.is_empty()
638 && op.operator_comments.is_empty()
639 && op.trailing_comments.is_empty()
640}
641
642#[derive(Debug, Clone, Copy)]
643enum ConnectorKind {
644 And,
645 Or,
646}