stamparm/maltrail · error
wildcard alert
Error message
wildcard alert
What it means
Test assertion in wildcard_key_builds_the_python_trail: after pumping > NO_SUCH_NAME_PER_HOUR_THRESHOLD queries under one parent domain, observe() must eventually emit NxAlert::Wildcard carrying a Python-style trail string; the expect fails if no wildcard alert was produced. It encodes the contract that sustained NXDOMAIN traffic under one suffix is classified as a wildcard.
Solutions
- Confirm observe() still returns NxAlert::Wildcard once the hourly threshold is exceeded
- Check the wildcard key builder still aggregates the parent domain instead of per-subdomain keys
- Verify the test's key/parent-domain setup matches the detector's expectations
Defensive patterns
Strategy: validation
Validate before calling
let mut got_wildcard = false;
for i in 0..(settings::NO_SUCH_NAME_PER_HOUR_THRESHOLD + 2) {
if matches!(nx.observe(key, &format!("h{i}.evil.com"), 10), Some(NxAlert::Wildcard { .. })) { got_wildcard = true; }
}
assert!(got_wildcard, "wildcard alert never fired"); Prevention
- When changing alert variants, grep tests for exhaustive matches on NxAlert
- Keep threshold constants in settings so tests scale with them
When it happens
Trigger: The loop runs NO_SUCH_NAME_PER_HOUR_THRESHOLD+2 times yet no NxAlert::Wildcard variant is returned — the wildcard detector never fires (threshold logic, key building, or counting regressed) or observe now returns a different NxAlert variant.
Common situations: Changing the wildcard detection threshold, renaming/alert-variant refactors, or a bug where repeated subdomains are deduplicated so the counter never crosses the threshold.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- must load
- config must load
- hex is ascii
- [!] no VERSION constant found in
- [!] no maltrail-sensor package entry found in
AI-assisted analysis of stamparm/maltrail@77cfb06d76 (2026-09-13).
Data as JSON: /api/errors/507f50f1b1256fc0.
Report an issue: GitHub.
Appendix: source
Thrown at sensor/src/heuristics/nxdomain.rs:180
if let Some(NxAlert::Exact { trail }) = nx.observe("a.com", "a.com", 10) {
assert_eq!(trail, "a.com");
alerts += 1;
}
}
assert_eq!(alerts, 1, "the key is dropped after alerting, so it fires once");
}
#[test]
fn wildcard_key_builds_the_python_trail() {
let mut nx = NxCounters::default();
let key = "*.evil.com";
let mut trail = None;
for i in 0..(settings::NO_SUCH_NAME_PER_HOUR_THRESHOLD + 2) {
if let Some(NxAlert::Wildcard { trail: t, .. }) = nx.observe(key, &format!("h{i}.evil.com"), 10) {
trail = Some(t);
}
}
let trail = trail.expect("wildcard alert");
assert!(trail.starts_with('('), "{trail}");
assert!(trail.ends_with(").evil.com"), "{trail}");
assert!(trail.contains("h1,"), "{trail}");
}
#[test]
fn entropy_and_consonants() {
// sanity: a high-entropy DGA-looking label beats the 3.5 threshold
assert!(label_entropy("xkqwzlvbnmfghjd") > settings::SUSPICIOUS_DOMAIN_ENTROPY_THRESHOLD);
assert!(label_entropy("aaaa") < 0.001);
assert_eq!(label_entropy(""), 0.0);
assert_eq!(consonant_count("google"), 3);
assert!(consonant_count("xkqwzlvbnmf") > settings::SUSPICIOUS_DOMAIN_CONSONANT_THRESHOLD);
}
}
View on GitHub (pinned to 77cfb06d76)