{"record":{"id":"73fa317031a1eaf2","repo":"crowdsecurity/crowdsec","slug":"no-match-type-defined","errorCode":null,"errorMessage":"no match type defined","messagePattern":"no match type defined","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/appsec/appsec_rule/appsec_rule.go","lineNumber":57,"sourceCode":"\tVariables []string `yaml:\"variables\"`\n\n\tMatch     Match        `yaml:\"match\"`\n\tTransform []string     `yaml:\"transform\"` //t:lowercase, t:uppercase, etc\n\tAnd       []CustomRule `yaml:\"and,omitempty\"`\n\tOr        []CustomRule `yaml:\"or,omitempty\"`\n\n\tBodyType string `yaml:\"body_type,omitempty\"`\n}\n\n// Convert renders the rule; ruleIndex is its position in the collection, used\n// to keep ids unique across rules that share identical leaves.\nfunc (v *CustomRule) Convert(ruleType string, appsecRuleName string, appsecRuleDescription string, ruleIndex int) (string, []uint32, error) {\n\tif v.Zones == nil && v.And == nil && v.Or == nil {\n\t\treturn \"\", nil, errors.New(\"no zones defined\")\n\t}\n\n\tif v.Match.Type == \"\" && v.And == nil && v.Or == nil {\n\t\treturn \"\", nil, errors.New(\"no match type defined\")\n\t}\n\n\tif v.Match.Value == \"\" && v.And == nil && v.Or == nil {\n\t\treturn \"\", nil, errors.New(\"no match value defined\")\n\t}\n\n\tswitch ruleType {\n\tcase ModsecurityRuleType:\n\t\tr := ModsecurityRule{}\n\t\treturn r.Build(v, appsecRuleName, appsecRuleDescription, ruleIndex)\n\tdefault:\n\t\treturn \"\", nil, fmt.Errorf(\"unknown rule format '%s'\", ruleType)\n\t}\n}\n","sourceCodeStart":39,"sourceCodeEnd":72,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/appsec_rule/appsec_rule.go#L39-L72","documentation":"CustomRule.Convert requires a non-empty match type for leaf rules (e.g. contains, regex, equals). A rule that declares zones but has no `match.type` cannot be compiled, so Convert rejects it. This only fires for standalone leaf rules, not for rules that delegate matching to `and`/`or` children.","triggerScenarios":"Defining a rule with `zones:` but missing or empty `match.type`, e.g. only `match.value:` given; Convert is called during collection loading (as in the appsec_rule unit tests).","commonSituations":"Typo like `type: contains_any` misspelled to empty; a copy-paste that dropped the `type:` key; dynamically generated rules where match type interpolation produced an empty string.","solutions":["Set a valid match type, e.g. `match: {type: contains, value: ...}`","Verify the `match:` mapping is not nested/indented into a sibling key","Refer to the supported match types (contains, regex, equals, starts_with, etc.) in the docs"],"exampleFix":"// before\n- zones:\n    - URI\n  match:\n    value: bad-string\n\n// after\n- zones:\n    - URI\n  match:\n    type: contains\n    value: bad-string","handlingStrategy":"validation","validationCode":"if len(rule.Zones) > 0 && rule.Match.Type == \"\" {\n    return fmt.Errorf(\"rule %q: zones set but match.type is empty\", rule.Name)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use a config template with required match.type/value fields to avoid omissions","Validate generated rules in CI before loading them"],"tags":["appsec","rules","validation"],"backgroundTag":"missing-required-config-field","analyzedSha":"909b5157986a2b2c2163300fdaef5ed01289f7d2","analyzedAt":"2026-09-06T12:27:26.012Z","contentChangedAt":"2026-09-06T12:27:26.012Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}