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, err

View on GitHub (pinned to 909b515798)

Solutions

  1. Add a zones list to the leaf rule
  2. If the node was meant to group children, add `and:` or `or:` entries instead
  3. 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 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


AI-assisted analysis of crowdsecurity/crowdsec@909b515798 (2026-09-06). Data as JSON: /api/errors/fc8745ff56be2a0a. Report an issue: GitHub.