Rule authoring (inspequte)
Workflow
- •Define rule metadata: unique
id, clearname, and shortdescription. - •Write rule messages for end users so they are intuitive and actionable: explain what is wrong and what to change to fix it.
- •Add
#[derive(Default)]to the rule struct (required for automatic registration). - •Add
crate::register_rule!(RuleName);after the struct declaration to enable automatic discovery. - •Implement
Rule::runusingAnalysisContextand helpers fromcrate::rules(ex:result_message,method_location_with_line,class_location). Always guard rule scans withif !context.is_analysis_target_class(class) { continue; }so classpath-only classes are skipped. - •Add harness tests in the same rule file (
#[cfg(test)]): compile Java sources withJvmTestHarness, analyze, then assert onrule_idand message text. - •Declare the new rule module in
src/rules/mod.rs(ex:pub(crate) mod my_new_rule;). - •Update SARIF snapshot tests if rule list changes (see
tests/snapshots/andINSPEQUTE_UPDATE_SNAPSHOTS=1 cargo test sarif_callgraph_snapshot). - •Keep output deterministic (results are sorted by
rule_id/message; avoid non-deterministic ordering in rule code).
Note: Rules are automatically discovered and registered at compile time using the inventory crate. No manual registration in src/engine.rs is needed.
See references/rule-checklist.md for a compact checklist.
Harness testing
- •Use
JvmTestHarness::new(); it requiresJAVA_HOME(Java 21). - •Prefer local stub sources over downloading jars.
- •Filter SARIF results by
rule_idfor assertions. - •Cover both happy-path and edge cases: include cases that should report, cases that should not report (false positives), and cases that should not miss reports (false negatives).
- •Use generic names in Java harness code (ex:
ClassA,methodOne,varOne) and avoid names from user examples; keep real JDK/library API names where required.
Complete rule example with automatic registration
rust
/// Rule that detects [describe what your rule checks].
#[derive(Default)]
pub(crate) struct MyNewRule;
crate::register_rule!(MyNewRule);
impl Rule for MyNewRule {
fn metadata(&self) -> RuleMetadata {
RuleMetadata {
id: "MY_NEW_RULE",
name: "My new rule",
description: "Brief description of what this rule checks",
}
}
fn run(&self, context: &AnalysisContext) -> Result<Vec<SarifResult>> {
// Implementation here
Ok(vec![])
}
}
Harness test template
rust
let harness = JvmTestHarness::new().expect("JAVA_HOME must be set for harness tests");
let sources = vec![SourceFile {
path: "com/example/Sample.java".to_string(),
contents: r#"
package com.example;
public class Sample {
public void run() {
// code under test
}
}
"#.to_string(),
}];
let output = harness
.compile_and_analyze(Language::Java, &sources, &[])
.expect("run harness analysis");
let messages: Vec<String> = output
.results
.iter()
.filter(|result| result.rule_id.as_deref() == Some("RULE_ID"))
.filter_map(|result| result.message.text.clone())
.collect();
assert!(messages.iter().any(|msg| msg.contains("expected")));
Guardrails
- •Keep tests in the rule file to avoid a massive shared test module.
- •Use ASCII-only edits unless the file already uses Unicode.
- •Add doc comments to any new structs.
- •Verify changes with
cargo build,cargo test, andcargo audit --format sarif. - •If
cargo auditis unavailable, install it first viacargo install cargo-audit --locked.