Update dependencies and enhance pattern handling

- Added `tracing-appender` and `log` dependencies to improve error logging.
- Enhanced `walk.rs` to add error handling with warning logs for ignore patterns.
- Expanded Rust and JavaScript patterns with additional security vulnerability checks.
- Simplified and updated pattern queries for improved accuracy and consistency.
- Removed unused print statement in `index.rs`.
This commit is contained in:
elipeter 2025-06-17 02:22:14 +02:00
parent 22369cc404
commit 7bfce3ad7f
6 changed files with 203 additions and 32 deletions

38
Cargo.lock generated
View file

@ -12,6 +12,7 @@ dependencies = [
"directories",
"filetime",
"ignore",
"log",
"num_cpus",
"once_cell",
"rusqlite",
@ -19,6 +20,7 @@ dependencies = [
"tempfile",
"toml",
"tracing",
"tracing-appender",
"tracing-subscriber",
"tree-sitter",
"tree-sitter-c",
@ -588,7 +590,7 @@ checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b"
dependencies = [
"getrandom 0.2.16",
"libredox",
"thiserror",
"thiserror 2.0.12",
]
[[package]]
@ -776,13 +778,33 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
"thiserror-impl 1.0.69",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
"thiserror-impl 2.0.12",
]
[[package]]
name = "thiserror-impl"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
@ -888,6 +910,18 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "tracing-appender"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf"
dependencies = [
"crossbeam-channel",
"thiserror 1.0.69",
"time",
"tracing-subscriber",
]
[[package]]
name = "tracing-attributes"
version = "0.1.29"

View file

@ -12,6 +12,7 @@ clap = { version = "4.5.40", features = ["derive"] }
serde = { version = "1.0.219", features = ["derive"] }
toml = "0.8.23"
tracing-subscriber = { version = "0.3.19", features = ["env-filter", "json", "ansi","time"] }
tracing-appender = "0.2.3"
tracing = "0.1.41"
num_cpus = "1.17.0"
@ -32,3 +33,4 @@ crossbeam-channel = "0.5.15"
blake3 = "1.8.2"
filetime = "0.2.25"
once_cell = "1.21.3"
log = "0.4.27"

View file

@ -43,6 +43,5 @@ pub fn build_index(
) -> Result<(), Box<dyn std::error::Error>> {
// TODO: Implement actual index building
fs::File::create(db_path)?;
println!("Index building logic goes here...");
Ok(())
}

View file

@ -37,4 +37,86 @@ pub const PATTERNS: &[Pattern] = &[
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
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
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
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
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
function: (member_expression
object: (identifier) @obj
(#eq? @obj \"crypto\")
property: (property_identifier) @prop
(#eq? @prop \"createHash\"))
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
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
left: (member_expression
object: (identifier) @obj
(#eq? @obj \"Object\")
property: (property_identifier) @prop
(#eq? @prop \"prototype\"))) @vuln",
severity: Severity::Medium,
},
];

View file

@ -1,9 +1,5 @@
use crate::patterns::{Pattern, Severity};
/// The full catalogue.
///
/// *Feel free to prune, extend, or tweak severities to suit your own threat
/// model.*
pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "unsafe_block",
@ -14,19 +10,57 @@ pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "unsafe_fn",
description: "`unsafe fn` declaration",
query: "(function_item (modifier) @kw (#eq? @kw \"unsafe\")) @vuln",
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
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
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
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 function: (field_expression field: (field_identifier) @name (#eq? @name \"unwrap\"))) @vuln",
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 function: (field_expression field: (field_identifier) @name (#eq? @name \"expect\"))) @vuln",
query: "(call_expression
function: (field_expression
field: (field_identifier) @name
(#eq? @name \"expect\"))) @vuln",
severity: Severity::Medium,
},
Pattern {
@ -38,31 +72,47 @@ pub const PATTERNS: &[Pattern] = &[
Pattern {
id: "todo_or_unimplemented",
description: "`todo!()` / `unimplemented!()` placeholder",
query: "(macro_invocation (identifier) @id (#match? @id \"todo|unimplemented\")) @vuln",
query: "(macro_invocation
(identifier) @id
(#match? @id \"todo|unimplemented\")) @vuln",
severity: Severity::Low,
},
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: "get_unchecked",
description: "`get_unchecked` or `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: "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: "narrow_cast_with_as",
description: "`as` cast to an 8-/16-bit integer (possible truncation)",
query: "(as_expression left: (_) right: (primitive_type) @to (#match? @to \"u8|i8|u16|i16\")) @vuln",
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
},
];

View file

@ -43,11 +43,15 @@ pub fn spawn_senders(
let mut ob = OverrideBuilder::new(root);
for ext in &cfg.scanner.excluded_extensions {
ob.add(&format!("!*.{ext}")).unwrap();
if let Err(e) = ob.add(&format!("!*.{ext}")) {
tracing::warn!("could not add ignore pattern: {e}");
}
}
for dir in &cfg.scanner.excluded_directories {
ob.add(&format!("!**/{dir}/**")).unwrap();
if let Err(e) = ob.add(&format!("!**/{dir}/**")) {
tracing::warn!("could not add ignore pattern: {e}");
}
}
let overrides = ob.build().unwrap();