{"record":{"id":"064538e0f2405356","repo":"crowdsecurity/crowdsec","slug":"bot-entry-s-in-s-has-no-identity-verification","errorCode":null,"errorMessage":"bot entry '%s' in %s has no identity verification (need at least one of ips/ranges/rdns)","messagePattern":"bot entry '(.+?)' in (.+?) has no identity verification \\(need at least one of ips/ranges/rdns\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/exprhelpers/botfile.go","lineNumber":63,"sourceCode":"\treturn regexp.Compile(\"(?i)\" + pattern) // Force case insensitive match\n}\n\nfunc botFileInit(filename string, line string) error {\n\tentry := &botEntry{}\n\n\tdec := json.NewDecoder(strings.NewReader(line))\n\tdec.DisallowUnknownFields()\n\n\tif err := dec.Decode(entry); err != nil {\n\t\treturn fmt.Errorf(\"failed to parse JSON line in %s: %w\", filename, err)\n\t}\n\n\tif entry.Name == \"\" {\n\t\treturn fmt.Errorf(\"missing mandatory 'name' field in %s: %s\", filename, line)\n\t}\n\n\tif len(entry.IPs)+len(entry.Ranges)+len(entry.RDNS) == 0 {\n\t\treturn fmt.Errorf(\"bot entry '%s' in %s has no identity verification (need at least one of ips/ranges/rdns)\", entry.Name, filename)\n\t}\n\n\tvar err error\n\n\tif entry.UserAgent != \"\" {\n\t\tif entry.uaRegex, err = compileBotRegex(entry.UserAgent); err != nil {\n\t\t\treturn fmt.Errorf(\"invalid user_agent regex for bot entry '%s' in %s: %w\", entry.Name, filename, err)\n\t\t}\n\t}\n\n\tfor _, p := range entry.Paths {\n\t\tre, err := compileBotRegex(p)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"invalid path regex '%s' for bot entry '%s' in %s: %w\", p, entry.Name, filename, err)\n\t\t}\n\n\t\tentry.pathRegexes = append(entry.pathRegexes, re)\n\t}","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/exprhelpers/botfile.go#L45-L81","documentation":"botFileInit enforces that every bot entry carries at least one identity-verification mechanism: ips, ranges, or rdns. This error is thrown when an entry has none of these, i.e. it would only (potentially) match on user_agent/paths. UA-only entries are rejected by design because a User-Agent is trivially spoofable — identity must be corroborated by IP, CIDR, or forward-confirmed reverse DNS.","triggerScenarios":"A bots JSONL line defines only name (+ optionally user_agent/paths) with no \"ips\", \"ranges\", or \"rdns\" arrays, e.g. {\"name\":\"googlebot\",\"user_agent\":\"Googlebot\"}.","commonSituations":"Porting a UA-only blocklist from another tool where UA matching alone was accepted; authoring a new bot entry and assuming UA is sufficient; copying an example that showed only the UA field; empty arrays like \"ips\":[] also trigger since len sums to 0.","solutions":["Add at least one identity source to the entry: verified ips (e.g. Google/BD published ranges), a \"ranges\" CIDR list, or an \"rdns\" pattern.","For crawler bots, fetch the operator's official published IP ranges (e.g. https://developers.google.com/search/apis/ipranges) and put them in \"ranges\".","Use \"rdns\":[\"(^|\\.)googlebot\\.com$\"] style forward-confirmed reverse-DNS patterns when IPs are not published.","If you truly want UA-only matching, this loader is not the place — use a parse/expression rule in a scenario instead."],"exampleFix":"// before\n{\"name\":\"googlebot\",\"user_agent\":\"Googlebot\"}\n// after\n{\"name\":\"googlebot\",\"user_agent\":\"Googlebot\",\"rdns\":[\"(^|\\.)googlebot\\.com$\"]}","handlingStrategy":"validation","validationCode":"var probe struct {\n\tIPs    []string `json:\"ips\"`\n\tRanges []string `json:\"ranges\"`\n\tRDNS   []string `json:\"rdns\"`\n}\n_ = json.Unmarshal([]byte(line), &probe)\nvalid := len(probe.IPs)+len(probe.Ranges)+len(probe.RDNS) > 0","typeGuard":null,"tryCatchPattern":"if err := exprhelpers.FileInit(botFile, \"bots\"); err != nil {\n\tif strings.Contains(err.Error(), \"no identity verification\") {\n\t\tlog.Errorf(\"entry needs ips/ranges/rdns: %v\", err)\n\t}\n\treturn err\n}","preventionTips":["Never author UA-only entries; always attach published IP ranges or an rdns pattern.","For big operators, pull official IP-range feeds (e.g. Google ipranges JSON) into \"ranges\".","Remember empty arrays count as zero — remove unused empty arrays rather than relying on them.","Prefer rdns for operators without stable published ranges; anchor the regex."],"tags":["go","validation","data-files","crowdsec"],"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-14T05:17:10.506Z"}