vyre 0.4.0

GPU compute intermediate representation with a standard operation library
Documentation
//! Rule detectors for injection and parser-abuse payloads.

use super::bytes::contains_ci;
use super::decode::percent_decode_lower;
use super::{validate_input, DetectionError};

/// Return true for eval plus decoder plus character-code JavaScript obfuscation.
///
/// # Errors
///
/// Returns `Fix: ...` when input exceeds the detector limit.
pub fn detect_obfuscated_js(input: &[u8]) -> Result<bool, DetectionError> {
    validate_input(input)?;
    let normalized = percent_decode_lower(input);
    let eval = contains_ci(&normalized, b"eval(") || contains_ci(&normalized, b"settimeout(");
    let decoder = contains_ci(&normalized, b"atob(") || contains_ci(&normalized, b"unescape(");
    let charcode = contains_ci(&normalized, b"fromcharcode")
        || contains_ci(&normalized, br"\x")
        || contains_ci(&normalized, br"\u00");
    Ok(eval && decoder && charcode)
}

/// Return true for SQL injection tautology, UNION, comment, or stacked-query payloads.
///
/// # Errors
///
/// Returns `Fix: ...` when input exceeds the detector limit.
pub fn detect_sql_injection(input: &[u8]) -> Result<bool, DetectionError> {
    validate_input(input)?;
    let s = percent_decode_lower(input);
    let sql_words = contains_ci(&s, b"select ") && contains_ci(&s, b" from ");
    let union_select = contains_ci(&s, b"union select");
    let tautology = contains_ci(&s, b" or 1=1")
        || contains_ci(&s, b"' or '1'='1")
        || contains_ci(&s, b"\" or \"1\"=\"1")
        || contains_ci(&s, b" or true");
    let comment = contains_ci(&s, b"--") || contains_ci(&s, b"/*") || contains_ci(&s, b"#");
    let stacked = contains_ci(&s, b"; drop ") || contains_ci(&s, b"; select ");
    Ok(union_select || (tautology && (comment || sql_words)) || stacked)
}

/// Return true for script tags, JavaScript URLs, or executable event handlers.
///
/// # Errors
///
/// Returns `Fix: ...` when input exceeds the detector limit.
pub fn detect_xss(input: &[u8]) -> Result<bool, DetectionError> {
    validate_input(input)?;
    let s = percent_decode_lower(input);
    Ok(contains_ci(&s, b"<script")
        || contains_ci(&s, b"javascript:")
        || contains_ci(&s, b"onerror=")
        || contains_ci(&s, b"onload=")
        || contains_ci(&s, b"<svg") && (contains_ci(&s, b"onload") || contains_ci(&s, b"script")))
}

/// Return true for traversal into local sensitive files.
///
/// # Errors
///
/// Returns `Fix: ...` when input exceeds the detector limit.
pub fn detect_lfi(input: &[u8]) -> Result<bool, DetectionError> {
    validate_input(input)?;
    let s = percent_decode_lower(input);
    Ok((contains_ci(&s, b"../") || contains_ci(&s, br"..\"))
        && (contains_ci(&s, b"/etc/passwd")
            || contains_ci(&s, b"boot.ini")
            || contains_ci(&s, b"win.ini")
            || contains_ci(&s, b"/proc/self/environ")))
}

/// Return true for remote URLs supplied to include-like parameters.
///
/// # Errors
///
/// Returns `Fix: ...` when input exceeds the detector limit.
pub fn detect_rfi(input: &[u8]) -> Result<bool, DetectionError> {
    validate_input(input)?;
    let s = percent_decode_lower(input);
    let param = contains_ci(&s, b"file=")
        || contains_ci(&s, b"path=")
        || contains_ci(&s, b"page=")
        || contains_ci(&s, b"include=");
    let remote = contains_ci(&s, b"http://") || contains_ci(&s, b"https://");
    Ok(param && remote)
}

/// Return true for shell metacharacters paired with command names.
///
/// # Errors
///
/// Returns `Fix: ...` when input exceeds the detector limit.
pub fn detect_command_injection(input: &[u8]) -> Result<bool, DetectionError> {
    validate_input(input)?;
    let s = percent_decode_lower(input);
    let meta = contains_ci(&s, b";")
        || contains_ci(&s, b"&&")
        || contains_ci(&s, b"||")
        || contains_ci(&s, b"`")
        || contains_ci(&s, b"$(")
        || contains_ci(&s, b"|");
    let command = contains_ci(&s, b" cat ")
        || contains_ci(&s, b" id")
        || contains_ci(&s, b" whoami")
        || contains_ci(&s, b" curl ")
        || contains_ci(&s, b" wget ")
        || contains_ci(&s, b" nc ")
        || contains_ci(&s, b" /bin/sh")
        || contains_ci(&s, b" powershell");
    Ok(meta && command)
}

/// Return true for URL payloads targeting loopback, metadata, or private hosts.
///
/// # Errors
///
/// Returns `Fix: ...` when input exceeds the detector limit.
pub fn detect_ssrf(input: &[u8]) -> Result<bool, DetectionError> {
    validate_input(input)?;
    let s = percent_decode_lower(input);
    let has_url = contains_ci(&s, b"http://") || contains_ci(&s, b"https://");
    let target = contains_ci(&s, b"localhost")
        || contains_ci(&s, b"127.0.0.1")
        || contains_ci(&s, b"0.0.0.0")
        || contains_ci(&s, b"169.254.169.254")
        || contains_ci(&s, b"10.")
        || contains_ci(&s, b"192.168.")
        || contains_ci(&s, b"172.16.");
    Ok(has_url && target)
}

/// Return true for decoded or raw parent-directory traversal.
///
/// # Errors
///
/// Returns `Fix: ...` when input exceeds the detector limit.
pub fn detect_path_traversal(input: &[u8]) -> Result<bool, DetectionError> {
    validate_input(input)?;
    let s = percent_decode_lower(input);
    Ok(contains_ci(&s, b"../")
        || contains_ci(&s, br"..\")
        || contains_ci(&s, b"..//")
        || contains_ci(&s, br"..\\"))
}

/// Return true for XML external entity declarations with SYSTEM or PUBLIC.
///
/// # Errors
///
/// Returns `Fix: ...` when input exceeds the detector limit.
pub fn detect_xxe(input: &[u8]) -> Result<bool, DetectionError> {
    validate_input(input)?;
    let s = percent_decode_lower(input);
    Ok(contains_ci(&s, b"<!doctype")
        && contains_ci(&s, b"<!entity")
        && (contains_ci(&s, b"system") || contains_ci(&s, b"public")))
}