Skip to content

fix(apps/ensadmin): validate name query params on name details page#1834

Draft
notrab wants to merge 19 commits intomainfrom
notrab/normalized-name-state
Draft

fix(apps/ensadmin): validate name query params on name details page#1834
notrab wants to merge 19 commits intomainfrom
notrab/normalized-name-state

Conversation

@notrab
Copy link
Copy Markdown
Member

@notrab notrab commented Mar 26, 2026

Lite PR

Tip: Review docs on the ENSNode PR process

Summary

  • Validate name query param using isInterpretedName and isNormalizedName before rendering the name detail page
  • Show ErrorInfo cards for invalid names (not ENS normalized) and names with encoded labelhashes not yet supported
  • Tighten prop types from Name to NormalizedName in NameDetailPageContent and ProfileHeader

Why


Testing

I've tested the updated form inputs (typing and clicking on the View profile button):

value expected
(empty) Button disabled
vitalik.eth Redirects to ?name=vitalik.eth
VITALIK.ETH Normalizes → redirects to ?name=vitalik.eth
LiGhTWaLkEr.EtH Normalizes → redirects to ?name=lightwalker.eth
No-op (Empty outcome, trimmed to empty)
. Inline error: "not a valid ENS name"
abc..123 Inline error: "not a valid ENS name"
abc|123.eth Inline error: "not a valid ENS name"
% Inline error: "not a valid ENS name"
[e4310bf4547cb18b16b5348881d24a66d61fa94a013e5636b730b86ee64a3923].eth Inline error: "encoded labelhashes...coming soon"

and the direct url

