Refactor codebase for consistent indentation and formatting

- Standardized spacing and indentation across multiple modules for improved readability.
- Reorganized `patterns` and `utils` imports for consistency.
- Updated `NyxError` and `NyxResult` related implementations to maintain consistent formatting.
- Enhanced readability in AST patterns for better clarity and maintainability.
This commit is contained in:
elipeter 2025-06-24 20:27:06 +02:00
parent b3870997d7
commit 14a549ac39
26 changed files with 1314 additions and 1221 deletions

View file

@ -1,40 +1,40 @@
use crate::patterns::{Pattern, Severity};
pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "strcpy_call",
description: "strcpy() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"strcpy\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "strcat_call",
description: "strcat() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"strcat\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "sprintf_call",
description: "sprintf() (no length limit)",
query: "(call_expression function: (identifier) @id (#eq? @id \"sprintf\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "gets_call",
description: "gets() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"gets\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "scanf_with_percent_s",
description: "scanf(\"%s\") without length specifier",
query: "(call_expression function: (identifier) @id (#eq? @id \"scanf\") arguments: (argument_list (string_literal) @fmt (#match? @fmt \".*%s.*\"))) @vuln",
severity: Severity::High,
},
Pattern {
id: "system_call",
description: "system() shell execution",
query: "(call_expression function: (identifier) @id (#eq? @id \"system\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "strcpy_call",
description: "strcpy() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"strcpy\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "strcat_call",
description: "strcat() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"strcat\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "sprintf_call",
description: "sprintf() (no length limit)",
query: "(call_expression function: (identifier) @id (#eq? @id \"sprintf\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "gets_call",
description: "gets() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"gets\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "scanf_with_percent_s",
description: "scanf(\"%s\") without length specifier",
query: "(call_expression function: (identifier) @id (#eq? @id \"scanf\") arguments: (argument_list (string_literal) @fmt (#match? @fmt \".*%s.*\"))) @vuln",
severity: Severity::High,
},
Pattern {
id: "system_call",
description: "system() shell execution",
query: "(call_expression function: (identifier) @id (#eq? @id \"system\")) @vuln",
severity: Severity::Medium,
},
];

View file

@ -1,40 +1,40 @@
use crate::patterns::{Pattern, Severity};
pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "strcpy_call",
description: "strcpy() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"strcpy\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "strcat_call",
description: "strcat() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"strcat\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "sprintf_call",
description: "sprintf() (no length limit)",
query: "(call_expression function: (identifier) @id (#eq? @id \"sprintf\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "gets_call",
description: "gets() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"gets\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "system_call",
description: "system() shell execution",
query: "(call_expression function: (identifier) @id (#eq? @id \"system\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "reinterpret_cast",
description: "reinterpret_cast usage",
query: "(reinterpret_cast_expression) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "strcpy_call",
description: "strcpy() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"strcpy\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "strcat_call",
description: "strcat() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"strcat\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "sprintf_call",
description: "sprintf() (no length limit)",
query: "(call_expression function: (identifier) @id (#eq? @id \"sprintf\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "gets_call",
description: "gets() usage",
query: "(call_expression function: (identifier) @id (#eq? @id \"gets\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "system_call",
description: "system() shell execution",
query: "(call_expression function: (identifier) @id (#eq? @id \"system\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "reinterpret_cast",
description: "reinterpret_cast usage",
query: "(reinterpret_cast_expression) @vuln",
severity: Severity::Medium,
},
];

View file

@ -1,34 +1,34 @@
use crate::patterns::{Pattern, Severity};
pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "exec_command",
description: "os/exec Command construction",
query: "(call_expression function: (selector_expression field: (field_identifier) @f (#eq? @f \"Command\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "http_insecure_tls",
description: "&http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}",
query: "(composite_literal type: (selector_expression field: (field_identifier) @t (#eq? @t \"Transport\")) body: (literal_value (keyed_element key: (identifier) @k (#eq? @k \"TLSClientConfig\") value: (composite_literal body: (literal_value (keyed_element key: (identifier) @ik (#eq? @ik \"InsecureSkipVerify\") value: (true)))))) @vuln",
severity: Severity::High,
},
Pattern {
id: "unsafe_pointer",
description: "Use of unsafe.Pointer",
query: "(qualified_type type: (selector_expression field: (field_identifier) @f (#eq? @f \"Pointer\"))) @vuln",
severity: Severity::High,
},
Pattern {
id: "md5_sha1",
description: "crypto/md5 or crypto/sha1 usage",
query: "(call_expression function: (selector_expression object: (identifier) @pkg (#match? @pkg \"md5|sha1\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "hardcoded_secret",
description: "Hard-coded string that looks like an API key/token",
query: "(interpreted_string_literal) @s (#match? @s \"(?i)(api|secret|token|password)[=:]?[ \\t]*[A-Za-z0-9_\\-]{8,}\")",
severity: Severity::Low,
},
Pattern {
id: "exec_command",
description: "os/exec Command construction",
query: "(call_expression function: (selector_expression field: (field_identifier) @f (#eq? @f \"Command\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "http_insecure_tls",
description: "&http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}",
query: "(composite_literal type: (selector_expression field: (field_identifier) @t (#eq? @t \"Transport\")) body: (literal_value (keyed_element key: (identifier) @k (#eq? @k \"TLSClientConfig\") value: (composite_literal body: (literal_value (keyed_element key: (identifier) @ik (#eq? @ik \"InsecureSkipVerify\") value: (true)))))) @vuln",
severity: Severity::High,
},
Pattern {
id: "unsafe_pointer",
description: "Use of unsafe.Pointer",
query: "(qualified_type type: (selector_expression field: (field_identifier) @f (#eq? @f \"Pointer\"))) @vuln",
severity: Severity::High,
},
Pattern {
id: "md5_sha1",
description: "crypto/md5 or crypto/sha1 usage",
query: "(call_expression function: (selector_expression object: (identifier) @pkg (#match? @pkg \"md5|sha1\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "hardcoded_secret",
description: "Hard-coded string that looks like an API key/token",
query: "(interpreted_string_literal) @s (#match? @s \"(?i)(api|secret|token|password)[=:]?[ \\t]*[A-Za-z0-9_\\-]{8,}\")",
severity: Severity::Low,
},
];

