Great work!

XP to next level

BugEater

The Trimming Problem

Trimming whitespace from user input sounds like a safe, helpful thing to do. It is — when done consistently. Done inconsistently, it creates a class of bugs that are nearly impossible to notice visually and very difficult to reproduce.

What trim() Does and Doesn't Remove

Java's String.trim() removes all characters with code point ≤ U+0020 from both ends of the string. This includes:

  • Space (U+0020)
  • Tab (U+0009)
  • Newline (U+000A)
  • Carriage return (U+000D)
  • Form feed (U+000C)

It does NOT remove:

  • Non-breaking space (U+00A0)
  • Zero-width space (U+200B)
  • Ideographic space (U+3000)
  • Any other Unicode whitespace category member above U+0020

JavaScript's String.prototype.trim() follows the Unicode White_Space property and removes more characters, but still misses some invisible characters.

The consequence: a user who pastes text from a PDF, a messaging app, or a word processor may have invisible non-breaking spaces appended to their input. These survive trim() and remain in the stored value.

The Three Trim Policies

Trim on input, store clean: The system trims before storing. Submitted admin becomes stored admin. The stored value is clean. However, if the frontend validates length before trimming, it will count the spaces in the length. A 20-character field that accepts admin (22 chars including spaces) but stores admin (5 chars) has a silent mismatch between "what passed validation" and "what was stored."

Trim on comparison, store raw: The system stores the raw submitted value including any surrounding whitespace. Equality checks like login, lookup, and search apply trim() before comparing. This is safer: the stored value reflects exactly what was submitted. But if any comparison path forgets to trim, the check will fail for values with surrounding whitespace.

No trim, no consistency: The system sometimes trims, sometimes doesn't, depending on which developer wrote which endpoint. This is the bug factory. The same username submitted to registration (which trims) and to password reset (which doesn't) produces non-matching lookups.

The Inconsistency Bug Pattern

The canonical trimming bug requires exactly two conditions:

  1. At least one code path trims (or stores a trimmed value)
  2. At least one code path does not trim (or compares against a raw value)

If all paths trim consistently, everything works. If no paths trim, everything works (with some usability issues). The bug only manifests when one path says "these are the same" and another says "these are different" for the same user input.

Common scenario: registration trims and stores admin. Login does not trim and compares against submitted admin. Login fails for a valid username because adminadmin.

Testing for Trim Inconsistency

The test is simple to design:

  1. Register/create a resource using a value with leading or trailing whitespace (e.g., admin)
  2. Attempt to retrieve/authenticate/look up using the exact value with whitespace
  3. Attempt to retrieve/authenticate/look up using the trimmed value (e.g., admin)
  4. Compare the results

If step 2 works but step 3 doesn't: the system stored the raw value and compares exactly. If step 3 works but step 2 doesn't: the system stored trimmed and rejected the whitespace version. If both work: the system normalizes at both storage and lookup. If neither works: there may be another issue, or the system is broken.

The goal is to determine which behavior is consistent and whether it matches the specification.

The Empty After Trim Bug

A final case: the user submits (three spaces). After trimming, this becomes an empty string "". Does the system validate the trimmed value or the original? If it validates the original (3 characters), a spaces-only username passes the minimum-length check. If it validates the trimmed value (0 characters), it correctly rejects spaces-only input.

This is a common source of the "username is required" validator being defeated by whitespace-only input.

Quiz

A user registers with alice (leading space). The server trims before storing, saving alice. They log in typing alice with no space. What happens?

A QA tester submits two spaces to a required field. The server trims and gets an empty string. What should the correct server behaviour be?

A bug report says two accounts exist with the same email address that look identical on screen. What is the most likely cause?

Which approach most reliably prevents whitespace-related duplicate records in a username field?