Audit seed
Fresh Nemesis / nana-project-handles-v6 all src/**/*.sol and script/**/*.sol / identity verification + input monoculture breaker / Codex
Repos involved
Root cause
JBProjectHandles.setEnsNamePartsFor rejects empty labels, dots, ASCII controls, DEL, exact eth, and a selected set of Unicode format controls. The blocklist omits other invisible format controls such as U+2060 WORD JOINER. Accepted bytes are stored in _ensNamePartsOf and later returned verbatim by handleOf as a verified handle if the ENS juicebox text record matches.
Relevant code:
src/JBProjectHandles.sol:115-118 validates each byte and calls _isDisallowedUnicodeFormat.
src/JBProjectHandles.sol:228-255 only blocks selected Unicode format-control sequences.
src/JBProjectHandles.sol:124 stores accepted parts.
src/JBProjectHandles.sol:198 returns _formatHandle(ensNameParts) after ENS text-record verification.
Impact
A setter can publish a visually deceptive verified handle containing invisible formatting bytes. This does not create direct fund loss, but it breaks the identity registry's display-safety invariant and can mislead frontends, indexers, and users that treat handleOf output as safe verified metadata.
Proof of concept
Added local audit PoC: test/audit/CodexNemesisUnicodeFormatBypass.t.sol.
Sequence:
- Store a label containing U+2060:
unicode"safe\u2060evil".
- Mock the ENS registry to return a resolver for the exact node.
- Mock the resolver text record to return
"1:123".
- Call
handleOf(1, 123, setter).
- The call returns the stored handle and its byte length is
bytes("safeevil").length + 3, confirming the invisible UTF-8 sequence survived verification.
Verification command:
forge test --match-path test/audit/CodexNemesisUnicodeFormatBypass.t.sol -vvv
Result:
[PASS] test_verifiedHandleCanContainWordJoinerFormatControl()
Full suite result after adding the PoC:
71 tests passed, 0 failed, 0 skipped
Why this survived self-review
The strongest counter-argument is that callers are instructed to submit ENS-normalized labels offchain. That does not eliminate the issue because the contract explicitly attempts to reject dangerous formatting controls before storage, then treats the stored bytes as display-safe verified output. Once accepted, no later normalization or display-safety check occurs before handleOf returns the value.
Recommended fix
At minimum, reject the full U+2060-U+206F invisible/format-control range:
if (second == 0x81) return third >= 0xa0 && third <= 0xaf;
Consider also blocking other common invisible or display-altering codepoints such as U+00AD, U+034F, U+180E, U+FE00-U+FE0F, and variation selector supplement codepoints if non-ASCII labels remain supported. A stricter alternative is an onchain allowlist for display labels plus ENSIP-15 normalization offchain.
Audit seed
Fresh Nemesis / nana-project-handles-v6 all
src/**/*.solandscript/**/*.sol/ identity verification + input monoculture breaker / CodexRepos involved
nana-project-handles-v6Root cause
JBProjectHandles.setEnsNamePartsForrejects empty labels, dots, ASCII controls, DEL, exacteth, and a selected set of Unicode format controls. The blocklist omits other invisible format controls such as U+2060 WORD JOINER. Accepted bytes are stored in_ensNamePartsOfand later returned verbatim byhandleOfas a verified handle if the ENSjuiceboxtext record matches.Relevant code:
src/JBProjectHandles.sol:115-118validates each byte and calls_isDisallowedUnicodeFormat.src/JBProjectHandles.sol:228-255only blocks selected Unicode format-control sequences.src/JBProjectHandles.sol:124stores accepted parts.src/JBProjectHandles.sol:198returns_formatHandle(ensNameParts)after ENS text-record verification.Impact
A setter can publish a visually deceptive verified handle containing invisible formatting bytes. This does not create direct fund loss, but it breaks the identity registry's display-safety invariant and can mislead frontends, indexers, and users that treat
handleOfoutput as safe verified metadata.Proof of concept
Added local audit PoC:
test/audit/CodexNemesisUnicodeFormatBypass.t.sol.Sequence:
unicode"safe\u2060evil"."1:123".handleOf(1, 123, setter).bytes("safeevil").length + 3, confirming the invisible UTF-8 sequence survived verification.Verification command:
forge test --match-path test/audit/CodexNemesisUnicodeFormatBypass.t.sol -vvvResult:
Full suite result after adding the PoC:
Why this survived self-review
The strongest counter-argument is that callers are instructed to submit ENS-normalized labels offchain. That does not eliminate the issue because the contract explicitly attempts to reject dangerous formatting controls before storage, then treats the stored bytes as display-safe verified output. Once accepted, no later normalization or display-safety check occurs before
handleOfreturns the value.Recommended fix
At minimum, reject the full U+2060-U+206F invisible/format-control range:
Consider also blocking other common invisible or display-altering codepoints such as U+00AD, U+034F, U+180E, U+FE00-U+FE0F, and variation selector supplement codepoints if non-ASCII labels remain supported. A stricter alternative is an onchain allowlist for display labels plus ENSIP-15 normalization offchain.