View file

@ -1,40 +1,40 @@
use crate::patterns::{Pattern, Severity};
pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "runtime_exec",
description: "Runtime.getRuntime().exec(...) arbitrary-command execution",
query: "(method_invocation object: (method_invocation name: (identifier) @n (#eq? @n \"getRuntime\")) name: (identifier) @id (#eq? @id \"exec\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "class_for_name",
description: "Dynamic reflection via Class.forName(...)",
query: "(method_invocation object: (identifier) @c (#eq? @c \"Class\") name: (identifier) @id (#eq? @id \"forName\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "object_deserialization",
description: "java.io.ObjectInputStream#readObject() deserialization",
query: "(method_invocation object: (identifier) @o (#eq? @o \"ObjectInputStream\") name: (identifier) @id (#eq? @id \"readObject\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "insecure_random",
description: "java.util.Random used where SecureRandom is expected",
query: "(object_creation_expression type: (identifier) @t (#eq? @t \"Random\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "thread_stop",
description: "Deprecated Thread.stop() invocation",
query: "(method_invocation name: (identifier) @id (#eq? @id \"stop\") object: (identifier) @obj (#eq? @obj \"Thread\")) @vuln",
severity: Severity::Low,
},
Pattern {
id: "sql_concat",
description: "SQL built with string concatenation",
query: "(method_invocation name: (identifier) @id (#match? @id \"execute(Query|Update)?\") arguments: (argument_list (binary_expression) @concat)) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "runtime_exec",
description: "Runtime.getRuntime().exec(...) arbitrary-command execution",
query: "(method_invocation object: (method_invocation name: (identifier) @n (#eq? @n \"getRuntime\")) name: (identifier) @id (#eq? @id \"exec\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "class_for_name",
description: "Dynamic reflection via Class.forName(...)",
query: "(method_invocation object: (identifier) @c (#eq? @c \"Class\") name: (identifier) @id (#eq? @id \"forName\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "object_deserialization",
description: "java.io.ObjectInputStream#readObject() deserialization",
query: "(method_invocation object: (identifier) @o (#eq? @o \"ObjectInputStream\") name: (identifier) @id (#eq? @id \"readObject\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "insecure_random",
description: "java.util.Random used where SecureRandom is expected",
query: "(object_creation_expression type: (identifier) @t (#eq? @t \"Random\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "thread_stop",
description: "Deprecated Thread.stop() invocation",
query: "(method_invocation name: (identifier) @id (#eq? @id \"stop\") object: (identifier) @obj (#eq? @obj \"Thread\")) @vuln",
severity: Severity::Low,
},
Pattern {
id: "sql_concat",
description: "SQL built with string concatenation",
query: "(method_invocation name: (identifier) @id (#match? @id \"execute(Query|Update)?\") arguments: (argument_list (binary_expression) @concat)) @vuln",
severity: Severity::Medium,
},
];

View file

