crowdsecurity/crowdsec · error

ErrAllowlistReasonSize

ErrAllowlistReasonSize

Error message

%w: %d > %d

What it means

sealCookieV0 rejects allowlist reasons longer than MaxAllowlistReasonLen (256 bytes) before sealing, because the reason rides inside every Set-Cookie/Cookie round-trip until expiration and must keep the cookie under the 4 KB browser limit. The sentinel error is ErrAllowlistReasonSize, reported with the actual vs. maximum lengths.

Source

Thrown at pkg/appsec/challenge/crypto.go:114

	return key, nil
}

// sealCookieV0 produces a v0 cookie sealed under the long-lived master
// cookie key. notAfter is the unix-seconds expiration; flags carries the
// allowlist bit set by GrantChallengeCookie (0 for normal cookies);
// reason is the operator-supplied allowlist reason (empty for normal
// cookies). All three are prepended to the marshaled proto BEFORE
// encryption so they are both confidential (not observable from the wire)
// and authenticated (any tamper attempt invalidates the GCM tag).
//
// Returns ErrAllowlistReasonSize if reason exceeds MaxAllowlistReasonLen.
func sealCookieV0(envelope *pb.ChallengeCookie, masterCookieKey []byte, notAfter int64, flags byte, reason string, aad []byte, maxCookieLen int) (string, error) {
	if maxCookieLen <= 0 {
		maxCookieLen = MaxCookieLen
	}

	if len(reason) > MaxAllowlistReasonLen {
		return "", fmt.Errorf("%w: %d > %d", ErrAllowlistReasonSize, len(reason), MaxAllowlistReasonLen)
	}

	key, err := deriveKey(masterCookieKey)
	if err != nil {
		return "", err
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return "", fmt.Errorf("failed to create cipher: %w", err)
	}

	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", fmt.Errorf("failed to create GCM: %w", err)
	}

	// Reject an over-limit envelope before marshaling it.

View on GitHub (pinned to 909b515798)

Solutions

  1. Truncate or shorten the reason to 256 bytes or fewer before calling SealAllowlistCookie.
  2. Compare errors.Is(err, challenge.ErrAllowlistReasonSize) to detect this case specifically and return a clear 4xx to the operator.
  3. Store long explanations out-of-band (database, ticket system) and keep only a short identifier in the cookie reason.

Example fix

// before
ck, err := rt.SealAllowlistCookie(req, longBanReason, nil)
// after
if len(longBanReason) > challenge.MaxAllowlistReasonLen {
    longBanReason = longBanReason[:challenge.MaxAllowlistReasonLen]
}
ck, err := rt.SealAllowlistCookie(req, longBanReason, nil)
Defensive patterns

Strategy: validation

Validate before calling

if len(reason) > challenge.MaxAllowlistReasonLen {
    reason = reason[:challenge.MaxAllowlistReasonLen]
}

Try / catch

ck, err := rt.SealAllowlistCookie(req, reason, nil)
if errors.Is(err, challenge.ErrAllowlistReasonSize) {
    return http.StatusRequestEntityTooLarge
}

Prevention

When it happens

Trigger: Calling SealAllowlistCookie/GrantChallengeCookie (or sealCookieV0 directly) with a reason string whose byte length exceeds 256; ValidateChallengeResponse and normal cookie sealing are unaffected since they pass an empty reason.

Common situations: Operators writing descriptive allowlist reasons (URLs, explanations, concatenated ticket references) into the allowlist grant API; automated integrations echoing ban descriptions as reasons.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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