///|
/// Defensive limits applied while compiling untrusted rule text.
pub(all) struct CompileLimits {
max_source_length : Int
max_ast_nodes : Int
max_ast_depth : Int
} derive(Eq, Debug, ToJson, FromJson)
///|
/// Conservative defaults suitable for API and CI use.
pub fn CompileLimits::default() -> CompileLimits {
{ max_source_length: 16384, max_ast_nodes: 4096, max_ast_depth: 128 }
}
///|
fn Expr::metrics(self : Expr) -> (Int, Int) {
match self {
Literal(_, _) | Variable(_, _) => (1, 1)
Member(target, _, _) | OptionalMember(target, _, _) | Unary(_, target, _) => {
let (nodes, depth) = target.metrics()
(nodes + 1, depth + 1)
}
Index(target, index, _)
| OptionalIndex(target, index, _)
| Binary(target, _, index, _) => {
let (left_nodes, left_depth) = target.metrics()
let (right_nodes, right_depth) = index.metrics()
(
left_nodes + right_nodes + 1,
(if left_depth > right_depth { left_depth } else { right_depth }) + 1,
)
}
ArrayLiteral(items, _) | Call(_, items, _) => {
let mut nodes = 1
let mut depth = 1
for item in items {
let (child_nodes, child_depth) = item.metrics()
nodes = nodes + child_nodes
if child_depth + 1 > depth {
depth = child_depth + 1
}
}
(nodes, depth)
}
}
}
///|
fn validate_compile_limits(limits : CompileLimits) -> Result[Unit, Diagnostic] {
if limits.max_source_length <= 0 ||
limits.max_ast_nodes <= 0 ||
limits.max_ast_depth <= 0 {
Err(
Diagnostic::new(
Configure,
"C010",
"compile limits must be positive integers",
Span::new(0, 0),
hint="Set every compile limit to at least 1.",
),
)
} else {
Ok(())
}
}