@ -1,94 +1,94 @@
use crate::patterns::{Pattern, Severity};
pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "eval_call",
description: "Use of eval()",
query: "(call_expression function: (identifier) @id (#eq? @id \"eval\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "new_function",
description: "new Function() constructor",
query: "(new_expression constructor: (identifier) @id (#eq? @id \"Function\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "document_write",
description: "document.write() call",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"document\") property: (property_identifier) @prop (#eq? @prop \"write\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "inner_html_assignment",
description: "Assignment to element.innerHTML",
query: "(assignment_expression left: (member_expression property: (property_identifier) @prop (#eq? @prop \"innerHTML\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "settimeout_string",
description: "setTimeout / setInterval with a string argument",
query: "(call_expression function: (identifier) @id (#match? @id \"setTimeout|setInterval\") arguments: (arguments (string) @code . _)) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "json_parse",
description: "JSON.parse on dynamic string",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"JSON\") property: (property_identifier) @prop (#eq? @prop \"parse\"))) @vuln",
severity: Severity::Low,
},
Pattern {
id: "outer_html_assignment",
description: "Assignment to element.outerHTML",
query: "(assignment_expression
Pattern {
id: "eval_call",
description: "Use of eval()",
query: "(call_expression function: (identifier) @id (#eq? @id \"eval\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "new_function",
description: "new Function() constructor",
query: "(new_expression constructor: (identifier) @id (#eq? @id \"Function\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "document_write",
description: "document.write() call",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"document\") property: (property_identifier) @prop (#eq? @prop \"write\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "inner_html_assignment",
description: "Assignment to element.innerHTML",
query: "(assignment_expression left: (member_expression property: (property_identifier) @prop (#eq? @prop \"innerHTML\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "settimeout_string",
description: "setTimeout / setInterval with a string argument",
query: "(call_expression function: (identifier) @id (#match? @id \"setTimeout|setInterval\") arguments: (arguments (string) @code . _)) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "json_parse",
description: "JSON.parse on dynamic string",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"JSON\") property: (property_identifier) @prop (#eq? @prop \"parse\"))) @vuln",
severity: Severity::Low,
},
Pattern {
id: "outer_html_assignment",
description: "Assignment to element.outerHTML",
query: "(assignment_expression
left: (member_expression
property: (property_identifier) @prop
(#eq? @prop \"outerHTML\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "insert_adjacent_html",
description: "insertAdjacentHTML() call",
query: "(call_expression
severity: Severity::Medium,
},
Pattern {
id: "insert_adjacent_html",
description: "insertAdjacentHTML() call",
query: "(call_expression
function: (member_expression
property: (property_identifier) @prop
(#eq? @prop \"insertAdjacentHTML\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "location_href_assignment",
description: "Assignment to window.location / location.href",
query: "(assignment_expression
severity: Severity::Medium,
},
Pattern {
id: "location_href_assignment",
description: "Assignment to window.location / location.href",
query: "(assignment_expression
left: (member_expression
object: (identifier)? @obj
property: (property_identifier) @prop
(#match? @prop \"location|href\"))) @vuln",
severity: Severity::High,
},
Pattern {
id: "cookie_assignment",
description: "Write to document.cookie",
query: "(assignment_expression
severity: Severity::High,
},
Pattern {
id: "cookie_assignment",
description: "Write to document.cookie",
query: "(assignment_expression
left: (member_expression
object: (identifier) @obj
(#eq? @obj \"document\")
property: (property_identifier) @prop
(#eq? @prop \"cookie\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "proto_pollution",
description: "Assignment to __proto__ (prototype pollution)",
query: "(assignment_expression
severity: Severity::Medium,
},
Pattern {
id: "proto_pollution",
description: "Assignment to __proto__ (prototype pollution)",
query: "(assignment_expression
left: (member_expression
property: (property_identifier) @prop
(#eq? @prop \"__proto__\"))) @vuln",
severity: Severity::High,
},
Pattern {
id: "weak_hash_md5",
description: "crypto.createHash(\"md5\")",
query: "(call_expression
severity: Severity::High,
},
Pattern {
id: "weak_hash_md5",
description: "crypto.createHash(\"md5\")",
query: "(call_expression
function: (member_expression
object: (identifier) @obj
(#eq? @obj \"crypto\")
@ -97,26 +97,26 @@ pub const PATTERNS: &[Pattern] = &[
arguments: (arguments
(string) @alg
(#eq? @alg \"md5\"))) @vuln",
severity: Severity::Low,
},
Pattern {
id: "regexp_constructor_string",
description: "new RegExp() with a dynamic string",
query: "(new_expression
severity: Severity::Low,
},
Pattern {
id: "regexp_constructor_string",
description: "new RegExp() with a dynamic string",
query: "(new_expression
constructor: (identifier) @id
(#eq? @id \"RegExp\")
arguments: (arguments (string) @pattern)) @vuln",
severity: Severity::Low,
},
Pattern {
id: "dangerous_extend_builtin",
description: "Extending Object.prototype (may lead to collisions/pollution)",
query: "(assignment_expression
severity: Severity::Low,
},
Pattern {
id: "dangerous_extend_builtin",
description: "Extending Object.prototype (may lead to collisions/pollution)",
query: "(assignment_expression
left: (member_expression
object: (identifier) @obj
(#eq? @obj \"Object\")
property: (property_identifier) @prop
(#eq? @prop \"prototype\"))) @vuln",
severity: Severity::Medium,
},
severity: Severity::Medium,
},
];

View file

@ -1,116 +1,115 @@
pub mod rust;
pub mod typescript;
pub mod javascript;
pub mod cpp;
pub mod c;
mod java;
pub mod cpp;
mod go;
mod java;
pub mod javascript;
mod php;
mod python;
mod ruby;
pub mod rust;
pub mod typescript;
use console::style;
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
use console::style;
use serde::{Deserialize, Serialize};
use once_cell::sync::Lazy;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
pub enum Severity { High, Medium, Low }
pub enum Severity {
High,
Medium,
Low,
}
impl fmt::Display for Severity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match *self {
Severity::High => style("HIGH").red().bold().to_string(),
Severity::Medium => style("MEDIUM").yellow().bold().to_string(),
Severity::Low => style("LOW").cyan().bold().to_string(),
};
f.write_str(&s)
}
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match *self {
Severity::High => style("HIGH").red().bold().to_string(),
Severity::Medium => style("MEDIUM").yellow().bold().to_string(),
Severity::Low => style("LOW").cyan().bold().to_string(),
};
f.write_str(&s)
}
}
impl Severity {
/// Textual value stored in SQLite.
pub fn as_db_str(self) -> &'static str {
match self {
Severity::High => "HIGH",
Severity::Medium => "MEDIUM",
Severity::Low => "LOW",
/// Textual value stored in SQLite.
pub fn as_db_str(self) -> &'static str {
match self {
Severity::High => "HIGH",
Severity::Medium => "MEDIUM",
Severity::Low => "LOW",
}
}
}
}
impl FromStr for Severity { // TODO: FIX
type Err = ();
impl FromStr for Severity {
// TODO: FIX
type Err = ();
fn from_str(input: &str) -> Result<Self, Self::Err> {
match input.to_lowercase().as_str() {
"medium" => Ok(Severity::Medium),
"high" => Ok(Severity::High),
_ => Ok(Severity::Low),
fn from_str(input: &str) -> Result<Self, Self::Err> {
match input.to_lowercase().as_str() {
"medium" => Ok(Severity::Medium),
"high" => Ok(Severity::High),
_ => Ok(Severity::Low),
}
}
}
}
/// One AST pattern with a tree-sitter query and meta-data.
#[derive(Debug, Clone, Serialize)]
pub struct Pattern {
/// Unique identifier (snake-case preferred).
pub id: &'static str,
/// Human-readable explanation.
pub description: &'static str,
/// tree-sitter query string.
pub query: &'static str,
/// Rough severity bucket.
pub severity: Severity,
/// Unique identifier (snake-case preferred).
pub id: &'static str,
/// Human-readable explanation.
pub description: &'static str,
/// tree-sitter query string.
pub query: &'static str,
/// Rough severity bucket.
pub severity: Severity,
}
/// Global, lazily-initialised registry: lang-name → pattern slice
static REGISTRY: Lazy<HashMap<&'static str, &'static [Pattern]>> = Lazy::new(|| {
let mut m = HashMap::new();
let mut m = HashMap::new();
// ---- Rust ----
m.insert("rust", rust::PATTERNS);
// ---- Rust ----
m.insert("rust", rust::PATTERNS);
// ---- TypeScript ----
m.insert("typescript", typescript::PATTERNS);
m.insert("ts", typescript::PATTERNS);
m.insert("tsx", typescript::PATTERNS);
// ---- TypeScript ----
m.insert("typescript", typescript::PATTERNS);
m.insert("ts", typescript::PATTERNS);
m.insert("tsx", typescript::PATTERNS);
// ---- JavaScript ----
m.insert("javascript", javascript::PATTERNS);
m.insert("js", javascript::PATTERNS);
// ---- JavaScript ----
m.insert("javascript", javascript::PATTERNS);
m.insert("js", javascript::PATTERNS);
// ---- C & C++ ----
m.insert("c", c::PATTERNS);
m.insert("cpp", cpp::PATTERNS);
m.insert("c++", cpp::PATTERNS);
// ---- C & C++ ----
m.insert("c", c::PATTERNS);
m.insert("cpp", cpp::PATTERNS);
m.insert("c++", cpp::PATTERNS);
// ---- Other languages in the folder ----
m.insert("java", java::PATTERNS);
m.insert("go", go::PATTERNS);
m.insert("php", php::PATTERNS);
m.insert("python", python::PATTERNS);
m.insert("py", python::PATTERNS);
m.insert("ruby", ruby::PATTERNS);
m.insert("rb", ruby::PATTERNS);
// ---- Other languages in the folder ----
m.insert("java", java::PATTERNS);
m.insert("go", go::PATTERNS);
m.insert("php", php::PATTERNS);
m.insert("python", python::PATTERNS);
m.insert("py", python::PATTERNS);
m.insert("ruby", ruby::PATTERNS);
m.insert("rb", ruby::PATTERNS);
tracing::debug!("AST-pattern registry initialised ({} languages)", m.len());
m
tracing::debug!("AST-pattern registry initialised ({} languages)", m.len());
m
});
/// Return all patterns for the requested language (case-insensitive).
///
/// Unknown languages yield an **empty** `Vec`.
pub fn load(lang: &str) -> Vec<Pattern> {
let key = lang.to_ascii_lowercase();
REGISTRY
.get(key.as_str())
.copied()
.unwrap_or(&[])
.to_vec()
}
let key = lang.to_ascii_lowercase();
REGISTRY.get(key.as_str()).copied().unwrap_or(&[]).to_vec()
}

View file

@ -1,40 +1,40 @@
use crate::patterns::{Pattern, Severity};
pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "eval_call",
description: "eval($code) execution",
query: "(function_call_expression function: (name) @n (#eq? @n \"eval\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "preg_replace_e",
description: "preg_replace with deprecated /e modifier",
query: "(function_call_expression function: (name) @n (#eq? @n \"preg_replace\") arguments: (arguments (string) @pat (#match? @pat \"/.*e.*$/\"))) @vuln",
severity: Severity::High,
},
Pattern {
id: "create_function",
description: "create_function(...) anonymous eval-like",
query: "(function_call_expression function: (name) @n (#eq? @n \"create_function\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "unserialize_call",
description: "unserialize(...) on user input",
query: "(function_call_expression function: (name) @n (#eq? @n \"unserialize\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "mysql_query_concat",
description: "mysql_query with concatenated SQL",
query: "(function_call_expression function: (name) @n (#eq? @n \"mysql_query\") arguments: (arguments (binary_expression) @concat)) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "system_call",
description: "system()/shell_exec()/exec() command execution",
query: "(function_call_expression function: (name) @n (#match? @n \"system|shell_exec|exec|passthru\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "eval_call",
description: "eval($code) execution",
query: "(function_call_expression function: (name) @n (#eq? @n \"eval\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "preg_replace_e",
description: "preg_replace with deprecated /e modifier",
query: "(function_call_expression function: (name) @n (#eq? @n \"preg_replace\") arguments: (arguments (string) @pat (#match? @pat \"/.*e.*$/\"))) @vuln",
severity: Severity::High,
},
Pattern {
id: "create_function",
description: "create_function(...) anonymous eval-like",
query: "(function_call_expression function: (name) @n (#eq? @n \"create_function\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "unserialize_call",
description: "unserialize(...) on user input",
query: "(function_call_expression function: (name) @n (#eq? @n \"unserialize\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "mysql_query_concat",
description: "mysql_query with concatenated SQL",
query: "(function_call_expression function: (name) @n (#eq? @n \"mysql_query\") arguments: (arguments (binary_expression) @concat)) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "system_call",
description: "system()/shell_exec()/exec() command execution",
query: "(function_call_expression function: (name) @n (#match? @n \"system|shell_exec|exec|passthru\")) @vuln",
severity: Severity::Medium,
},
];

View file

@ -1,22 +1,22 @@
use crate::patterns::{Pattern, Severity};
pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "eval_call",
description: "eval() on dynamic input",
query: "(call function: (identifier) @id (#eq? @id \"eval\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "exec_call",
description: "exec(...) execution of dynamic code",
query: "(call function: (identifier) @id (#eq? @id \"exec\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "subprocess_shell_true",
description: "subprocess.* with shell=True",
query: "(call function: (attribute object: (identifier) @pkg (#eq? @pkg \"subprocess\")) arguments: (argument_list . (keyword_argument name: (identifier) @k (#eq? @k \"shell\")) (true) @val)) @vuln",
severity: Severity::Medium,
}
Pattern {
id: "eval_call",
description: "eval() on dynamic input",
query: "(call function: (identifier) @id (#eq? @id \"eval\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "exec_call",
description: "exec(...) execution of dynamic code",
query: "(call function: (identifier) @id (#eq? @id \"exec\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "subprocess_shell_true",
description: "subprocess.* with shell=True",
query: "(call function: (attribute object: (identifier) @pkg (#eq? @pkg \"subprocess\")) arguments: (argument_list . (keyword_argument name: (identifier) @k (#eq? @k \"shell\")) (true) @val)) @vuln",
severity: Severity::Medium,
},
];

View file

@ -1,45 +1,44 @@
use crate::patterns::{Pattern, Severity};
pub const PATTERNS: &[Pattern] = &[
// ---------- Runtime code-execution primitives ----------
Pattern {
id: "eval_call",
description: "Kernel#eval usage",
query: r#"
// ---------- Runtime code-execution primitives ----------
Pattern {
id: "eval_call",
description: "Kernel#eval usage",
query: r#"
(call
(identifier) @id
(#eq? @id "eval")
) @vuln
"#,
severity: Severity::High,
},
Pattern {
id: "instance_eval_call",
description: "Object#instance_eval usage",
query: r#"
severity: Severity::High,
},
Pattern {
id: "instance_eval_call",
description: "Object#instance_eval usage",
query: r#"
(call
(identifier) @id
(#eq? @id "instance_eval")
) @vuln
"#,
severity: Severity::High,
},
Pattern {
id: "class_eval_call",
description: "Module#class_eval / module_eval usage",
query: r#"
severity: Severity::High,
},
Pattern {
id: "class_eval_call",
description: "Module#class_eval / module_eval usage",
query: r#"
(call
(identifier) @id
(#match? @id "^(class_eval|module_eval)$")
) @vuln
"#,
severity: Severity::High,
},
// ---------- Shell execution ----------
Pattern {
id: "system_exec_interp",
description: "system/exec with string interpolation",
query: r#"
severity: Severity::High,
},
// ---------- Shell execution ----------
Pattern {
id: "system_exec_interp",
description: "system/exec with string interpolation",
query: r#"
(call
method: (identifier) @m
(#match? @m "^(system|exec)$")
@ -50,21 +49,20 @@ pub const PATTERNS: &[Pattern] = &[
)
)
"#,
severity: Severity::High,
},
Pattern {
id: "backtick_command",
description: "Back-tick shell execution",
// `uname -a`
query: r#"(shell_command) @vuln"#,
severity: Severity::High,
},
// ---------- Dangerous deserialisation ----------
Pattern {
id: "yaml_load",
description: "YAML.load / Psych.load (arbitrary object deserialisation)",
query: r#"
severity: Severity::High,
},
Pattern {
id: "backtick_command",
description: "Back-tick shell execution",
// `uname -a`
query: r#"(shell_command) @vuln"#,
severity: Severity::High,
},
// ---------- Dangerous deserialisation ----------
Pattern {
id: "yaml_load",
description: "YAML.load / Psych.load (arbitrary object deserialisation)",
query: r#"
(call
receiver: (constant) @recv
(#match? @recv "^(YAML|Psych)$")
@ -72,12 +70,12 @@ pub const PATTERNS: &[Pattern] = &[
(#eq? @m "load")
) @vuln
"#,
severity: Severity::High,
},
Pattern {
id: "marshal_load",
description: "Marshal.load usage",
query: r#"
severity: Severity::High,
},
Pattern {
id: "marshal_load",
description: "Marshal.load usage",
query: r#"
(call
receiver: (constant) @recv
(#eq? @recv "Marshal")
@ -85,14 +83,13 @@ pub const PATTERNS: &[Pattern] = &[
(#eq? @m "load")
) @vuln
"#,
severity: Severity::High,
},
// ---------- Reflection / meta-programming ----------
Pattern {
id: "send_dynamic",
description: "send() with dynamic first argument (not a literal symbol)",
query: r#"
severity: Severity::High,
},
// ---------- Reflection / meta-programming ----------
Pattern {
id: "send_dynamic",
description: "send() with dynamic first argument (not a literal symbol)",
query: r#"
(call
method: (identifier) @m
(#eq? @m "send")
@ -104,25 +101,24 @@ pub const PATTERNS: &[Pattern] = &[
)
)
"#,
severity: Severity::Medium,
},
Pattern {
id: "constantize_call",
description: "ActiveSupport constantize / safe_constantize on tainted data",
query: r#"
severity: Severity::Medium,
},
Pattern {
id: "constantize_call",
description: "ActiveSupport constantize / safe_constantize on tainted data",
query: r#"
(call
method: (identifier) @m
(#match? @m "^(constantize|safe_constantize)$")
) @vuln
"#,
severity: Severity::Medium,
},
// ---------- Insecure resource access ----------
Pattern {
id: "open_uri_http",
description: "Kernel#open with HTTP(S) URL (open-uri auto-follow)",
query: r#"
severity: Severity::Medium,
},
// ---------- Insecure resource access ----------
Pattern {
id: "open_uri_http",
description: "Kernel#open with HTTP(S) URL (open-uri auto-follow)",
query: r#"
(call
method: (identifier) @m
(#eq? @m "open")
@ -132,6 +128,6 @@ pub const PATTERNS: &[Pattern] = &[
)
) @vuln
"#,
severity: Severity::Medium,
},
severity: Severity::Medium,
},
];

View file

@ -1,118 +1,118 @@
use crate::patterns::{Pattern, Severity};
pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "unsafe_block",
description: "Use of an `unsafe` block",
query: "(unsafe_block) @vuln",
severity: Severity::High,
},
Pattern {
id: "unsafe_fn",
description: "`unsafe fn` declaration",
query: "(function_item
Pattern {
id: "unsafe_block",
description: "Use of an `unsafe` block",
query: "(unsafe_block) @vuln",
severity: Severity::High,
},
Pattern {
id: "unsafe_fn",
description: "`unsafe fn` declaration",
query: "(function_item
(function_modifiers) @mods
(#match? @mods \"^unsafe\\b\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "transmute_call",
description: "`std::mem::transmute` call",
query: "(call_expression
severity: Severity::High,
},
Pattern {
id: "transmute_call",
description: "`std::mem::transmute` call",
query: "(call_expression
function: (scoped_identifier
path: (identifier) @p (#eq? @p \"mem\")
name: (identifier) @f (#eq? @f \"transmute\")))
@vuln",
severity: Severity::High,
},
Pattern {
id: "copy_nonoverlapping",
description: "Raw pointer `copy_nonoverlapping`",
query: "(call_expression
severity: Severity::High,
},
Pattern {
id: "copy_nonoverlapping",
description: "Raw pointer `copy_nonoverlapping`",
query: "(call_expression
function: (scoped_identifier
path: (identifier) @p (#eq? @p \"ptr\")
name: (identifier) @f (#eq? @f \"copy_nonoverlapping\")))
@vuln",
severity: Severity::High,
},
Pattern {
id: "get_unchecked",
description: "`get_unchecked` / `get_unchecked_mut` slice access",
query: "(call_expression
severity: Severity::High,
},
Pattern {
id: "get_unchecked",
description: "`get_unchecked` / `get_unchecked_mut` slice access",
query: "(call_expression
function: (field_expression
field: (field_identifier) @m
(#match? @m \"get_unchecked(_mut)?\"))) @vuln",
severity: Severity::High,
},
Pattern {
id: "unwrap_call",
description: "`.unwrap()` call (may panic)",
query: "(call_expression
severity: Severity::High,
},
Pattern {
id: "unwrap_call",
description: "`.unwrap()` call (may panic)",
query: "(call_expression
function: (field_expression
field: (field_identifier) @name
(#eq? @name \"unwrap\"))) ; exact match
@vuln",
severity: Severity::Medium,
},
Pattern {
id: "expect_call",
description: "`.expect()` call (may panic)",
query: "(call_expression
severity: Severity::Medium,
},
Pattern {
id: "expect_call",
description: "`.expect()` call (may panic)",
query: "(call_expression
function: (field_expression
field: (field_identifier) @name
(#eq? @name \"expect\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "panic_macro",
description: "`panic!` macro invocation",
query: "(macro_invocation (identifier) @id (#eq? @id \"panic\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "todo_or_unimplemented",
description: "`todo!()` / `unimplemented!()` placeholder",
query: "(macro_invocation
severity: Severity::Medium,
},
Pattern {
id: "panic_macro",
description: "`panic!` macro invocation",
query: "(macro_invocation (identifier) @id (#eq? @id \"panic\")) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "todo_or_unimplemented",
description: "`todo!()` / `unimplemented!()` placeholder",
query: "(macro_invocation
(identifier) @id
(#match? @id \"todo|unimplemented\")) @vuln",
severity: Severity::Low,
},
Pattern {
id: "narrow_cast_with_as",
description: "`as` cast to an 8-/16-bit integer (possible truncation)",
query: "(type_cast_expression
severity: Severity::Low,
},
Pattern {
id: "narrow_cast_with_as",
description: "`as` cast to an 8-/16-bit integer (possible truncation)",
query: "(type_cast_expression
type: (primitive_type) @to
(#match? @to \"^u?i(8|16)$\")) @vuln",
severity: Severity::Low,
},
Pattern {
id: "mem_zeroed",
description: "`std::mem::zeroed()`",
query: "(call_expression function:(scoped_identifier path:(identifier)@p (#eq? @p \"mem\") name:(identifier)@n (#eq? @n \"zeroed\")))@vuln",
severity: Severity::High
},
Pattern {
id: "mem_forget",
description: "`std::mem::forget()`",
query: "(call_expression function:(scoped_identifier path:(identifier)@p (#eq? @p \"mem\") name:(identifier)@n (#eq? @n \"forget\")))@vuln",
severity: Severity::Medium
},
Pattern {
id: "ptr_read",
description: "`ptr::read_*` raw-ptr read",
query: "(call_expression function:(scoped_identifier path:(identifier)@p (#eq? @p \"ptr\") name:(identifier)@n (#match? @n \"read(_volatile)?\")))@vuln",
severity: Severity::High
},
Pattern {
id: "arc_unwrap",
description: "`Arc::unwrap_or_else_unchecked`",
query: "(call_expression function:(scoped_identifier name:(identifier)@n (#eq? @n \"unwrap_or_else_unchecked\")))@vuln",
severity: Severity::High
},
Pattern {
id: "dbg_macro",
description: "`dbg!()` left in code",
query: "(macro_invocation (identifier)@id (#eq? @id \"dbg\"))@vuln",
severity: Severity::Low
},
severity: Severity::Low,
},
Pattern {
id: "mem_zeroed",
description: "`std::mem::zeroed()`",
query: "(call_expression function:(scoped_identifier path:(identifier)@p (#eq? @p \"mem\") name:(identifier)@n (#eq? @n \"zeroed\")))@vuln",
severity: Severity::High,
},
Pattern {
id: "mem_forget",
description: "`std::mem::forget()`",
query: "(call_expression function:(scoped_identifier path:(identifier)@p (#eq? @p \"mem\") name:(identifier)@n (#eq? @n \"forget\")))@vuln",
severity: Severity::Medium,
},
Pattern {
id: "ptr_read",
description: "`ptr::read_*` raw-ptr read",
query: "(call_expression function:(scoped_identifier path:(identifier)@p (#eq? @p \"ptr\") name:(identifier)@n (#match? @n \"read(_volatile)?\")))@vuln",
severity: Severity::High,
},
Pattern {
id: "arc_unwrap",
description: "`Arc::unwrap_or_else_unchecked`",
query: "(call_expression function:(scoped_identifier name:(identifier)@n (#eq? @n \"unwrap_or_else_unchecked\")))@vuln",
severity: Severity::High,
},
Pattern {
id: "dbg_macro",
description: "`dbg!()` left in code",
query: "(macro_invocation (identifier)@id (#eq? @id \"dbg\"))@vuln",
severity: Severity::Low,
},
];

View file

@ -1,106 +1,106 @@
use crate::patterns::{Pattern, Severity};
pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "eval_call",
description: "Use of eval()",
query: "(call_expression function: (identifier) @id (#eq? @id \"eval\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "new_function",
description: "new Function() constructor",
query: "(new_expression constructor: (identifier) @id (#eq? @id \"Function\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "document_write",
description: "document.write() call",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"document\") property: (property_identifier) @prop (#eq? @prop \"write\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "inner_html_assignment",
description: "Assignment to element.innerHTML",
query: "(assignment_expression left: (member_expression property: (property_identifier) @prop (#eq? @prop \"innerHTML\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "settimeout_string",
description: "setTimeout / setInterval with a string argument",
query: "(call_expression function: (identifier) @id (#match? @id \"setTimeout|setInterval\") arguments: (arguments (string) @code . _)) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "any_type",
description: "Type annotation of `any`",
query: "(type_annotation (predefined_type) @t (#eq? @t \"any\")) @vuln",
severity: Severity::Low,
},
Pattern {
id: "json_parse",
description: "JSON.parse on dynamic string",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"JSON\") property: (property_identifier) @prop (#eq? @prop \"parse\"))) @vuln",
severity: Severity::Low,
},
Pattern {
id: "as_any_assertion",
description: "Type assertion to `any` using `as any`",
query: "(as_expression type: (predefined_type) @t (#eq? @t \"any\")) @vuln",
severity: Severity::Low,
},
Pattern {
id: "type_assertion_any",
description: "Type assertion to `any` using `<any>` syntax",
query: "(type_assertion type: (predefined_type) @t (#eq? @t \"any\")) @vuln",
severity: Severity::Low,
},
Pattern {
id: "outer_html_assignment",
description: "Assignment to element.outerHTML",
query: "(assignment_expression left: (member_expression property: (property_identifier) @prop (#eq? @prop \"outerHTML\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "insert_adjacent_html",
description: "insertAdjacentHTML() call",
query: "(call_expression function: (member_expression property: (property_identifier) @prop (#eq? @prop \"insertAdjacentHTML\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "document_cookie_write",
description: "Write to document.cookie",
query: "(assignment_expression left: (member_expression object: (identifier) @obj (#eq? @obj \"document\") property: (property_identifier) @prop (#eq? @prop \"cookie\"))) @vuln",
severity: Severity::Low,
},
Pattern {
id: "onclick_setattribute",
description: "Element.setAttribute('onclick', …)",
query: "(call_expression function: (member_expression property: (property_identifier) @prop (#eq? @prop \"setAttribute\")) arguments: (arguments (string) @name (#eq? @name \"\\\"onclick\\\"\") . (string) @handler)) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "math_random_call",
description: "Use of Math.random() for security-sensitive randomness",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"Math\") property: (property_identifier) @prop (#eq? @prop \"random\"))) @vuln",
severity: Severity::Low,
},
Pattern {
id: "crypto_createhash_md5",
description: "Insecure hash algorithm: crypto.createHash('md5')",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"crypto\") property: (property_identifier) @prop (#eq? @prop \"createHash\")) arguments: (arguments (string) @alg (#match? @alg \"(?i)\\\"md5\\\"\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "fetch_http_url",
description: "fetch() over plain HTTP",
query: "(call_expression function: (identifier) @id (#eq? @id \"fetch\") arguments: (arguments (string) @url (#match? @url \"^\\\"http://\"))) @vuln",
severity: Severity::Low,
},
Pattern {
id: "xhr_eval_response",
description: "eval() of XMLHttpRequest.responseText",
query: "(call_expression function: (identifier) @id (#eq? @id \"eval\") arguments: (arguments (member_expression property: (property_identifier) @prop (#eq? @prop \"responseText\")))) @vuln",
severity: Severity::High,
},
];
Pattern {
id: "eval_call",
description: "Use of eval()",
query: "(call_expression function: (identifier) @id (#eq? @id \"eval\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "new_function",
description: "new Function() constructor",
query: "(new_expression constructor: (identifier) @id (#eq? @id \"Function\")) @vuln",
severity: Severity::High,
},
Pattern {
id: "document_write",
description: "document.write() call",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"document\") property: (property_identifier) @prop (#eq? @prop \"write\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "inner_html_assignment",
description: "Assignment to element.innerHTML",
query: "(assignment_expression left: (member_expression property: (property_identifier) @prop (#eq? @prop \"innerHTML\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "settimeout_string",
description: "setTimeout / setInterval with a string argument",
query: "(call_expression function: (identifier) @id (#match? @id \"setTimeout|setInterval\") arguments: (arguments (string) @code . _)) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "any_type",
description: "Type annotation of `any`",
query: "(type_annotation (predefined_type) @t (#eq? @t \"any\")) @vuln",
severity: Severity::Low,
},
Pattern {
id: "json_parse",
description: "JSON.parse on dynamic string",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"JSON\") property: (property_identifier) @prop (#eq? @prop \"parse\"))) @vuln",
severity: Severity::Low,
},
Pattern {
id: "as_any_assertion",
description: "Type assertion to `any` using `as any`",
query: "(as_expression type: (predefined_type) @t (#eq? @t \"any\")) @vuln",
severity: Severity::Low,
},
Pattern {
id: "type_assertion_any",
description: "Type assertion to `any` using `<any>` syntax",
query: "(type_assertion type: (predefined_type) @t (#eq? @t \"any\")) @vuln",
severity: Severity::Low,
},
Pattern {
id: "outer_html_assignment",
description: "Assignment to element.outerHTML",
query: "(assignment_expression left: (member_expression property: (property_identifier) @prop (#eq? @prop \"outerHTML\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "insert_adjacent_html",
description: "insertAdjacentHTML() call",
query: "(call_expression function: (member_expression property: (property_identifier) @prop (#eq? @prop \"insertAdjacentHTML\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "document_cookie_write",
description: "Write to document.cookie",
query: "(assignment_expression left: (member_expression object: (identifier) @obj (#eq? @obj \"document\") property: (property_identifier) @prop (#eq? @prop \"cookie\"))) @vuln",
severity: Severity::Low,
},
Pattern {
id: "onclick_setattribute",
description: "Element.setAttribute('onclick', …)",
query: "(call_expression function: (member_expression property: (property_identifier) @prop (#eq? @prop \"setAttribute\")) arguments: (arguments (string) @name (#eq? @name \"\\\"onclick\\\"\") . (string) @handler)) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "math_random_call",
description: "Use of Math.random() for security-sensitive randomness",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"Math\") property: (property_identifier) @prop (#eq? @prop \"random\"))) @vuln",
severity: Severity::Low,
},
Pattern {
id: "crypto_createhash_md5",
description: "Insecure hash algorithm: crypto.createHash('md5')",
query: "(call_expression function: (member_expression object: (identifier) @obj (#eq? @obj \"crypto\") property: (property_identifier) @prop (#eq? @prop \"createHash\")) arguments: (arguments (string) @alg (#match? @alg \"(?i)\\\"md5\\\"\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
id: "fetch_http_url",
description: "fetch() over plain HTTP",
query: "(call_expression function: (identifier) @id (#eq? @id \"fetch\") arguments: (arguments (string) @url (#match? @url \"^\\\"http://\"))) @vuln",
severity: Severity::Low,
},
Pattern {
id: "xhr_eval_response",
description: "eval() of XMLHttpRequest.responseText",
query: "(call_expression function: (identifier) @id (#eq? @id \"eval\") arguments: (arguments (member_expression property: (property_identifier) @prop (#eq? @prop \"responseText\")))) @vuln",
severity: Severity::High,
},
];