whatwaf 1.10.6

Heuristic web application firewall (WAF) detector.
Documentation
use once_cell::sync::Lazy;
use regex::Regex;
use sha2::{Digest, Sha256};

#[derive(Debug)]
pub struct Asset {
    pub mime: String,
    pub hash: String,
    pub size: usize,
}

#[derive(Debug)]
pub struct HttpResponse {
    pub status: u16,
    pub content_length: Option<u64>,
    pub headers: Vec<(String, String)>,
    pub body: String,
    pub assets: Vec<Asset>,
}

static SVG_RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?is)<svg.*?</svg>").unwrap());

pub fn fetch(
    client: &reqwest::blocking::Client,
    url: &str,
) -> Result<HttpResponse, reqwest::Error> {
    let res = client.get(url).send()?;

    let status = res.status().as_u16();
    let content_length = res.content_length();
    let headers = res
        .headers()
        .iter()
        .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string()))
        .collect();
    let body = res.text().unwrap_or_default();
    let assets = extract_svg_assets(&body);

    Ok(HttpResponse {
        status,
        content_length,
        headers,
        body,
        assets,
    })
}

pub fn add_param(original: &str, key: &str, value: &str) -> String {
    if let Ok(mut url) = reqwest::Url::parse(original) {
        url.query_pairs_mut().append_pair(key, value);
        return url.to_string();
    }

    original.to_string()
}

pub fn extract_svg_assets(body: &str) -> Vec<Asset> {
    SVG_RE
        .find_iter(body)
        .map(|m| {
            let bytes = m.as_str().as_bytes();

            Asset {
                mime: "image/svg+xml".into(),
                hash: sha256_hex(bytes),
                size: bytes.len(),
            }
        })
        .collect()
}

fn sha256_hex(bytes: &[u8]) -> String {
    let mut hasher = Sha256::new();
    hasher.update(bytes);
    hex::encode(hasher.finalize())
}