Skip to content

Commit ec99444

Browse files
refactor: replace validateURL with isValidTokenListSource for URL validation
1 parent 71e9bef commit ec99444

7 files changed

Lines changed: 39 additions & 47 deletions

File tree

apps/cowswap-frontend/src/modules/tokensList/containers/ManageListsAndTokens/index.tsx

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ReactNode, useMemo, useState } from 'react'
22

33
import { TokenWithLogo } from '@cowprotocol/common-const'
4-
import { isAddress, parseENSAddress, uriToHttp } from '@cowprotocol/common-utils'
4+
import { isAddress, isValidTokenListSource } from '@cowprotocol/common-utils'
55
import { ListState, useSearchList, useSearchToken } from '@cowprotocol/tokens'
66
import { ModalHeader } from '@cowprotocol/ui'
77

@@ -44,14 +44,7 @@ export function ManageListsAndTokens(props: ManageListsAndTokensProps): ReactNod
4444
const isListUrlValid = useMemo(() => {
4545
if (!listInput) return false
4646

47-
const value = listInput.trim()
48-
49-
if (parseENSAddress(value)) return true
50-
51-
const protocol = value.split(':')[0]?.toLowerCase()
52-
if (protocol !== 'http' && protocol !== 'https') return false
53-
54-
return uriToHttp(value).length > 0
47+
return isValidTokenListSource(listInput)
5548
}, [listInput])
5649

5750
const tokenSearchResponse = useSearchToken(isTokenAddressValid ? tokenInput : null)

apps/widget-configurator/src/app/configurator/controls/AddCustomListDialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { ReactNode, useEffect, useRef, useState } from 'react'
22

3+
import { isValidTokenListSource } from '@cowprotocol/common-utils'
34
import { Command, TokenInfo } from '@cowprotocol/types'
45

56
import {
@@ -17,7 +18,6 @@ import Tabs from '@mui/material/Tabs'
1718

1819
import { DEFAULT_CUSTOM_TOKENS } from '../consts'
1920
import { parseCustomTokensInput } from '../utils/parseCustomTokensInput'
20-
import { validateURL } from '../utils/validateURL'
2121

2222
const jsonTextAreaStyles = {
2323
fontFamily: 'monospace',
@@ -80,7 +80,7 @@ export function AddCustomListDialog({
8080

8181
setCustomListUrl(value)
8282

83-
setHasErrors(value ? !validateURL(value) : false)
83+
setHasErrors(value ? !isValidTokenListSource(value) : false)
8484
}
8585

8686
// TODO: Add proper return type annotation

apps/widget-configurator/src/app/configurator/utils/validateURL.test.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

apps/widget-configurator/src/app/configurator/utils/validateURL.ts

Lines changed: 0 additions & 12 deletions
This file was deleted.

libs/common-utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export * from './isInjectedWidget'
3636
export * from './isIframe'
3737
export * from './isSellOrder'
3838
export * from './isSupportedChainId'
39+
export * from './isValidTokenListSource'
3940
export * from './isZero'
4041
export * from './jotai/atomWithPartialUpdate'
4142
export * from './legacyAddressUtils'
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { isValidTokenListSource } from './isValidTokenListSource'
2+
3+
describe('isValidTokenListSource', () => {
4+
it('accepts http and https URLs', () => {
5+
expect(isValidTokenListSource('http://example.com/list.json')).toBe(true)
6+
expect(isValidTokenListSource('https://example.com/list.json')).toBe(true)
7+
})
8+
9+
it('accepts uriToHttp-supported protocols and ENS sources', () => {
10+
expect(isValidTokenListSource('data:application/json,{}')).toBe(true)
11+
expect(isValidTokenListSource('/@fs/etc/passwd')).toBe(true)
12+
expect(isValidTokenListSource('ipfs://QmHash')).toBe(true)
13+
expect(isValidTokenListSource('ipns://tokens.uniswap.org')).toBe(true)
14+
expect(isValidTokenListSource('ar://hash')).toBe(true)
15+
expect(isValidTokenListSource('tokens.uniswap.eth')).toBe(true)
16+
expect(isValidTokenListSource('tokens.uniswap.eth/list.json')).toBe(true)
17+
})
18+
19+
it('rejects unsafe or unsupported sources', () => {
20+
expect(isValidTokenListSource('')).toBe(false)
21+
expect(isValidTokenListSource(' https://example.com/list.json ')).toBe(false)
22+
expect(isValidTokenListSource('\tipfs://QmHash\n')).toBe(false)
23+
expect(isValidTokenListSource('ftp://example.com/list.json')).toBe(false)
24+
expect(isValidTokenListSource('file:///tmp/list.json')).toBe(false)
25+
expect(isValidTokenListSource('javascript:alert(1)')).toBe(false)
26+
expect(isValidTokenListSource('blob:https://example.com/id')).toBe(false)
27+
})
28+
})
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { parseENSAddress } from './parseENSAddress'
2+
import { uriToHttp } from './uriToHttp'
3+
4+
export function isValidTokenListSource(source: string): boolean {
5+
return uriToHttp(source).length > 0 || Boolean(parseENSAddress(source))
6+
}

0 commit comments

Comments
 (0)