mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-12 19:55:14 +02:00
141 lines
6.2 KiB
Rust
141 lines
6.2 KiB
Rust
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,
|
|
},
|
|
];
|