crowdsecurity/crowdsec · error
leaf rule must have zones
Error message
leaf rule must have zones
What it means
When flattening a CustomRule tree into disjunctive normal form (DNF) for modsecurity/Coraza compilation, flattenToDNF treats a node with no `and`/`or` children as a leaf, and a leaf must carry a zones list. A childless node without zones is structurally invalid and cannot be rendered as a match condition, so the build of the whole rule fails.
Source
Thrown at pkg/appsec/appsec_rule/modsecurity.go:141
return strings.Join(rules, "\n"), m.ids, nil
}
// leafCopy returns a shallow copy of the rule with And/Or cleared.
func leafCopy(rule *CustomRule) *CustomRule {
cp := *rule
cp.And = nil
cp.Or = nil
return &cp
}
// flattenToDNF converts a CustomRule tree into Disjunctive Normal Form:
// a list of AND-groups (conjunctions), where the outer list is OR.
func flattenToDNF(rule *CustomRule) ([][]*CustomRule, error) {
// Leaf node: has zones, no children
if len(rule.And) == 0 && len(rule.Or) == 0 {
if rule.Zones == nil {
return nil, errors.New("leaf rule must have zones")
}
return [][]*CustomRule{{rule}}, nil
}
// Collect DNF parts to be AND-combined via cross-product
var parts [][][]*CustomRule
// If this node has zones alongside And/Or children, treat as implicit AND term
if rule.Zones != nil {
parts = append(parts, [][]*CustomRule{{leafCopy(rule)}})
}
// Each And child's DNF is cross-producted
for i := range rule.And {
childDNF, err := flattenToDNF(&rule.And[i])
if err != nil {
return nil, errView on GitHub (pinned to 909b515798)
Solutions
- Add a zones list to the leaf rule
- If the node was meant to group children, add `and:` or `or:` entries instead
- Re-check the generator/template producing rules so every leaf includes zones
Example fix
// before
rule := &CustomRule{Match: Match{Type: "contains", Value: "x"}}
// after
rule := &CustomRule{Zones: []string{"URI"}, Match: Match{Type: "contains", Value: "x"}} Defensive patterns
Strategy: validation
Validate before calling
func isLeaf(r *CustomRule) bool { return len(r.And) == 0 && len(r.Or) == 0 }
if isLeaf(rule) && rule.Zones == nil {
return fmt.Errorf("leaf rule %+v must define zones", rule)
} Prevention
- When building rules programmatically, always set Zones for childless nodes
- Add unit tests constructing each rule shape you generate
When it happens
Trigger: A leaf rule (no and/or) has `zones` nil — e.g. only `match:` present; flattenToDNF is reached from CustomRule Build via modsecurity.go during rule compilation.
Common situations: Rules constructed programmatically or by a generator that set Match but forgot Zones; YAML where the zones key was dropped; interacting with the DNF expansion path after refactor.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- no zones defined
- no match type defined
- no match value defined
- rule has no zones, 'and', or 'or' children
- invalid schema name
AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06).
Data as JSON: /api/errors/fc8745ff56be2a0a.
Report an issue: GitHub.