CLAUDE WATERMARK REMOVER · PRACTICAL TEST
Why does a cleaned data URL have origin equal to the string null?
Both of our data URLs serialize origin as the string "null", not JavaScript null. They carry different payloads yet share that printed origin. Treat this serialization as an opaque-origin representation, not a unique address or identity token.
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 data scheme direct string / keep / invisible true | "data:text/plain,A"U+0064 U+0061 U+200B U+0074 U+0061 U+003A U+0074 U+0065 U+0078 U+0074 U+002F U+0070 U+006C U+0061 U+0069 U+006E U+002C U+0041 | "data:text/plain,A"Removed 1 invisible character | {"error":"TypeError"}{"protocol":"data:","origin":"null","originType":"string","equalsNullString":true,"equalsNullValue":false} |
| Data URL A direct string / keep / invisible true | "data:text/plain,A"U+0064 U+0061 U+0074 U+0061 U+003A U+0074 U+0065 U+0078 U+0074 U+002F U+0070 U+006C U+0061 U+0069 U+006E U+002C U+0041 | "data:text/plain,A"No selected invisible characters found | {"protocol":"data:","origin":"null","originType":"string","equalsNullString":true,"equalsNullValue":false}{"protocol":"data:","origin":"null","originType":"string","equalsNullString":true,"equalsNullValue":false} |
| Data URL B direct string / keep / invisible true | "data:text/plain,B"U+0064 U+0061 U+0074 U+0061 U+003A U+0074 U+0065 U+0078 U+0074 U+002F U+0070 U+006C U+0061 U+0069 U+006E U+002C U+0042 | "data:text/plain,B"No selected invisible characters found | {"protocol":"data:","origin":"null","originType":"string","equalsNullString":true,"equalsNullValue":false}{"protocol":"data:","origin":"null","originType":"string","equalsNullString":true,"equalsNullValue":false} |
| HTTPS reference direct string / keep / invisible true | "https://example.test/p"U+0068 U+0074 U+0074 U+0070 U+0073 U+003A U+002F U+002F U+0065 U+0078 U+0061 U+006D U+0070 U+006C U+0065 U+002E U+0074 U+0065 U+00 … | "https://example.test/p"No selected invisible characters found | {"protocol":"https:","origin":"https://example.test","originType":"string","equalsNullString":false,"equalsNullValue":false}{"protocol":"https:","origin":"https://example.test","originType":"string","equalsNullString":false,"equalsNullValue":false} |
The value has a string type even when it prints null
The marked data scheme initially fails URL construction. After deletion the exact data URL parses and returns protocol data:, origin null in string form, originType string, true for equality with the null spelling and false for equality with the null value. Saving both comparisons avoids ambiguity introduced by plain logging. The homepage changes only scheme adjacency. It does not generate an origin object, navigate a frame or decide whether a URL should be opened. All URL operations in this experiment are local parsing and serialization.
Different data payloads share the same printed origin
The two data controls end in A and B respectively. Both produce the same serialized origin and the same type comparisons. That shared string therefore does not uniquely label those two URLs or their payloads. The HTTPS reference instead yields https://example.test, still a string, with both null comparisons false. No address is fetched, and the example domain is only a parsing fixture. This comparison goes beyond the earlier protocol-policy page, which asks whether a parsed scheme is allowed; here the focus is what the origin getter actually returns.
Do not derive browsing permissions from this diagnostic
Use the origin model and context required by the destination when evaluating browser relationships. Equality between two printed null strings is not a same-origin permission test, and this harness does not create documents to test those permissions. It also does not cover blob URLs, file origins, redirects or sandbox settings. The linked URL standard defines the getter and its serialization; our saved fields establish the results of these four calls. Authored data URLs provide no evidence of Claude provenance, watermark presence, detection scores or authorization to contact any destination.
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 origin getter 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.