Detail Bug Report
https://app.detail.dev/org_ea7bf3e3-a2f4-4402-9351-baa0e1eaa1f5/bugs/bug_1da31ddb-6151-4699-8e1d-65d3ee1b30f9
Summary
- Context: The
FormoAnalyticsSession class manages session-based deduplication for wallet detection and identification events using browser cookies (or fallback storage) to avoid redundant tracking calls.
- Bug: The session tracking logic appends an unbounded list of wallet-address pairs to a single cookie without size checks, rotation, or limit enforcement.
- Actual vs. expected: When the list of identified addresses exceeds browser cookie size limits (typically 4KB), the cookie is truncated by the browser. This causes the
isWalletIdentified check to fail for the truncated entries, leading to duplicate IDENTIFY events on every page load and causing the cookie to grow indefinitely with redundant, broken entries.
- Impact: Severe impact including event spamming the analytics backend and causing "431 Request Header Fields Too Large" errors, which can completely block a user from accessing the website.
Code with Bug
public markWalletIdentified(address: string, rdns: string): void {
const identifiedKey = this.generateIdentificationKey(address, rdns);
const identifiedWallets =
cookie().get(SESSION_WALLET_IDENTIFIED_KEY)?.split(",") || [];
const alreadyExists = identifiedWallets.includes(identifiedKey);
if (!alreadyExists) {
identifiedWallets.push(identifiedKey);
const newValue = identifiedWallets.join(",");
cookie().set(SESSION_WALLET_IDENTIFIED_KEY, newValue, { // <-- BUG 🔴 unbounded cookie value can exceed browser limits and get truncated
// Expires by the end of the day
expires: new Date(Date.now() + 86400 * 1000).toUTCString(),
path: "/",
});
}
}
Explanation
- The code stores a comma-separated list of "wallet identification keys" in a single cookie and keeps appending new entries with no cap or size validation.
- Browsers typically enforce ~4KB per-cookie limits; once exceeded, the stored value is truncated.
- After truncation, entries near the end of the list are lost, so
isWalletIdentified no longer recognizes previously-identified wallets. This causes the system to repeatedly emit IDENTIFY events and repeatedly rewrite the cookie on subsequent page loads.
- Continued growth of cookie headers can eventually trigger HTTP "431 Request Header Fields Too Large", blocking the site for affected users.
- In fallback mode, the issue can be worse because the
WebStorage (local storage fallback) ignores the expires option (src/storage/built-in/web.ts), allowing the stored list to grow indefinitely across days.
Recommended Fix
- Implement a limit: cap the number of identified addresses stored in the session (e.g., keep only the last 20 seen addresses).
- Size Validation: check the length of
newValue before calling cookie().set().
- Fallback Expiration: update
WebStorage to respect expiration or use a timestamp-prefixed value to handle session cleanup in Local Storage.
History
This bug was introduced in commit e540da2. The change was intended to prevent duplicate identify events within a session by tracking previously identified wallets in a cookie, but it failed to implement any size limits or rotation, leading to unbounded cookie growth.
Detail Bug Report
https://app.detail.dev/org_ea7bf3e3-a2f4-4402-9351-baa0e1eaa1f5/bugs/bug_1da31ddb-6151-4699-8e1d-65d3ee1b30f9
Summary
FormoAnalyticsSessionclass manages session-based deduplication for wallet detection and identification events using browser cookies (or fallback storage) to avoid redundant tracking calls.isWalletIdentifiedcheck to fail for the truncated entries, leading to duplicateIDENTIFYevents on every page load and causing the cookie to grow indefinitely with redundant, broken entries.Code with Bug
Explanation
isWalletIdentifiedno longer recognizes previously-identified wallets. This causes the system to repeatedly emitIDENTIFYevents and repeatedly rewrite the cookie on subsequent page loads.WebStorage(local storage fallback) ignores theexpiresoption (src/storage/built-in/web.ts), allowing the stored list to grow indefinitely across days.Recommended Fix
newValuebefore callingcookie().set().WebStorageto respect expiration or use a timestamp-prefixed value to handle session cleanup in Local Storage.History
This bug was introduced in commit e540da2. The change was intended to prevent duplicate identify events within a session by tracking previously identified wallets in a cookie, but it failed to implement any size limits or rotation, leading to unbounded cookie growth.