@@ -249,6 +249,18 @@ fn eval_expr(
249249 )
250250 None => normal_result(state, [] )
251251 }
252+ Expr_Lexscan =>
253+ match node_child(expr, "expr") {
254+ Some (scrutinee) =>
255+ eval_lex_scan(
256+ scrutinee,
257+ node_child_items(expr, "cases"),
258+ state,
259+ spec,
260+ findings,
261+ )
262+ None => normal_result(state, [] )
263+ }
252264 Expr_Try =>
253265 match node_child(expr, "body") {
254266 Some (body) =>
@@ -2049,6 +2061,56 @@ fn bind_lex_pattern(
20492061 }
20502062}
20512063
2064+ ///|
2065+ fn lex_scan_pattern_bound_roots(pattern : @untyped_ast.Node ) -> Array [String ] {
2066+ let roots : Array [String ] = []
2067+ add_lex_scan_pattern_bound_roots(roots, pattern)
2068+ roots
2069+ }
2070+
2071+ ///|
2072+ fn add_lex_scan_pattern_bound_roots(
2073+ roots : Array [String ],
2074+ pattern : @untyped_ast.Node ,
2075+ ) -> Unit {
2076+ match pattern.kind {
2077+ LexScanCasePattern_Binder =>
2078+ if node_first_child(pattern) is Some (binder) {
2079+ add_binder_root_name(roots, binder)
2080+ }
2081+ LexScanCasePattern_Pattern =>
2082+ if node_first_child(pattern) is Some (pat) {
2083+ add_regex_pattern_bound_roots(roots, pat)
2084+ }
2085+ LexScanCasePattern_Wildcard => ()
2086+ _ => ()
2087+ }
2088+ }
2089+
2090+ ///|
2091+ fn bind_lex_scan_pattern(
2092+ pattern : @untyped_ast.Node ,
2093+ value : TaintTree ,
2094+ state : TaintState ,
2095+ ) -> TaintState {
2096+ match pattern.kind {
2097+ LexScanCasePattern_Binder =>
2098+ if node_first_child(pattern) is Some (binder) {
2099+ bind_binder(binder, value, state)
2100+ } else {
2101+ state
2102+ }
2103+ LexScanCasePattern_Pattern =>
2104+ if node_first_child(pattern) is Some (pat) {
2105+ bind_regex_pattern(pat, value, state)
2106+ } else {
2107+ state
2108+ }
2109+ LexScanCasePattern_Wildcard => state
2110+ _ => state
2111+ }
2112+ }
2113+
20522114///|
20532115fn regex_pattern_bound_roots(pattern : @untyped_ast.Node ) -> Array [String ] {
20542116 let roots : Array [String ] = []
@@ -2405,6 +2467,90 @@ fn eval_lex_case(
24052467 restore_result_scoped_roots(result, pattern_result.state, roots)
24062468}
24072469
2470+ ///|
2471+ fn eval_lex_scan(
2472+ scrutinee : @untyped_ast.Node ,
2473+ cases : Array [@untyped_ast.Node ],
2474+ state : TaintState ,
2475+ spec : TaintSpec ,
2476+ findings : Array [SinkFinding ],
2477+ ) -> EvalResult {
2478+ let scrutinee_result = eval_expr(scrutinee, state, spec, findings)
2479+ if scrutinee_result.flow != FlowNormal {
2480+ return scrutinee_result
2481+ }
2482+ let normal_states : Array [TaintState ] = []
2483+ let mut branch_value : TaintTree = []
2484+ let exit_states : Array [TaintState ] = []
2485+ let mut exit_flow = FlowNormal
2486+ let mut exit_value : TaintTree = []
2487+ for case in cases {
2488+ let case_result = eval_lex_scan_case(
2489+ case,
2490+ scrutinee_result.value,
2491+ scrutinee_result.state,
2492+ spec,
2493+ findings,
2494+ )
2495+ if case_result.flow == FlowNormal {
2496+ normal_states.push(case_result.state)
2497+ branch_value = tree_merge(branch_value, case_result.value)
2498+ } else {
2499+ if exit_states.is_empty() {
2500+ exit_flow = case_result.flow
2501+ exit_value = case_result.value
2502+ }
2503+ exit_states.push(case_result.state)
2504+ }
2505+ }
2506+ if normal_states.length() > 0 {
2507+ normal_result(state_merge_all(normal_states), branch_value)
2508+ } else if exit_states.length() > 0 {
2509+ flow_result(exit_flow, state_merge_all(exit_states), exit_value)
2510+ } else {
2511+ normal_result(scrutinee_result.state, [] )
2512+ }
2513+ }
2514+
2515+ ///|
2516+ fn eval_lex_scan_case(
2517+ case : @untyped_ast.Node ,
2518+ value : TaintTree ,
2519+ base_state : TaintState ,
2520+ spec : TaintSpec ,
2521+ findings : Array [SinkFinding ],
2522+ ) -> EvalResult {
2523+ let pattern = match node_child(case, "pat") {
2524+ Some (pattern) => pattern
2525+ None => return normal_result(base_state, [] )
2526+ }
2527+ let body = match node_child(case, "body") {
2528+ Some (body) => body
2529+ None => return normal_result(base_state, [] )
2530+ }
2531+ let roots = lex_scan_pattern_bound_roots(pattern)
2532+ let bound = bind_lex_scan_pattern(pattern, value, base_state)
2533+ let result = if node_optional_child(case, "guard") is Some (guard_expr) {
2534+ let guard_result = eval_condition_scope(guard_expr, bound, spec, findings)
2535+ if guard_result.flow == FlowNormal {
2536+ restore_result_scoped_roots(
2537+ eval_expr(body, guard_result.true_state, spec, findings),
2538+ guard_result.base_state,
2539+ guard_result.bound_roots,
2540+ )
2541+ } else {
2542+ {
2543+ state: guard_result.base_state,
2544+ value: guard_result.value,
2545+ flow: guard_result.flow,
2546+ }
2547+ }
2548+ } else {
2549+ eval_expr(body, bound, spec, findings)
2550+ }
2551+ restore_result_scoped_roots(result, base_state, roots)
2552+ }
2553+
24082554///|
24092555fn eval_try(
24102556 body : @untyped_ast.Node ,
@@ -2856,7 +3002,7 @@ fn eval_unknown_expr(
28563002 }
28573003 None => normal_result(state, [] )
28583004 }
2859- Expr_Interp =>
3005+ Expr_Interp | Expr_BytesInterp =>
28603006 eval_interp_elems(node_child_items(expr, "elems"), state, spec, findings)
28613007 Expr_Map => {
28623008 let mut current = state
0 commit comments