CLAUDE WATERMARK REMOVER · PRACTICAL TEST
Why does URL.username return percent escapes after assigning plain text?
Our username setter encodes a space as %20 and an at sign as %40. The getter retains that encoded spelling. Cleaning U+200B before assignment changes the stored username; it does not decode the URL property for you.
Open the Claude text cleaner · Full measured data
Measured inputs and outputs
These are locally constructed test strings, not evidence that Claude inserts these characters. We executed the saved homepage script snapshot with a minimal DOM harness and compared exact strings. The invisible-checkbox state and dash mode appear in each row. The observations below test this page's specific question. Destination observations run independently on the exact input and cleaned strings in the recorded Node runtime. We make no detector-score or statistical-watermark removal claim.
| Fixture and mode | Input string and code points | Output and cleaner status | Observed before / after |
|---|---|---|---|
| Marked username direct string / keep / invisible true | "AB"U+0041 U+200B U+0042 | "AB"Removed 1 invisible character | {"username":"A%E2%80%8BB","href":"https://A%E2%80%8BB@example.invalid/","decodedOnce":"AB","host":"example.invalid"}{"username":"AB","href":"https://AB@example.invalid/","decodedOnce":"AB","host":"example.invalid"} |
| Space in username direct string / keep / invisible true | "A B"U+0041 U+0020 U+0042 | "A B"No selected invisible characters found | {"username":"A%20B","href":"https://A%20B@example.invalid/","decodedOnce":"A B","host":"example.invalid"}{"username":"A%20B","href":"https://A%20B@example.invalid/","decodedOnce":"A B","host":"example.invalid"} |
| At sign in username direct string / keep / invisible true | "A@B"U+0041 U+0040 U+0042 | "A@B"No selected invisible characters found | {"username":"A%40B","href":"https://A%40B@example.invalid/","decodedOnce":"A@B","host":"example.invalid"}{"username":"A%40B","href":"https://A%40B@example.invalid/","decodedOnce":"A@B","host":"example.invalid"} |
| Encoded-looking username direct string / keep / invisible true | "A%20B"U+0041 U+0025 U+0032 U+0030 U+0042 | "A%20B"No selected invisible characters found | {"username":"A%20B","href":"https://A%20B@example.invalid/","decodedOnce":"A B","host":"example.invalid"}{"username":"A%20B","href":"https://A%20B@example.invalid/","decodedOnce":"A B","host":"example.invalid"} |
A property value can be a serialized component
We construct a URL for the reserved example.invalid host, assign each supplied string to its username property and read the property back. A B becomes A%20B, and A@B becomes A%40B. Reading username therefore does not give the original plain spelling in those controls. The host remains example.invalid in every observation. This differs from our earlier userinfo guide, which parses a complete URL and asks which component identifies the host. Today the component boundary is already chosen; the question is the representation produced by a setter and returned by its getter.
Compare actual and encoded-looking marker inputs
For A plus U+200B plus B, the setter records A%E2%80%8BB. After cleanup, it records AB. The literal A%20B control remains A%20B through assignment and cleanup, while a single decodeURIComponent call yields A B. These observations show why an encoded-looking value must not be assumed to be the same representation as an unencoded string. The cleaner acts before assignment and does not call the decoder. The observer retains username, href and the separately decoded value so the transformation responsible for each spelling is visible to a reviewer.
Label representations before comparing them
When debugging a component mismatch, identify whether the expected value is the original application string, URL serialization or a decoded display value. Compare like representations and decode only at a documented boundary. Our space and percent-looking controls converge to the same getter spelling even though their input strings differ. An application requiring preservation of that distinction must keep its original data separately instead of treating a round trip through this property as a lossless storage contract. This recommendation follows from the paired controls and does not require any request to the example host.
No login or network behavior was tested
All usernames are invented labels, and constructing these URL objects sends no request. We do not test browser navigation, HTTP authentication, password managers, account identifiers or a service accepting these strings. Do not substitute private userinfo into a public reproduction record. The precise capability measured is the WHATWG URL username property on this runtime. A changed serialization does not indicate a changed host, successful authentication or a safe destination. The homepage does not parse credential components or decide whether an identifier should be edited; review the intended spelling independently before applying character deletion to meaningful data.
Reproduce this test
Save reproduce.cjs and tested-app.js in the same folder. Run the command below with Node.js. The harness prints its runtime, script SHA-256 and every measured row. Compare those rows with the original record. Using a newer script or runtime creates a new experiment; retain the version information with your rerun.
node reproduce.cjsReference and next check
WHATWG URL username provides the relevant primary definition. The table and fixture analysis are original measurements. For broader inspection, use our Unicode inspector. Read the scope distinction before interpreting cleanup as a watermark result.