Skip to content

refactor: replace validateURL with isValidTokenListSource for URL validation#7406

Merged
kernelwhisperer merged 2 commits into
cowprotocol:developfrom
Kropiunig:fix/validate-url-non-http-schemes
May 14, 2026
Merged

refactor: replace validateURL with isValidTokenListSource for URL validation#7406
kernelwhisperer merged 2 commits into
cowprotocol:developfrom
Kropiunig:fix/validate-url-non-http-schemes

Conversation

@Kropiunig
Copy link
Copy Markdown
Contributor

@Kropiunig Kropiunig commented Apr 27, 2026

Summary

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved token list validation logic with stricter URL and source format checking.
    • Enhanced error detection for invalid token list sources with better handling of edge cases like whitespace and unsupported protocols.
  • Tests

    • Added comprehensive test coverage for token list source validation.

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 27, 2026

@Kropiunig is attempting to deploy a commit to the cow Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 27, 2026

Walkthrough

Replace duplicated URL/ENS validation with a new shared isValidTokenListSource util; update imports/usages in frontend components and adjust validateURL to trim and enforce http/https scheme checks. Tests added/extended to cover accepted and rejected source formats.

Changes

Token-list source validation (single cohort)

Layer / File(s) Summary
Shared validator + export
libs/common-utils/src/isValidTokenListSource.ts, libs/common-utils/src/isValidTokenListSource.test.ts, libs/common-utils/src/index.ts
Add isValidTokenListSource(source: string): boolean which returns true when uriToHttp(source) yields a non-empty string or parseENSAddress(source) is truthy; add tests for accepted/rejected inputs; re-export from common-utils index.
Standalone validator refinement
apps/widget-configurator/src/app/configurator/utils/validateURL.ts, apps/widget-configurator/src/app/configurator/utils/validateURL.test.ts
validateURL now trims input, tries ENS parsing, enforces scheme (http/https) before accepting via uriToHttp; tests extended with explicit positive http(s) cases and negative cases (data:, ipfs://, ipns://, ar://, /@fs/...).
Frontend integrations / wiring
apps/widget-configurator/src/app/configurator/controls/AddCustomListDialog.tsx, apps/cowswap-frontend/src/modules/tokensList/containers/ManageListsAndTokens/index.tsx
Replace prior per-app validation calls (validateURL or uriToHttp+parseENSAddress) with isValidTokenListSource; update imports and component hasErrors logic accordingly.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

"I nibble at inputs, tidy every line,"
"Trim the whitespace, keep ENS and http fine."
"No data: tunnels, nor ipfs stray,"
"A clean list path hopping on its way." 🐇✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The description provides a clear summary of the changes but lacks testing instructions and verification steps as specified in the repository template. Add 'To Test' section with specific steps to verify token-list source validation works correctly for all supported schemes (http, https, ipfs, ipns, ar, data, /@fs/, and ENS names).
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main refactoring: replacing validateURL with isValidTokenListSource for URL validation across the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@Kropiunig Kropiunig force-pushed the fix/validate-url-non-http-schemes branch from 68e7d68 to 01530cc Compare April 27, 2026 17:07
@Kropiunig Kropiunig changed the base branch from main to develop April 27, 2026 17:07
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
apps/widget-configurator/src/app/configurator/utils/validateURL.test.ts (1)

11-24: Add coverage for the ENS path and trim() behavior.

The implementation also added two behaviors that aren't asserted here: (1) ENS acceptance via parseENSAddress (the early return true on line 6 of validateURL.ts) and (2) input trimming (line 4). Without tests, regressions in either branch would slip through silently — e.g., someone moving the trim() after the protocol check would not break any test, even though ' https://example.com ' would start failing.

🧪 Suggested additions
+  it('accepts ENS names', () => {
+    expect(validateURL('tokens.uniswap.eth')).toBe(true)
+    expect(validateURL('list.tokens.eth/path')).toBe(true)
+  })
+
+  it('trims surrounding whitespace before validating', () => {
+    expect(validateURL('  https://example.com/list.json  ')).toBe(true)
+    expect(validateURL('\thttps://example.com/list.json\n')).toBe(true)
+    expect(validateURL('   ')).toBe(false)
+  })
+
+  it('rejects empty input', () => {
+    expect(validateURL('')).toBe(false)
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/widget-configurator/src/app/configurator/utils/validateURL.test.ts`
around lines 11 - 24, Add tests to validateURL.test.ts to cover two missing
behaviors: ensure validateURL accepts ENS-like inputs by asserting
validateURL('example.eth') (or other ENS strings) returns true to exercise the
parseENSAddress early-return branch in validateURL, and ensure trimming is
applied by asserting that inputs with surrounding whitespace like '
https://example.com ' still return true to exercise the trim() behavior; update
or add expectations accordingly so both the ENS and trimmed-input branches are
covered.
apps/cowswap-frontend/src/modules/tokensList/containers/ManageListsAndTokens/index.tsx (1)

47-54: Consider extracting the shared URL validation into a utility.

Lines 47–54 now duplicate the exact logic in apps/widget-configurator/src/app/configurator/utils/validateURL.ts (trim → ENS-first → http/https allowlist → uriToHttp(...).length > 0). Keeping two copies in sync invites future drift the next time the rules change (e.g., another scheme is rejected/added). Consider promoting it to a shared helper (e.g., a validateListUrl/isValidListLocation export in @cowprotocol/common-utils) and consuming it from both call sites.

♻️ Sketch
// libs/common-utils/src/validateListLocation.ts
import { parseENSAddress } from './parseENSAddress'
import { uriToHttp } from './uriToHttp'

export function validateListLocation(input: string): boolean {
  const value = input.trim()
  if (parseENSAddress(value)) return true
  const protocol = value.split(':')[0]?.toLowerCase()
  if (protocol !== 'http' && protocol !== 'https') return false
  return uriToHttp(value).length > 0
}

Then in this file:

-    const value = listInput.trim()
-
-    if (parseENSAddress(value)) return true
-
-    const protocol = value.split(':')[0]?.toLowerCase()
-    if (protocol !== 'http' && protocol !== 'https') return false
-
-    return uriToHttp(value).length > 0
+    return validateListLocation(listInput)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/cowswap-frontend/src/modules/tokensList/containers/ManageListsAndTokens/index.tsx`
around lines 47 - 54, The URL validation logic in ManageListsAndTokens (trim →
parseENSAddress → protocol check → uriToHttp length) is duplicated; extract it
into a shared helper (e.g., validateListLocation or isValidListLocation) in the
common-utils library and export it, then replace the inline logic in
ManageListsAndTokens/index.tsx with a call to that helper; reference the
existing helpers parseENSAddress and uriToHttp when implementing the new
validateListLocation and update callers to import and use validateListLocation
instead of repeating the logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@apps/cowswap-frontend/src/modules/tokensList/containers/ManageListsAndTokens/index.tsx`:
- Around line 47-54: The URL validation logic in ManageListsAndTokens (trim →
parseENSAddress → protocol check → uriToHttp length) is duplicated; extract it
into a shared helper (e.g., validateListLocation or isValidListLocation) in the
common-utils library and export it, then replace the inline logic in
ManageListsAndTokens/index.tsx with a call to that helper; reference the
existing helpers parseENSAddress and uriToHttp when implementing the new
validateListLocation and update callers to import and use validateListLocation
instead of repeating the logic.

In `@apps/widget-configurator/src/app/configurator/utils/validateURL.test.ts`:
- Around line 11-24: Add tests to validateURL.test.ts to cover two missing
behaviors: ensure validateURL accepts ENS-like inputs by asserting
validateURL('example.eth') (or other ENS strings) returns true to exercise the
parseENSAddress early-return branch in validateURL, and ensure trimming is
applied by asserting that inputs with surrounding whitespace like '
https://example.com ' still return true to exercise the trim() behavior; update
or add expectations accordingly so both the ENS and trimmed-input branches are
covered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 04fcbcaa-7474-4b2c-a452-daeb715324a8

📥 Commits

Reviewing files that changed from the base of the PR and between 3ff9551 and 01530cc.

📒 Files selected for processing (3)
  • apps/cowswap-frontend/src/modules/tokensList/containers/ManageListsAndTokens/index.tsx
  • apps/widget-configurator/src/app/configurator/utils/validateURL.test.ts
  • apps/widget-configurator/src/app/configurator/utils/validateURL.ts

@kernelwhisperer
Copy link
Copy Markdown
Contributor

Thank you @Kropiunig, we'll take a look soon-ish :)

@kernelwhisperer kernelwhisperer force-pushed the fix/validate-url-non-http-schemes branch from 01530cc to eb79d4a Compare May 7, 2026 12:53
Copy link
Copy Markdown
Contributor

@kernelwhisperer kernelwhisperer left a comment

Choose a reason for hiding this comment

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

LGTM, but I need to test it.

@kernelwhisperer
Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 7, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 7, 2026

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

Project Deployment Actions Updated (UTC)
swap-dev Ready Ready Preview May 7, 2026 1:06pm
widget-configurator Ready Ready Preview May 7, 2026 1:06pm

Request Review

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/common-utils/src/isValidTokenListSource.ts`:
- Around line 4-6: The validator function isValidTokenListSource currently
accepts sources that map to non-HTTP schemes via uriToHttp; trim the input and
explicitly require the source's scheme to be http or https before relying on
uriToHttp or parseENSAddress. Update isValidTokenListSource to trim the source
string, check that it starts with 'http://' or 'https://' (case-insensitive) and
only then call uriToHttp(source) to validate, falling back to
Boolean(parseENSAddress(source)) for ENS inputs.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 2f93cb07-0a70-4ebc-b3ed-31412bcc112a

📥 Commits

Reviewing files that changed from the base of the PR and between eb79d4a and 9289b41.

📒 Files selected for processing (7)
  • apps/cowswap-frontend/src/modules/tokensList/containers/ManageListsAndTokens/index.tsx
  • apps/widget-configurator/src/app/configurator/controls/AddCustomListDialog.tsx
  • apps/widget-configurator/src/app/configurator/utils/validateURL.test.ts
  • apps/widget-configurator/src/app/configurator/utils/validateURL.ts
  • libs/common-utils/src/index.ts
  • libs/common-utils/src/isValidTokenListSource.test.ts
  • libs/common-utils/src/isValidTokenListSource.ts
💤 Files with no reviewable changes (2)
  • apps/widget-configurator/src/app/configurator/utils/validateURL.test.ts
  • apps/widget-configurator/src/app/configurator/utils/validateURL.ts
✅ Files skipped from review due to trivial changes (2)
  • libs/common-utils/src/index.ts
  • libs/common-utils/src/isValidTokenListSource.test.ts

Comment on lines +4 to +6
export function isValidTokenListSource(source: string): boolean {
return uriToHttp(source).length > 0 || Boolean(parseENSAddress(source))
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 8, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Protocol allowlist is still missing in the shared validator.

Line 5 still treats uriToHttp(source).length > 0 as valid, so non-HTTP schemes can continue to pass when uriToHttp returns mapped URLs. Please enforce http/https explicitly (and trim first), then call uriToHttp.

Suggested fix
 export function isValidTokenListSource(source: string): boolean {
-  return uriToHttp(source).length > 0 || Boolean(parseENSAddress(source))
+  const value = source.trim()
+  if (!value) return false
+
+  if (parseENSAddress(value)) return true
+
+  const protocol = value.split(':')[0]?.toLowerCase()
+  if (protocol !== 'http' && protocol !== 'https') return false
+
+  return uriToHttp(value).length > 0
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/common-utils/src/isValidTokenListSource.ts` around lines 4 - 6, The
validator function isValidTokenListSource currently accepts sources that map to
non-HTTP schemes via uriToHttp; trim the input and explicitly require the
source's scheme to be http or https before relying on uriToHttp or
parseENSAddress. Update isValidTokenListSource to trim the source string, check
that it starts with 'http://' or 'https://' (case-insensitive) and only then
call uriToHttp(source) to validate, falling back to
Boolean(parseENSAddress(source)) for ENS inputs.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why should we disallow non-http schemes?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@kernelwhisperer, that's a fair challenge — let me look at what uriToHttp does with those schemes before responding further.

@kernelwhisperer, that's a great challenge — my comment was overly restrictive for this specific context, and I think you're right to question it.

Here's the distinction I should have drawn more carefully:

validateURL (widget configurator) — a user-facing URL input field where enforcing http/https is a reasonable defence-in-depth measure, since there's no legitimate UX reason for a widget integrator to enter ipfs:// or data: directly.

isValidTokenListSource (shared utility) — a broader validator for token list sources, where non-HTTP schemes are entirely legitimate:

  • The Uniswap token list spec explicitly recommends pinning lists to IPFS and referencing them by an ENS name that resolves to the contenthash. So ipfs://, ipns://, and ar:// are expected inputs here.
  • uriToHttp already acts as a translation layer that converts ipfs://, ipns://, and ar:// into HTTP gateway URLs — so those schemes result in safe HTTP fetches regardless. If uriToHttp returns an empty array for a given scheme (e.g. javascript:, file:, blob:), the source is already rejected.
  • The test suite for isValidTokenListSource confirms this intent: it accepts ipfs://, ipns://, ar://, and rejects javascript:, file:, and blob: — the truly dangerous ones.

So the current implementation of isValidTokenListSource (uriToHttp(source).length > 0 || Boolean(parseENSAddress(source))) is correct for this use case. I was wrong to flag it as an issue — the safety boundary is appropriately enforced by uriToHttp itself, not by an explicit protocol allowlist. I'll withdraw this concern.


✏️ Learnings added
Learnt from: kernelwhisperer
Repo: cowprotocol/cowswap PR: 7406
File: libs/common-utils/src/isValidTokenListSource.ts:4-6
Timestamp: 2026-05-08T11:00:38.516Z
Learning: In the cowprotocol/cowswap repo, `isValidTokenListSource` in `libs/common-utils/src/isValidTokenListSource.ts` intentionally accepts non-HTTP schemes like `ipfs://`, `ipns://`, and `ar://` because `uriToHttp` converts them to safe HTTP gateway URLs. The Uniswap token list spec recommends IPFS as the preferred hosting. Rejecting non-HTTP schemes here would be incorrect; the protocol allowlist restriction applies only to the widget-configurator's user-facing `validateURL` helper.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Copy Markdown
Contributor

@kernelwhisperer kernelwhisperer left a comment

Choose a reason for hiding this comment

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

  • Correction : We do want to support these token-list sources (ipfs://, ipns://, ENS, data:, etc.). The confusion came from CodeRabbit interpreting validateURL as a generic HTTP(S)-only URL validator, but this input is actually a token-list source validator and the logic was copied from the swap app.

  • Pushed a commit updating this. I moved the shared behavior into isValidTokenListSource and removed the misleading widget-local validateURL wrapper.

  • I tested that t2crtokens.eth works and ipfs://hash doesn't error with Enter valid list location

@kernelwhisperer kernelwhisperer force-pushed the fix/validate-url-non-http-schemes branch from 9289b41 to ec99444 Compare May 8, 2026 10:51
@kernelwhisperer kernelwhisperer changed the title fix(widget-configurator): reject non-HTTP(S) schemes in validateURL refactor: replace validateURL with isValidTokenListSource for URL validation May 8, 2026
@kernelwhisperer kernelwhisperer requested a review from a team May 8, 2026 10:54
@kernelwhisperer kernelwhisperer force-pushed the fix/validate-url-non-http-schemes branch from ec99444 to c383c07 Compare May 14, 2026 08:02
@kernelwhisperer kernelwhisperer enabled auto-merge (squash) May 14, 2026 08:02
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/widget-configurator/src/app/configurator/controls/AddCustomListDialog.tsx (1)

149-162: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update helper text and input type to match broader validation scope.

The validation now accepts more than just URLs (ENS names, ipfs:, ipns:, ar:, data:, /@fs/ schemes), but:

  1. The helper text on line 159 still says "Enter a valid URL", which is misleading.
  2. The type="url" attribute on line 154 may trigger HTML5 browser validation that could reject valid ENS names or non-HTTP schemes.
🔧 Suggested fix
         <TextField
           error={hasErrors}
           margin="dense"
           id="url"
           label="List URL"
-          type="url"
+          type="text"
           fullWidth
           variant="outlined"
           value={customListUrl}
           onChange={handleUrlInputChange}
-          helperText={hasErrors && 'Enter a valid URL'}
+          helperText={hasErrors && 'Enter a valid token list source (URL or ENS name)'}
           required
           autoComplete="off"
         />
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/widget-configurator/src/app/configurator/controls/AddCustomListDialog.tsx`
around lines 149 - 162, The TextField currently enforces URL-only semantics:
update the input to accept broader list identifiers by removing or changing
type="url" to type="text" on the TextField used for customListUrl (the component
using value={customListUrl} and onChange={handleUrlInputChange}), and update the
helperText (currently showing 'Enter a valid URL' via hasErrors) to a neutral
message like 'Enter a valid list address (URL, ENS, IPFS, AR, data, or /@fs/)'
so validation and user guidance reflect supported schemes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@apps/widget-configurator/src/app/configurator/controls/AddCustomListDialog.tsx`:
- Around line 149-162: The TextField currently enforces URL-only semantics:
update the input to accept broader list identifiers by removing or changing
type="url" to type="text" on the TextField used for customListUrl (the component
using value={customListUrl} and onChange={handleUrlInputChange}), and update the
helperText (currently showing 'Enter a valid URL' via hasErrors) to a neutral
message like 'Enter a valid list address (URL, ENS, IPFS, AR, data, or /@fs/)'
so validation and user guidance reflect supported schemes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4af072c5-69cd-4eb3-b832-ec1fa670af0a

📥 Commits

Reviewing files that changed from the base of the PR and between 9289b41 and c383c07.

📒 Files selected for processing (7)
  • apps/cowswap-frontend/src/modules/tokensList/containers/ManageListsAndTokens/index.tsx
  • apps/widget-configurator/src/app/configurator/controls/AddCustomListDialog.tsx
  • apps/widget-configurator/src/app/configurator/utils/validateURL.test.ts
  • apps/widget-configurator/src/app/configurator/utils/validateURL.ts
  • libs/common-utils/src/index.ts
  • libs/common-utils/src/isValidTokenListSource.test.ts
  • libs/common-utils/src/isValidTokenListSource.ts
💤 Files with no reviewable changes (2)
  • apps/widget-configurator/src/app/configurator/utils/validateURL.ts
  • apps/widget-configurator/src/app/configurator/utils/validateURL.test.ts
✅ Files skipped from review due to trivial changes (1)
  • libs/common-utils/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • libs/common-utils/src/isValidTokenListSource.test.ts
  • libs/common-utils/src/isValidTokenListSource.ts
  • apps/cowswap-frontend/src/modules/tokensList/containers/ManageListsAndTokens/index.tsx

@kernelwhisperer kernelwhisperer merged commit 0892f73 into cowprotocol:develop May 14, 2026
11 of 19 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators May 14, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants