Feat/configurable sanitizers and js precision (#32)

* chore: Exclude CLAUDE.md from Cargo.toml

* feat: Add configurable analysis rules and CLI commands for custom sanitizers and terminators

* feat: Enhance resource management and analysis efficiency

- Implemented parallel summary merging in `scan_filesystem` using rayon for improved performance.
- Introduced `GlobalSummaries::merge()` for efficient merging of summaries.
- Optimized file reading and hashing to eliminate redundant I/O operations.
- Added `should_scan_with_hash()` and `upsert_file_with_hash()` methods to streamline file processing.
- Enhanced taint analysis with in-place mutations to reduce memory allocations.
- Updated resource acquisition patterns to exclude false positives for `freopen` and wrapper functions.

* feat: Implement severity downgrade for findings in non-production paths and add source kind inference

* feat: Update versioning information in SECURITY.md for new stable line

* feat: Update categories in Cargo.toml to include parser-implementations and text-processing

* feat: Update dependencies in Cargo.lock for improved compatibility and performance

* feat: Update dependencies in Cargo.lock and Cargo.toml for improved compatibility
This commit is contained in:
Eli Peter 2026-02-25 04:02:11 -05:00 committed by GitHub
parent f96a89e7c1
commit 19b578c5c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 3775 additions and 432 deletions

View file

@ -5,10 +5,10 @@ use crate::patterns::Severity;
use crate::utils::Config;
use crate::utils::project::get_project_info;
use crate::walk::spawn_file_walker;
use blake3;
use bytesize::ByteSize;
use chrono::{DateTime, Local};
use console::style;
use indicatif::{ProgressBar, ProgressStyle};
use rayon::prelude::*;
use std::fs;
use std::path::PathBuf;
@ -25,7 +25,13 @@ pub fn handle(
let (project_name, db_path) = get_project_info(&build_path, database_dir)?;
if force || !db_path.exists() {
build_index(&project_name, &build_path, &db_path, config)?;
build_index(
&project_name,
&build_path,
&db_path,
config,
!config.output.quiet,
)?;
println!(
"✔ {} {}",
style("Index built:").green(),
@ -84,6 +90,7 @@ pub fn build_index(
project_path: &std::path::Path,
db_path: &std::path::Path,
config: &Config,
show_progress: bool,
) -> NyxResult<()> {
tracing::debug!("Building index for: {}", project_name);
fs::File::create(db_path)?;
@ -97,10 +104,27 @@ pub fn build_index(
tracing::debug!("Cleaned index for: {}", project_name);
let (rx, handle) = spawn_file_walker(project_path, config);
// Drain the channel BEFORE joining — the bounded channel will deadlock
// if we join first and the walker blocks on send.
let paths: Vec<PathBuf> = rx.into_iter().flatten().collect();
if let Err(err) = handle.join() {
tracing::error!("walker thread panicked: {:#?}", err);
}
let paths: Vec<PathBuf> = rx.into_iter().flatten().collect();
let pb = if show_progress {
let pb = ProgressBar::new(paths.len() as u64);
pb.set_style(
ProgressStyle::with_template(
"{spinner:.green} {msg} [{bar:30.cyan/blue}] {pos}/{len} ({eta})",
)
.unwrap()
.progress_chars("##-"),
);
pb.set_message("Indexing files");
pb
} else {
ProgressBar::hidden()
};
paths
.into_par_iter()
@ -108,18 +132,15 @@ pub fn build_index(
let mut idx = Indexer::from_pool(project_name, &pool)?;
// Read once, hash once — pass bytes to both rule execution and
// summary extraction.
// summary extraction. Use pre-computed hash for upsert to avoid
// a redundant file read inside upsert_file.
let bytes = std::fs::read(&path)?;
let hash = {
let mut hasher = blake3::Hasher::new();
hasher.update(&bytes);
hasher.finalize().as_bytes().to_vec()
};
let hash = Indexer::digest_bytes(&bytes);
// Run AST-only rules (no taint yet — summaries come later in scan)
let issues =
crate::commands::scan::run_rules_on_bytes(&bytes, &path, config, None, None)?;
let file_id = idx.upsert_file(&path)?;
let file_id = idx.upsert_file_with_hash(&path, &hash)?;
let rows: Vec<IssueRow> = issues
.iter()
@ -144,8 +165,10 @@ pub fn build_index(
idx.replace_summaries_for_file(&path, &hash, &sums)?;
}
pb.inc(1);
Ok(())
})?;
pb.finish_and_clear();
{
let idx = Indexer::from_pool(project_name, &pool)?;
@ -170,7 +193,7 @@ fn build_index_creates_db_and_registers_files() {
let db_path = td.path().join("proj.sqlite");
build_index("proj", &project_dir, &db_path, &cfg).expect("index build should succeed");
build_index("proj", &project_dir, &db_path, &cfg, false).expect("index build should succeed");
// ── Assert ────────────────────────────────────────────────────────────────
assert!(db_path.is_file(), "SQLite file must exist");