From fc69eff01f277a6ea3ff940d3c90d8ae69031728 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 09:33:42 +0000 Subject: [PATCH] refactor(detect): reduce complexity of parse_header_line in detect.rs Extract parse_header_value helper to eliminate the nested quoted-string parser from parse_header_line. The quoted-value logic (escape handling, closing-quote scan, tail slicing) was inlined inside the source= branch, adding ~4 levels of nesting. Moving it into a dedicated private function reduces parse_header_line to a flat dispatch loop with no deep nesting. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/detect.rs | 69 ++++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/src/detect.rs b/src/detect.rs index 36cbcff5..c47533f7 100644 --- a/src/detect.rs +++ b/src/detect.rs @@ -234,37 +234,9 @@ pub fn parse_header_line(line: &str) -> Option { while !remaining.is_empty() { remaining = remaining.trim_start(); if let Some(after) = remaining.strip_prefix("source=") { - // Handle quoted value: source="path with spaces" - if let Some(after_quote) = after.strip_prefix('"') { - // Find closing quote, skipping escaped quotes (\") - let mut value = String::new(); - let mut chars = after_quote.char_indices(); - let mut end_pos = after_quote.len(); - while let Some((i, c)) = chars.next() { - if c == '\\' { - // Consume the escaped character - if let Some((_, escaped)) = chars.next() { - value.push(escaped); - } - } else if c == '"' { - end_pos = i; - break; - } else { - value.push(c); - } - } - source = value; - remaining = if end_pos < after_quote.len() { - &after_quote[end_pos + 1..] - } else { - "" - }; - } else { - // Unquoted: take until next whitespace - let end = after.find(' ').unwrap_or(after.len()); - source = after[..end].to_string(); - remaining = &after[end..]; - } + let (value, tail) = parse_header_value(after); + source = value; + remaining = tail; } else if let Some(after) = remaining.strip_prefix("version=") { let end = after.find(' ').unwrap_or(after.len()); version = after[..end].to_string(); @@ -283,6 +255,41 @@ pub fn parse_header_line(line: &str) -> Option { Some(HeaderMetadata { source, version }) } +/// Parse a header field value that is either quoted (`"…"`) or unquoted (up to whitespace). +/// +/// Quoted values support backslash-escaped characters (e.g. `\"`). +/// Returns `(value, remaining_input)`. +fn parse_header_value(input: &str) -> (String, &str) { + if let Some(after_quote) = input.strip_prefix('"') { + // Quoted value: scan until closing `"`, honouring `\"` escapes. + let mut value = String::new(); + let mut chars = after_quote.char_indices(); + let mut end_pos = after_quote.len(); + while let Some((i, c)) = chars.next() { + if c == '\\' { + if let Some((_, escaped)) = chars.next() { + value.push(escaped); + } + } else if c == '"' { + end_pos = i; + break; + } else { + value.push(c); + } + } + let tail = if end_pos < after_quote.len() { + &after_quote[end_pos + 1..] + } else { + "" + }; + (value, tail) + } else { + // Unquoted: take until next whitespace. + let end = input.find(' ').unwrap_or(input.len()); + (input[..end].to_string(), &input[end..]) + } +} + #[cfg(test)] mod tests { use super::*;