Skip to content

Detector Development Guide

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-*).

src/assessment/detect.rs
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>;
}
src/assessment/detectors/my_detector.rs
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
}
}
pub mod my_detector;
pub fn register_all(registry: &mut DetectorRegistry) {
// ... existing detectors ...
registry.register(Box::new(my_detector::MyDetector));
}
#[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
}
}

The detectors/mod.rs provides helpers:

HelperPurpose
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.)
StatusWhen to Use
FindingStatus::PassScanner can confirm the requirement is satisfied
FindingStatus::FailScanner detects a clear violation
FindingStatus::NeedsReviewPattern detected but human/LLM judgment needed
FindingStatus::NotApplicableRequirement doesn’t apply (e.g., no AI usage -> AI-* is N/A)
RangeMeaning
0.90 - 1.00High confidence — clear pattern match (e.g., security.txt exists)
0.70 - 0.89Good confidence — strong pattern match (e.g., MD5 usage in security context)
0.50 - 0.69Moderate — pattern match but context uncertain
0.30 - 0.49Low — heuristic match, likely needs review
< 0.30Very low — speculative
  1. Use r#""# for regex with quotes: Rust’s regex crate doesn’t support look-ahead ((?!...)). Use post-match filtering instead.

  2. Skip comments: Always filter out comment lines before matching.

  3. Skip test files: Don’t flag patterns in test code unless relevant.

  4. Redact secrets: If a finding involves credentials, use [REDACTED] in the snippet.

  5. Use NeedsReview generously: When in doubt, flag for review rather than auto-passing or auto-failing.

DetectorFilePrefixesKey Patterns
cryptocrypto.rsCRYPTO-Weak algorithms, hardcoded keys, insecure PRNG
networknetwork.rsNET-Hardcoded credentials, HTTP URLs, API keys, debug endpoints
authauth.rsAUTH-Password logging, JWT alg:none, token logging
inputinput.rsINPUT-, UPLOAD-Command injection, SQL construction, path traversal
storagestorage.rsSTOR-Public buckets, hardcoded encryption keys
updateupdate.rsUPD-HTTP update channels, hash/signature verification
logginglogging.rsLOG-Passwords/tokens in logs, structured logging
supply_chainsupply_chain.rsSBOM-, OSS-, etc.Lockfiles, vendored dirs, CI vuln scanning
configconfig.rsCONFIG-Default passwords, debug mode, verbose errors
vuln_handlingvuln_handling.rsVH-security.txt, SECURITY.md, disclosure policy
aiai.rsAI-ML frameworks, prompt injection, model loading