Detector Development Guide
Overview
Section titled “Overview”Detectors scan project files for patterns related to CRA requirements and produce findings. Each detector handles one or more requirement ID prefixes (e.g., CRYPTO-*, NET-*).
The Detector Trait
Section titled “The Detector Trait”pub trait Detector: Send + Sync { /// Human-readable name (e.g., "crypto", "network") fn name(&self) -> &str;
/// Requirement ID prefixes this detector handles (e.g., ["CRYPTO-"]) fn handles_prefixes(&self) -> &[&str];
/// Run detection against the project fn detect( &self, catalog: &CatalogIndex, context: &ProjectContext, files: &ProjectFiles, ) -> Vec<Finding>;}Creating a New Detector
Section titled “Creating a New Detector”1. Create the file
Section titled “1. Create the file”src/assessment/detectors/my_detector.rs2. Implement the trait
Section titled “2. Implement the trait”use regex::Regex;use crate::assessment::catalog::CatalogIndex;use crate::assessment::detect::*;
pub struct MyDetector;
impl Detector for MyDetector { fn name(&self) -> &str { "my_detector" } fn handles_prefixes(&self) -> &[&str] { &["MYPREFIX-"] }
fn detect( &self, _catalog: &CatalogIndex, _context: &ProjectContext, files: &ProjectFiles, ) -> Vec<Finding> { let mut findings = Vec::new();
for (relative, content) in super::walk_source_files(files.root()) { // Your detection logic here let pattern = Regex::new(r"dangerous_function\(").unwrap(); for (line_num, line) in content.lines().enumerate() { if pattern.is_match(line) { findings.push(Finding { requirement_id: "MYPREFIX-01-R1".to_string(), risk_id: "MYPREFIX-01".to_string(), status: FindingStatus::Fail, confidence: 0.80, detector: "my_detector".to_string(), message: "Dangerous function usage detected".to_string(), source_locations: vec![SourceLocation { file: relative.clone(), line: line_num + 1, snippet: line.trim().to_string(), }], }); } } }
findings }}3. Register in detectors/mod.rs
Section titled “3. Register in detectors/mod.rs”pub mod my_detector;
pub fn register_all(registry: &mut DetectorRegistry) { // ... existing detectors ... registry.register(Box::new(my_detector::MyDetector));}4. Add tests
Section titled “4. Add tests”#[cfg(test)]mod tests { use super::*;
#[test] fn detects_dangerous_function() { // Test with inline code string }
#[test] fn ignores_safe_code() { // Verify no false positives }}Shared Helpers
Section titled “Shared Helpers”The detectors/mod.rs provides helpers:
| Helper | Purpose |
|---|---|
walk_source_files(root) | Yields (relative_path, content) for all source + config files |
is_source_file(path) | Check if file extension is a recognized source language |
is_config_file(path) | Check if file is a config file (yml, toml, json, env, Dockerfile) |
should_skip_dir(name) | Check if directory should be skipped (node_modules, .git, etc.) |
Finding Status Guide
Section titled “Finding Status Guide”| Status | When to Use |
|---|---|
FindingStatus::Pass | Scanner can confirm the requirement is satisfied |
FindingStatus::Fail | Scanner detects a clear violation |
FindingStatus::NeedsReview | Pattern detected but human/LLM judgment needed |
FindingStatus::NotApplicable | Requirement doesn’t apply (e.g., no AI usage -> AI-* is N/A) |
Confidence Scores
Section titled “Confidence Scores”| Range | Meaning |
|---|---|
| 0.90 - 1.00 | High confidence — clear pattern match (e.g., security.txt exists) |
| 0.70 - 0.89 | Good confidence — strong pattern match (e.g., MD5 usage in security context) |
| 0.50 - 0.69 | Moderate — pattern match but context uncertain |
| 0.30 - 0.49 | Low — heuristic match, likely needs review |
| < 0.30 | Very low — speculative |
Pattern Matching Tips
Section titled “Pattern Matching Tips”-
Use
r#""#for regex with quotes: Rust’s regex crate doesn’t support look-ahead ((?!...)). Use post-match filtering instead. -
Skip comments: Always filter out comment lines before matching.
-
Skip test files: Don’t flag patterns in test code unless relevant.
-
Redact secrets: If a finding involves credentials, use
[REDACTED]in the snippet. -
Use
NeedsReviewgenerously: When in doubt, flag for review rather than auto-passing or auto-failing.
Existing Detectors
Section titled “Existing Detectors”| Detector | File | Prefixes | Key Patterns |
|---|---|---|---|
| crypto | crypto.rs | CRYPTO- | Weak algorithms, hardcoded keys, insecure PRNG |
| network | network.rs | NET- | Hardcoded credentials, HTTP URLs, API keys, debug endpoints |
| auth | auth.rs | AUTH- | Password logging, JWT alg:none, token logging |
| input | input.rs | INPUT-, UPLOAD- | Command injection, SQL construction, path traversal |
| storage | storage.rs | STOR- | Public buckets, hardcoded encryption keys |
| update | update.rs | UPD- | HTTP update channels, hash/signature verification |
| logging | logging.rs | LOG- | Passwords/tokens in logs, structured logging |
| supply_chain | supply_chain.rs | SBOM-, OSS-, etc. | Lockfiles, vendored dirs, CI vuln scanning |
| config | config.rs | CONFIG- | Default passwords, debug mode, verbose errors |
| vuln_handling | vuln_handling.rs | VH- | security.txt, SECURITY.md, disclosure policy |
| ai | ai.rs | AI- | ML frameworks, prompt injection, model loading |