mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-12 19:55:14 +02:00
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:
parent
b3870997d7
commit
14a549ac39
26 changed files with 1314 additions and 1221 deletions
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue