use std::borrow::Cow;
use std::collections::HashMap;
use once_cell::sync::Lazy;
use regex::Regex;
use tracing::debug;
use crate::compiler::CompiledRuleLibrary;
use crate::utils::{VersionExtractor, DetectionUpdater};
pub struct UrlAnalyzer;
impl UrlAnalyzer {
pub fn analyze(
compiled_lib: &CompiledRuleLibrary,
urls: &[&str],
detected: &mut HashMap<String, (u8, Option<String>)>,
) {
for url in urls {
for (_, compiled_tech) in &compiled_lib.tech_patterns {
if let Some((conf, _)) = detected.get(&compiled_tech.name) {
if *conf >= 100 {
continue;
}
}
let Some(url_patterns) = &compiled_tech.url_patterns else {
continue;
};
for pattern in url_patterns.iter() {
if let Some(captures) = pattern.regex.captures(url) {
let version = VersionExtractor::extract(&pattern.version_template, &captures);
debug!(
"URL匹配成功:技术={},版本={:?},规则={}",
compiled_tech.name,
version,
pattern.regex.as_str()
);
DetectionUpdater::update(
detected,
compiled_tech.name.clone(),
Some(pattern.confidence),
version,
);
break; }
}
}
}
}
}
pub struct HeaderAnalyzer;
impl HeaderAnalyzer {
pub fn analyze(
compiled_lib: &CompiledRuleLibrary,
headers: &HashMap<String, String>,
detected: &mut HashMap<String, (u8, Option<String>)>,
) {
for (_, compiled_tech) in &compiled_lib.tech_patterns {
let Some(header_patterns) = &compiled_tech.header_patterns else {
continue;
};
for (header_name, patterns) in header_patterns.iter() {
let Some(header_value) = headers.get(header_name) else {
continue;
};
for pattern in patterns {
if let Some(captures) = pattern.regex.captures(header_value) {
let version = VersionExtractor::extract(&pattern.version_template, &captures);
debug!(
"Header匹配成功:技术={},Header={},版本={:?},规则={}",
compiled_tech.name,
header_name,
version,
pattern.regex.as_str()
);
DetectionUpdater::update(
detected,
compiled_tech.name.clone(),
Some(pattern.confidence),
version,
);
}
}
}
}
}
}
pub struct HtmlAnalyzer;
impl HtmlAnalyzer {
pub fn analyze(
compiled_lib: &CompiledRuleLibrary,
html: &Cow<str>,
detected: &mut HashMap<String, (u8, Option<String>)>,
) {
for (_, compiled_tech) in &compiled_lib.tech_patterns {
let Some(html_patterns) = &compiled_tech.html_patterns else {
continue;
};
for pattern in html_patterns.iter() {
if let Some(captures) = pattern.regex.captures(html) {
let version = VersionExtractor::extract(&pattern.version_template, &captures);
DetectionUpdater::update(
detected,
compiled_tech.name.clone(),
Some(pattern.confidence),
version,
);
}
}
}
}
}
pub struct ScriptAnalyzer;
impl ScriptAnalyzer {
pub fn analyze(
compiled_lib: &CompiledRuleLibrary,
script_srcs: &[String],
detected: &mut HashMap<String, (u8, Option<String>)>,
) {
static JQUERY_VERSION_REGEX: Lazy<Regex> = Lazy::new(|| {
Regex::new(r#"jquery-(\d+\.\d+\.\d+)|\/(\d+\.\d+\.\d+)\/jquery"#).unwrap()
});
for src in script_srcs {
let jquery_version = JQUERY_VERSION_REGEX.captures(src).and_then(|cap| {
cap.get(1).map(|m| m.as_str().to_string()).or_else(|| cap.get(2).map(|m| m.as_str().to_string()))
});
for (_, compiled_tech) in &compiled_lib.tech_patterns {
let Some(script_patterns) = &compiled_tech.script_patterns else {
continue;
};
for pattern in script_patterns.iter() {
if pattern.regex.is_match(src) {
let version = if compiled_tech.name == "jQuery" {
jquery_version.clone()
} else {
pattern.regex.captures(src).and_then(|cap| {
VersionExtractor::extract(&pattern.version_template, &cap)
})
};
DetectionUpdater::update(
detected,
compiled_tech.name.clone(),
Some(pattern.confidence),
version,
);
}
}
}
}
}
}
pub struct MetaAnalyzer;
impl MetaAnalyzer {
pub fn analyze(
compiled_lib: &CompiledRuleLibrary,
meta_tags: &[(String, String)],
detected: &mut HashMap<String, (u8, Option<String>)>,
) {
for (meta_name, content) in meta_tags {
for (_, compiled_tech) in &compiled_lib.tech_patterns {
let Some(meta_patterns) = &compiled_tech.meta_patterns else {
continue;
};
let Some(patterns) = meta_patterns.get(meta_name) else {
continue;
};
for pattern in patterns {
if let Some(captures) = pattern.regex.captures(content) {
let version = VersionExtractor::extract(&pattern.version_template, &captures);
DetectionUpdater::update(
detected,
compiled_tech.name.clone(),
Some(pattern.confidence),
version,
);
}
}
}
}
}
}