{"record":{"id":"095af37dc184202d","repo":"crowdsecurity/crowdsec","slug":"allowlist-reason-exceeds-maximum-length","errorCode":null,"errorMessage":"allowlist reason exceeds maximum length","messagePattern":"allowlist reason exceeds maximum length","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/appsec/challenge/crypto.go","lineNumber":33,"sourceCode":"\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"golang.org/x/crypto/hkdf\"\n\n\t\"github.com/crowdsecurity/crowdsec/pkg/appsec/challenge/pb\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\nvar (\n\tErrCookieMalformed     = errors.New(\"malformed cookie\")\n\tErrCookieSignature     = errors.New(\"invalid cookie signature\")\n\tErrCookiePayload       = errors.New(\"invalid cookie payload\")\n\tErrCookieExpired       = errors.New(\"cookie expired\")\n\tErrCookieVersion       = errors.New(\"unknown cookie version\")\n\tErrAllowlistReasonSize = errors.New(\"allowlist reason exceeds maximum length\")\n\tErrCookieTooLarge      = errors.New(\"cookie exceeds maximum size\")\n)\n\nconst hkdfInfo = \"crowdsec-challenge-cookie\"\n\n// MaxAllowlistReasonLen caps the reason string operators pass to\n// GrantChallengeCookie. The reason travels inside every Set-Cookie + Cookie\n// header round-trip until the cookie expires; bounding it keeps the cookie\n// well under the 4 KB browser limit even with the AES-GCM tag + base64\n// expansion.\nconst MaxAllowlistReasonLen = 256\n\n// MaxCookieLen is the DEFAULT per-cookie size (RFC 6265 §6.1: 4096 bytes).\n// Can be configured via Config.MaxCookieSize and we reject anything bigger.\nconst MaxCookieLen = 4096\n\n// Cookie wire format. A single version byte at offset 0 lets us evolve the\n// format without flag-day-style cookie invalidation. New formats add a new","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/crowdsecurity/crowdsec/blob/909b5157986a2b2c2163300fdaef5ed01289f7d2/pkg/appsec/challenge/crypto.go#L15-L51","documentation":"ErrAllowlistReasonSize indicates the operator-supplied allowlist reason string embedded in a challenge cookie exceeds MaxAllowlistReasonLen. The reason travels inside every Set-Cookie/Cookie header, so sealCookieV0 rejects oversize reasons at seal time to guarantee the resulting cookie fits within the cookie size budget.","triggerScenarios":"Calling sealCookieV0 (via GrantChallengeCookie) with a reason string longer than MaxAllowlistReasonLen; also exercised by TestCookieV0_AllowlistReasonTooLong, which passes an oversize string from keyring_integration_test.go:367.","commonSituations":"An operator pastes a long justification or URL into the allowlist reason field; automation generates verbose reasons; a misconfigured UI does not truncate the reason input.","solutions":["Shorten the allowlist reason to at most MaxAllowlistReasonLen characters before granting the cookie.","Truncate or validate the reason at the API/UI boundary before calling GrantChallengeCookie.","Reference an external ID instead of embedding the full explanation (e.g. ticket number)."],"exampleFix":"// before\ncookie, err := sealCookieV0(c, key, notAfter, flag, longReason, ua, maxLen)\n// after\nif len(reason) > challenge.MaxAllowlistReasonLen {\n    reason = reason[:challenge.MaxAllowlistReasonLen]\n}\ncookie, err := sealCookieV0(c, key, notAfter, flag, reason, ua, maxLen)","handlingStrategy":"validation","validationCode":"if len(reason) > challenge.MaxAllowlistReasonLen {\n    return fmt.Errorf(\"allowlist reason must be <= %d chars\", challenge.MaxAllowlistReasonLen)\n}","typeGuard":null,"tryCatchPattern":"_, err := sealCookieV0(c, key, notAfter, flag, reason, ua, maxLen)\nif err != nil {\n    if errors.Is(err, challenge.ErrAllowlistReasonSize) {\n        reason = reason[:challenge.MaxAllowlistReasonLen]\n        _, err = sealCookieV0(c, key, notAfter, flag, reason, ua, maxLen)\n    }\n    return err\n}","preventionTips":["Validate/truncate reason length at the API/UI input boundary","Prefer short references (ticket IDs) over full explanations in reasons","Expose MaxAllowlistReasonLen in operator-facing docs","Add a unit test for the longest reason your tooling generates"],"tags":["appsec","cookie","validation","input-length"],"backgroundTag":"value-out-of-range","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"}