url expected behaviour
/name?name=vitalik.eth` Shows profile
/name?name=VITALIK.ETH UnnormalizedNameError card
/name?name=abc%7C123.eth UnnormalizedNameError card
/name?name=[e4310bf4547cb18b16b5348881d24a66d61fa94a013e5636b730b86ee64a3923].eth InterpretedNameUnsupportedError card
/name?name= Shows the form
/name Shows the form

Notes for Reviewer (Optional)

  • Removed unnecessary decodeURIComponent calls in @actions/name/page.tsx and @breadcrumbs/name/page.tsx as Next.js searchParams.get() already returns decoded values
  • The breadcrumbs page falls back to rendering the name as plain text when it's not a valid InterpretedName, since NameDisplay calls beautifyName which expects valid inptu

Pre-Review Checklist (Blocking)

  • This PR does not introduce significant changes and is low-risk to review quickly.
  • Relevant changesets are included (or are not required)

@notrab notrab requested a review from a team as a code owner March 26, 2026 08:17
Copilot AI review requested due to automatic review settings March 26, 2026 08:17
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Mar 26, 2026

🦋 Changeset detected

Latest commit: f2c0398

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 19 packages
Name Type
ensadmin Patch
ensindexer Patch
ensrainbow Patch
ensapi Patch
fallback-ensapi Patch
@ensnode/datasources Patch
@ensnode/ensrainbow-sdk Patch
@ensnode/ensdb-sdk Patch
@ensnode/ensnode-react Patch
@ensnode/ensnode-sdk Patch
@ensnode/ponder-sdk Patch
@ensnode/ponder-subgraph Patch
@ensnode/shared-configs Patch
@docs/ensnode Patch
@docs/ensrainbow Patch
@docs/mintlify Patch
@namehash/ens-referrals Patch
@namehash/namehash-ui Patch
@ensnode/integration-test-env Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
admin.ensnode.io Ready Ready Preview, Comment Mar 31, 2026 5:38pm
ensnode.io Ready Ready Preview, Comment Mar 31, 2026 5:38pm
ensrainbow.io Ready Ready Preview, Comment Mar 31, 2026 5:38pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds input interpretation/normalization and inline error handling to the Explore Names form and name detail page; introduces interpretation utilities and display formatting; tightens downstream prop typings to require normalized names; and switches several breadcrumb links to use next/link with asChild.

Changes

Cohort / File(s) Summary
Changeset
/.changeset/spicy-gifts-say.md
Adds patch-level changeset documenting the UI and validation changes for ensadmin.
Name interpretation & formatting
apps/ensadmin/src/lib/interpret-name-from-user-input.ts, apps/ensadmin/src/lib/interpret-name-from-user-input.test.ts, apps/ensadmin/src/lib/format-interpreted-name-for-display.ts
New interpreter that classifies inputs as Empty/Normalized/Reencoded/Encoded, comprehensive tests, and a formatter to produce display-ready names from interpreted inputs.
Explore Names page (form + validation + navigation)
apps/ensadmin/src/app/name/page.tsx
Form submit now uses interpretNameFromUserInput; navigates only for normalized results; inline formError handling for encoded/invalid inputs; trim-based empty-check and error display on edits.
Name detail rendering & typings
apps/ensadmin/src/app/name/_components/NameDetailPageContent.tsx, apps/ensadmin/src/app/name/_components/ProfileHeader.tsx
Prop types tightened from NameNormalizedName for downstream components.
Error components
apps/ensadmin/src/app/name/_components/NameErrors.tsx
New UnnormalizedNameError and InterpretedNameUnsupportedError stateless components rendering ErrorInfo blocks.
Actions & breadcrumbs (name)
apps/ensadmin/src/app/@actions/name/page.tsx, apps/ensadmin/src/app/@breadcrumbs/name/page.tsx
Query-param handling simplified (direct cast from searchParams); removed manual decodeURIComponent; breadcrumbs now render BreadcrumbLink asChild with next/link.
Breadcrumbs (mocks)
apps/ensadmin/src/app/@breadcrumbs/mock/.../page.tsx
Multiple mock pages updated to import next/link and wrap breadcrumb content in <Link> via BreadcrumbLink asChild.
Name display & ENS utilities
packages/namehash-ui/src/components/identity/Name.tsx, packages/namehash-ui/src/utils/ensManager.ts
NameDisplay validates via isInterpretedName and shows (invalid name) for bad props; getEnsManagerNameDetailsUrl now returns null for unnormalized names.
Tests removed
packages/ensnode-sdk/src/shared/interpretation/interpreted-names-and-labels.test.ts
Existing interpretation test suite deleted (coverage removed).

Sequence Diagram

sequenceDiagram
    participant User
    participant Browser
    participant Page as name/page.tsx
    participant Interpreter as interpretNameFromUserInput
    participant Validator as isInterpretedName / isNormalizedName
    participant Router as next/router
    participant Errors as NameErrors
    participant Detail as NameDetailPageContent

    User->>Browser: Open /name?name=rawQuery
    Browser->>Page: page loads (reads searchParams)
    Page->>Validator: validate `name`
    alt not isInterpretedName
        Page->>Errors: render UnnormalizedNameError
    else isInterpretedName and not isNormalizedName
        Page->>Errors: render InterpretedNameUnsupportedError
    else isNormalizedName
        Page->>Detail: render NameDetailPageContent (NormalizedName)
    end

    User->>Browser: Submit Explore Names form (rawInput)
    Browser->>Page: handleSubmit -> Interpreter(rawInput)
    alt outcome == Normalized
        Page->>Router: router.replace(/name?name=normalized)
    else outcome == Empty
        Page->>Page: do nothing (no navigation)
    else outcome == Reencoded or Encoded
        Page->>Page: set formError (show inline error)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

ensnode-sdk

Poem

🐰 I hopped through dots and hashes with delight,

I nudged raw input into tidy, normalized sight,
When names were odd I flagged them with care,
Redirected the neat ones to their rightful lair,
A rabbit cheers — inputs now land just right!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.27% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: validation of name query parameters on the name details page.
Description check ✅ Passed The description follows the template with clear Summary, Why, Testing, and Notes sections; all required information is present and well-documented.
Linked Issues check ✅ Passed The PR fully addresses requirements from both #1140 and #1059: implements form validation with normalization, disables button on empty input, shows errors for invalid names, redirects to normalized names, and validates name details page with proper error cards.
Out of Scope Changes check ✅ Passed All changes are scoped to the linked objectives: name validation, error rendering, type refinements, breadcrumb updates, and removal of unnecessary decodeURIComponent calls are all directly related.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch notrab/normalized-name-state

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…malizedName before rendering the name details page, displaying appropriate error states for invalid or unsupported names.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds validation for the name query param in ENSAdmin’s name details flow, ensuring only supported ENS name formats reach the details components and providing clearer user-facing error states for invalid/unsupported names.

Changes:

  • Validate name query param with isInterpretedName + isNormalizedName and show dedicated error cards for invalid names / encoded labelhash names.
  • Tighten downstream component prop types from Name to NormalizedName for the details content/header path.
  • Remove redundant decodeURIComponent usage in breadcrumbs/actions pages and guard NameDisplay from invalid inputs.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
apps/ensadmin/src/app/name/page.tsx Adds query param validation and routes to errors vs. details content.
apps/ensadmin/src/app/name/_components/NameDetailPageContent.tsx Tightens prop type to NormalizedName for safer downstream record fetching.
apps/ensadmin/src/app/name/_components/ProfileHeader.tsx Tightens prop type to NormalizedName to match validated inputs.
apps/ensadmin/src/app/name/_components/NameErrors.tsx Adds new UI components for invalid/unsupported name error states.
apps/ensadmin/src/app/@breadcrumbs/name/page.tsx Removes redundant decoding and avoids calling NameDisplay for invalid names.
apps/ensadmin/src/app/@actions/name/page.tsx Removes redundant decoding and suppresses ENS App link unless name is normalized.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 26, 2026

Greptile Summary

This PR adds input validation to the ENS name details page in ensadmin, preventing arbitrary strings from reaching the resolution APIs without first being checked for ENS validity. It introduces two user-facing error cards — one for names that fail isInterpretedName and one for names that pass it but contain encoded labelhashes not yet supported by the resolution layer — and tightens the prop types of NameDetailPageContent and ProfileHeader from Name to NormalizedName.\n\nKey changes:\n- name/page.tsx: Guards NameDetailPageContent behind sequential isInterpretedName / isNormalizedName checks; invalid names get a clear error UI instead of a confusing failure.\n- NameErrors.tsx (new): Two lightweight error components (InvalidNameError, EncodedLabelhashUnsupportedError) that wrap the existing ErrorInfo card.\n- @breadcrumbs/name/page.tsx & @actions/name/page.tsx: Removes the now-redundant decodeURIComponent calls (Next.js searchParams.get() already decodes); breadcrumbs fall back to plain text for non-interpreted names; the "View in ENS App" button is hidden for encoded-labelhash names.\n- NameDetailPageContent.tsx & ProfileHeader.tsx: Prop type narrowed to NormalizedName, improving downstream type safety.\n\nThe validation ordering is semantically correct for all documented test cases. One minor nuance: a name with a bracket-prefixed label that is too short to be a valid encoded labelhash (e.g. [abc].eth) will fall through isInterpretedName and display the "not ENS normalized" error, which is slightly inaccurate for that edge case.

Confidence Score: 5/5

Safe to merge — validation logic is correct for all documented inputs and no regressions are introduced.

The core validation logic is sound: the two type guards are applied in the right order, the type narrowing flows correctly through to NameDetailPageContent, decodeURIComponent removal is correct (Next.js already decodes search params), and ErrorInfo renders descriptions as React children so there is no XSS risk. The only finding is a P2 style note about a mildly misleading error message for a malformed-labelhash edge case that isn't part of any documented user path.

No files require special attention; apps/ensadmin/src/app/name/page.tsx has the only non-blocking note.

Important Files Changed

Filename Overview
apps/ensadmin/src/app/name/page.tsx Adds validation guards with isInterpretedName / isNormalizedName before rendering NameDetailPageContent; correctly routes to two new error components. Minor: error description for non-interpreted names can be misleading for malformed labelhash inputs.
apps/ensadmin/src/app/name/_components/NameErrors.tsx New file exporting InvalidNameError and EncodedLabelhashUnsupportedError components; both use ErrorInfo which renders description as React children (no XSS risk). Clean and straightforward.
apps/ensadmin/src/app/@breadcrumbs/name/page.tsx Removes redundant decodeURIComponent (correct, searchParams.get() already decodes); falls back to rendering raw name text when isInterpretedName is false, which is the right UX for the breadcrumb.
apps/ensadmin/src/app/@actions/name/page.tsx Removes redundant decodeURIComponent and guards getEnsManagerNameDetailsUrl behind isNormalizedName; correctly hides the "View in ENS App" button for encoded-labelhash names.
apps/ensadmin/src/app/name/_components/NameDetailPageContent.tsx Tightens prop type from Name to NormalizedName; no behavioural change, improves type safety downstream.
apps/ensadmin/src/app/name/_components/ProfileHeader.tsx Tightens prop type from Name to NormalizedName; consistent with the NameDetailPageContent change, no functional impact.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["User navigates to /name?name=X"] --> B{nameFromQuery\nnon-empty?}
    B -- No --> C["Show search / example names UI"]
    B -- Yes --> D{isInterpretedName\nnameFromQuery?}
    D -- No --> E["InvalidNameError\n'not ENS normalized'"]
    D -- Yes --> F{isNormalizedName\nnameFromQuery?}
    F -- No --> G["EncodedLabelhashUnsupportedError\n'coming soon'"]
    F -- Yes --> H["NameDetailPageContent\nname: NormalizedName"]
    H --> I["ProfileHeader + records"]
Loading

Reviews (1): Last reviewed commit: "docs(changeset): Validate name query par..." | Re-trigger Greptile

@vercel vercel bot temporarily deployed to Preview – ensnode.io March 26, 2026 08:26 Inactive
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io March 26, 2026 08:26 Inactive
Copilot AI review requested due to automatic review settings March 26, 2026 08:26
@vercel vercel bot temporarily deployed to Preview – ensrainbow.io March 26, 2026 08:26 Inactive
@vercel vercel bot temporarily deployed to Preview – ensnode.io March 26, 2026 08:26 Inactive
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Copy Markdown
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@notrab Good to see this 👍 Reviewed and shared a few suggestions. Please feel welcome to merge when ready!

@@ -13,11 +13,14 @@ export default function ActionsNamePage() {
const searchParams = useSearchParams();
const nameParam = searchParams.get("name");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's please move the typing for things to where they originate. Ex: If this is a Name and not an arbitrary string, then we should apply that typing here.

Please apply this idea everywhere it is relevant.

Note how the reinterpretation of this value as a Name is happening several lines below. I don't like that.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I've changed this to const nameFromQuery = searchParams.get("name") as Name | null which is really the only other option here as far as I can tell.

const ensAppProfileUrl = name && namespace ? getEnsManagerNameDetailsUrl(name, namespace) : null;
const ensAppProfileUrl =
name && isNormalizedName(name) && namespace
? getEnsManagerNameDetailsUrl(name, namespace)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any special reason not to move the isNormalizedName check into the implementation of getEnsManagerNameDetailsUrl?

Please ignore if not relevant.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getEnsManagerNameDetailsUrl lives in the namehash-ui package. isNormalizedName is the responsibility of the actions here. Maybe we should not link at all if it's not normalized.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why you mention how getEnsManagerNameDetailsUrl lives in the namehash-ui package? How is that relevant to the ideas here?

If we say that we don't want to link to the ENS Manager Name Details page for unnormalized names, then that decision should move into getEnsManagerNameDetailsUrl so that we never need to talk about it again.

Otherwise we have to keep endlessly talking about the implementation details of unnormalized name handling which I really hate as I've been doing that over and over again for 4 years.

type NormalizedName,
} from "@ensnode/ensnode-sdk";

export const NameInterpretationOutcomeResult = {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export const NameInterpretationOutcomeResult = {
// Represents the possible outcome of interpreting a Name from a raw user input
export const NameInterpretationOutcomeResult = {

| NameInterpretationOutcomeReencoded
| NameInterpretationOutcomeEncoded;

export function interpretNameFromUserInput(inputName: Name): NameInterpretationOutcome {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to create a JSDoc here that outlines....

Interprets a Name from raw user input into an InterpretedName

This function always succeeds with every input, producing an InterpretedName, and the outcome discrimninant tells the caller what happened so it can decide how to handle the result. Redirect, show error.

Processing logic tackled is

  • Empty labels encoded as labelhash > encoded
  • normalized labels as normalized
  • encoded labelhash labesl as lowercased > reencoded
  • unnormalizable labels as encoded as labelhash > encoded

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

packages/ensnode-sdk/src/shared/interpretation/interpreted-names-and-labels.test.ts:1

  • This change removes the entire interpreted-names-and-labels.test.ts suite without adding equivalent coverage elsewhere. That drops test coverage for core interpretation helpers (e.g., literalLabelsToInterpretedName, parsePartialInterpretedName, etc.) and increases regression risk; please restore these tests or move them so they still run in CI.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

export function UnnormalizedNameError() {
return (
<section className="p-6">
<ErrorInfo title="Invalid Name" description="The provided name is not a valid ENS name." />
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UnnormalizedNameError is shown for any name that is neither a NormalizedName nor an InterpretedName, which includes names that are valid and normalizable but simply not normalized (e.g. VITALIK.ETH). In that case, the description "not a valid ENS name" is inaccurate/misleading. Consider distinguishing "valid but not normalized" (e.g., normalize(name) succeeds but differs) from "invalid" (normalization throws) and adjusting the message accordingly.

Suggested change
<ErrorInfo title="Invalid Name" description="The provided name is not a valid ENS name." />
<ErrorInfo
title="Unnormalized Name"
description="The provided name is not in normalized ENS form. Please use a normalized ENS name (for example, lowercase and properly formatted)."
/>

Copilot uses AI. Check for mistakes.
Empty: "Empty",
/** All labels were normalized successfully.*/
Normalized: "Normalized",
/** One or moer labels were already formatted as encoded labelhashes. None were unnormalizable. */
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in doc comment: "moer" should be "more".

Suggested change
/** One or moer labels were already formatted as encoded labelhashes. None were unnormalizable. */
/** One or more labels were already formatted as encoded labelhashes. None were unnormalizable. */

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +95
switch (result.outcome) {
case NameInterpretationOutcomeResult.Empty:
break;
case NameInterpretationOutcomeResult.Normalized: {
const href = retainCurrentRawConnectionUrlParam(
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whitespace-only input is treated as Empty (no-op) and the submit button is disabled based on rawInputName.trim(). That means an input like " " can't be submitted via the button and won't surface an "Invalid Name" error, which conflicts with issue #1140's acceptance criteria (it explicitly lists " " as an Invalid Name case). Consider disabling only for the truly empty string, and returning an invalid-name outcome (with an inline error) for whitespace-only input.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

return <InterpretedNameUnsupportedError />;
}

return <UnnormalizedNameError />;
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The detail-page query-param validation currently treats a valid-but-unnormalized name (e.g. "VITALIK.ETH") the same as an invalid name and renders UnnormalizedNameError (with a "not a valid ENS name" message). This doesn’t match the intended behavior in #1140/#1059, where unnormalized-but-normalizable names should show an explicit "not ENS normalized" error distinct from truly invalid input. Consider adding a separate branch to detect “normalizable but not normalized” (e.g. via normalize(nameFromQuery) in a try/catch) and render a dedicated error state/message for that case.

Suggested change
return <UnnormalizedNameError />;
// Distinguish between names that are normalizable-but-unnormalized and
// inputs that are truly invalid.
const interpretationResult = interpretNameFromUserInput(nameFromQuery);
if (interpretationResult.outcome === NameInterpretationOutcomeResult.Normalized) {
// The name can be normalized but isn't in normalized form yet.
// Show a dedicated "not ENS normalized" error state.
return <UnnormalizedNameError />;
}
// For all other outcomes, treat the input as invalid and show a
// "not a valid ENS name" message.
return (
<section className="flex flex-col gap-6 p-6">
<Card className="w-full">
<CardHeader className="sm:pb-4 max-sm:p-3">
<CardTitle className="text-2xl">Explore ENS Names</CardTitle>
</CardHeader>
<CardContent className="max-sm:px-3 max-sm:pb-3">
<p className="text-sm text-red-600">
The provided input is not a valid ENS name.
</p>
</CardContent>
</Card>
</section>
);

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +102
/**
* Formats an InterpretedName for display in UI strings.
*
* - Normalized labels are beautified (via ens_beautify).
* - Non-normalized labels (encoded labelhashes) are lowercased for consistent display.
*
* NOTE: This function only takes an InterpretedName, not a raw Name.
* The return type is Name (not InterpretedName) because beautification
* may produce labels that are not normalized.
*
* @param interpretedName - The InterpretedName to format for display.
* @returns The formatted name.
*/
export const formatInterpretedNameForDisplay = (interpretedName: InterpretedName): Name => {
const displayLabels = interpretedName.split(".").map((label: Label) => {
if (isNormalizedLabel(label)) {
return ens_beautify(label);
} else {
return label.toLowerCase();
}
});
return displayLabels.join(".");
};
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatInterpretedNameForDisplay is newly introduced behavior (beautify normalized labels + lowercase encoded labelhash labels) but there are no unit tests covering it. Since this package already has names.test.ts for related helpers, please add tests for at least: (1) an InterpretedName containing an encoded labelhash with uppercase hex lowercases as expected, and (2) an InterpretedName containing normalized emoji labels is beautified. Also, this PR removes shared/interpretation/interpreted-names-and-labels.test.ts without an apparent replacement; if that deletion is unintentional, consider restoring/migrating those tests to avoid coverage regression for isInterpretedName/parsing helpers that are now used in UI validation.

Copilot uses AI. Check for mistakes.
}
});
return displayLabels.join(".");
};
Copy link
Copy Markdown
Contributor

@vercel vercel bot Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit tests have been successfully added for the formatInterpretedNameForDisplay function

Fix on Vercel

return <InterpretedNameUnsupportedError />;
}

return <UnnormalizedNameError />;
Copy link
Copy Markdown
Contributor

@vercel vercel bot Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Query parameter validation doesn't distinguish between unnormalized-but-normalizable names and truly invalid names, both showing the same generic "Invalid Name" error

Fix on Vercel

@notrab notrab marked this pull request as draft March 31, 2026 17:10
Copilot AI review requested due to automatic review settings March 31, 2026 17:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +58 to +72
export function interpretNameFromUserInput(inputName: Name): NameInterpretationOutcome {
if (inputName.trim() === "") {
return {
outcome: NameInterpretationOutcomeResult.Empty,
inputName,
interpretation: "" as InterpretedName,
};
}

let hadEmptyLabels = false;
let hadReencodedLabels = false;
let hadUnnormalizableLabels = false;

const interpretedLabels = inputName.split(".").map((label) => {
if (label === "") {
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interpretNameFromUserInput only uses trim() to detect the empty-string case, but then interprets the original untrimmed input. This means inputs like " vitalik.eth " (button enabled due to .trim()) will be treated as unnormalizable and encoded, producing an error instead of normalizing. Consider trimming once up-front (e.g., const trimmed = inputName.trim()) and using that for splitting/normalization while still returning the original inputName for display if desired.

Copilot uses AI. Check for mistakes.
const router = useRouter();
const searchParams = useSearchParams();
const nameFromQuery = searchParams.get("name");
const nameFromQuery = searchParams.get("name") as Name | null;
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nameFromQuery is validated without trimming, so URLs like /name?name=vitalik.eth%20 (or whitespace-only values) will be treated as invalid and show an error. In this same app, the breadcrumbs page trims the query param before using it. Consider applying .trim() (and treating the empty result as null) before the isNormalizedName / isInterpretedName checks.

Suggested change
const nameFromQuery = searchParams.get("name") as Name | null;
const rawNameFromQuery = searchParams.get("name");
const nameFromQuery =
rawNameFromQuery && rawNameFromQuery.trim() !== ""
? (rawNameFromQuery.trim() as Name)
: null;

Copilot uses AI. Check for mistakes.
Comment on lines +93 to +101
export const formatInterpretedNameForDisplay = (interpretedName: InterpretedName): Name => {
const displayLabels = interpretedName.split(".").map((label: Label) => {
if (isNormalizedLabel(label)) {
return ens_beautify(label);
} else {
return label.toLowerCase();
}
});
return displayLabels.join(".");
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

formatInterpretedNameForDisplay introduces new formatting behavior (beautify normalized labels + lowercase encoded labelhash labels), but there are no tests covering it. Since this module already has names.test.ts, consider adding test cases for mixed normalized/encoded-labelhash names and for ensuring encoded labelhash hex is lowercased.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement input validation and normalization on "Explore Names" form Refine Name Details Page Route Handling

3 participants