nyx-scanner 0.6.1

A multi-language static analysis tool for detecting security vulnerabilities
Documentation
use crate::evidence::Confidence;
use crate::patterns::{Pattern, PatternCategory, PatternTier, Severity};

/// Ruby AST patterns.
///
/// Taint rules cover `system`/`exec` (command injection), `eval` (code
/// execution), and `puts`/`print` (output sinks).  AST patterns here focus on
/// **deserialization** (YAML.load, Marshal.load), **instance_eval/class_eval**,
/// **backtick shell**, **send with dynamic arg**, and **constantize**.
pub const PATTERNS: &[Pattern] = &[
    // ── Tier A: Code execution ─────────────────────────────────────────
    Pattern {
        id: "rb.code_exec.eval",
        description: "Kernel#eval runs dynamic code",
        query: r#"(call (identifier) @id (#eq? @id "eval")) @vuln"#,
        severity: Severity::High,
        tier: PatternTier::A,
        category: PatternCategory::CodeExec,
        confidence: Confidence::High,
    },
    Pattern {
        id: "rb.code_exec.instance_eval",
        description: "instance_eval evaluates a string in object context",
        query: r#"(call
                     method: (identifier) @id (#eq? @id "instance_eval"))
                   @vuln"#,
        severity: Severity::High,
        tier: PatternTier::A,
        category: PatternCategory::CodeExec,
        confidence: Confidence::High,
    },
    Pattern {
        id: "rb.code_exec.class_eval",
        description: "class_eval / module_eval evaluates a string in class context",
        query: r#"(call
                     method: (identifier) @id (#match? @id "^(class_eval|module_eval)$"))
                   @vuln"#,
        severity: Severity::High,
        tier: PatternTier::A,
        category: PatternCategory::CodeExec,
        confidence: Confidence::High,
    },
    // ── Tier A: Command execution ──────────────────────────────────────
    Pattern {
        id: "rb.cmdi.backtick",
        description: "Backtick shell execution",
        query: r#"(subshell) @vuln"#,
        severity: Severity::High,
        tier: PatternTier::A,
        category: PatternCategory::CommandExec,
        confidence: Confidence::High,
    },
    // ── Tier A: Shell execution ─────────────────────────────────────────
    Pattern {
        id: "rb.cmdi.system_interp",
        description: "system/exec call runs a command",
        query: r#"(call
                     method: (identifier) @m (#match? @m "^(system|exec)$"))
                   @vuln"#,
        severity: Severity::High,
        tier: PatternTier::A,
        category: PatternCategory::CommandExec,
        confidence: Confidence::High,
    },
    // ── Tier A: Deserialization ────────────────────────────────────────
    Pattern {
        id: "rb.deser.yaml_load",
        description: "YAML.load deserializes arbitrary objects (use safe_load instead)",
        query: r#"(call
                     receiver: (constant) @recv (#match? @recv "^(YAML|Psych)$")
                     method: (identifier) @m (#eq? @m "load"))
                   @vuln"#,
        severity: Severity::High,
        tier: PatternTier::A,
        category: PatternCategory::Deserialization,
        confidence: Confidence::High,
    },
    Pattern {
        id: "rb.deser.marshal_load",
        description: "Marshal.load deserializes arbitrary Ruby objects",
        query: r#"(call
                     receiver: (constant) @recv (#eq? @recv "Marshal")
                     method: (identifier) @m (#eq? @m "load"))
                   @vuln"#,
        severity: Severity::High,
        tier: PatternTier::A,
        category: PatternCategory::Deserialization,
        confidence: Confidence::High,
    },
    // ── Tier A: Reflection ─────────────────────────────────────────────
    Pattern {
        id: "rb.reflection.send_dynamic",
        description: "send() with a non-symbol argument is arbitrary method dispatch",
        query: r#"(call
                     method: (identifier) @m (#eq? @m "send")
                     arguments: (argument_list
                       [(identifier) (string (interpolation)+)] @vuln))
        "#,
        severity: Severity::Medium,
        tier: PatternTier::B,
        category: PatternCategory::Reflection,
        confidence: Confidence::Medium,
    },
    Pattern {
        id: "rb.reflection.constantize",
        description: "constantize / safe_constantize performs dynamic class resolution",
        query: r#"(call
                     method: (identifier) @m (#match? @m "^(constantize|safe_constantize)$"))
                   @vuln"#,
        severity: Severity::Medium,
        tier: PatternTier::A,
        category: PatternCategory::Reflection,
        confidence: Confidence::High,
    },
    // ── Tier A: SSRF ───────────────────────────────────────────────────
    Pattern {
        id: "rb.ssrf.open_uri",
        description: "Kernel#open with an HTTP URL is an SSRF sink via open-uri",
        query: r#"(call
                     method: (identifier) @m (#eq? @m "open")
                     arguments: (argument_list
                       (string) @url (#match? @url "^\"https?://")))
                   @vuln"#,
        severity: Severity::Medium,
        tier: PatternTier::A,
        category: PatternCategory::InsecureTransport,
        confidence: Confidence::High,
    },
    // ── Tier A: Crypto ─────────────────────────────────────────────────
    Pattern {
        id: "rb.crypto.md5",
        description: "Digest::MD5 is a weak hash algorithm",
        query: r#"(scope_resolution
                     name: (constant) @c (#eq? @c "MD5"))
                   @vuln"#,
        severity: Severity::Low,
        tier: PatternTier::A,
        category: PatternCategory::Crypto,
        confidence: Confidence::Medium,
    },
];