{"record":{"id":"7fe64f7d42afb1e1","repo":"JuliusBrussee/caveman","slug":"w-rule-q-has-an-empty-pattern","errorCode":null,"errorMessage":"%w: rule %q has an empty pattern","messagePattern":"%w: rule %q has an empty pattern","errorType":"validation","errorClass":"ErrInvalidRule","httpStatus":null,"severity":"error","filePath":"shared/platform/redact/payload.go","lineNumber":582,"sourceCode":"\t\tsb.WriteByte('\\x1e')\n\n\t\tswitch r.Type {\n\t\tcase RuleTypeBuiltin:\n\t\t\t// A reference to the unconditional floor. Nothing to run, and\n\t\t\t// nothing it could switch off.\n\t\t\tcontinue\n\t\tcase RuleTypeRegex:\n\t\tcase RuleTypeJSONPath, RuleTypeHeader:\n\t\t\treturn nil, \"\", fmt.Errorf(\"%w: %q (rule %q)\", ErrRuleUnsupported, r.Type, r.Name)\n\t\tdefault:\n\t\t\treturn nil, \"\", fmt.Errorf(\"%w: %q (rule %q)\", ErrRuleUnsupported, r.Type, r.Name)\n\t\t}\n\n\t\tif strings.TrimSpace(r.Name) == \"\" {\n\t\t\treturn nil, \"\", fmt.Errorf(\"%w: empty name\", ErrInvalidRule)\n\t\t}\n\t\tif r.Pattern == \"\" {\n\t\t\treturn nil, \"\", fmt.Errorf(\"%w: rule %q has an empty pattern\", ErrInvalidRule, r.Name)\n\t\t}\n\t\tre, err := regexp.Compile(r.Pattern)\n\t\tif err != nil {\n\t\t\treturn nil, \"\", fmt.Errorf(\"%w: rule %q: %s\", ErrInvalidRule, r.Name, err)\n\t\t}\n\t\tif re.MatchString(\"\") {\n\t\t\t// Such a pattern matches at every position and would replace the\n\t\t\t// whole body with placeholders. Refuse it rather than destroy the\n\t\t\t// capture.\n\t\t\treturn nil, \"\", fmt.Errorf(\"%w: rule %q matches the empty string\", ErrInvalidRule, r.Name)\n\t\t}\n\t\trepl := r.Replacement\n\t\tif repl == \"\" {\n\t\t\trepl = \"[REDACTED:\" + r.Name + \"]\"\n\t\t}\n\t\tout = append(out, compiledRule{\n\t\t\tname:                 r.Name,\n\t\t\torigin:               OriginOrg,","sourceCodeStart":564,"sourceCodeEnd":600,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/redact/payload.go#L564-L600","documentation":"A regex rule has Pattern == \"\". An empty pattern compiles to a regex that matches at every position, which would blanket-replace the entire body with placeholders and destroy the capture — the same outcome the 'matches the empty string' guard exists for, caught one step earlier at the trivial case. Rejected with ErrInvalidRule, naming the rule.","triggerScenarios":"A Rule with Type RuleTypeRegex and an empty Pattern reaches compileOrgRules — typically a rule object created from config where the pattern key was absent, commented out, or the field name mistyped.","commonSituations":"Optional config sections that decode to zero-valued rules; a rule template/comment copied and not filled in; JSON schema not enforcing minLength on pattern","solutions":["Set a real Pattern or delete the placeholder rule from the list.","Validate rules at config load: for Type==regex require non-empty Pattern (and Name).","Add a JSON Schema / cue definition for the rules file with required fields so malformed rules fail before runtime."],"exampleFix":"// before\nrules := []redact.Rule{{Name: \"api-key\", Type: redact.RuleTypeRegex}} // Pattern missing\n\n// after\nrules := []redact.Rule{{Name: \"api-key\", Type: redact.RuleTypeRegex, Pattern: `(?i)api[_-]?key\"?\\s*[:=]\\s*\"?[A-Za-z0-9]{16,}`}}","handlingStrategy":"validation","validationCode":"func validatePatterns(rules []redact.Rule) error {\n    for _, r := range rules {\n        if r.Type == redact.RuleTypeRegex && r.Pattern == \"\" {\n            return fmt.Errorf(\"rule %q: empty pattern\", r.Name)\n        }\n    }\n    return nil\n}","typeGuard":"func hasPattern(r redact.Rule) bool { return r.Pattern != \"\" }","tryCatchPattern":"if _, _, err := redact.Payload(body, rules); err != nil {\n    if errors.Is(err, redact.ErrInvalidRule) && strings.Contains(err.Error(), \"empty pattern\") {\n        // drop or fill the placeholder rule, then retry\n    }\n}","preventionTips":["Enforce required fields in the rules file schema (JSON Schema required: [name, type, pattern]).","Validate the rule set at config load with a startup check.","Treat zero-valued Rule structs in a list as a config bug, not a no-op."],"tags":["redaction","validation","config"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}