Skip to content

Commit 32037d4

Browse files
authored
Move ip validation (#1949)
* Move IP validation to util file; add test * Test was simpler than expected, so no need to set up extra functions * Refactor tests
1 parent f02678b commit 32037d4

File tree

3 files changed

+32
-46
lines changed

3 files changed

+32
-46
lines changed

app/forms/ip-pool-range-add.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { useNavigate } from 'react-router-dom'
99

1010
import { useApiMutation, useApiQueryClient, type IpRange } from '@oxide/api'
1111
import { Message } from '@oxide/ui'
12-
import { IPV4_REGEX, IPV6_REGEX } from '@oxide/util'
12+
import { validateIp } from '@oxide/util'
1313

1414
import { SideModalForm, TextField } from 'app/components/form'
1515
import { useForm, useIpPoolSelector } from 'app/hooks'
@@ -21,12 +21,6 @@ const defaultValues: IpRange = {
2121
last: '',
2222
}
2323

24-
function validateIp(s: string) {
25-
const isv4 = IPV4_REGEX.test(s)
26-
const isv6 = !isv4 && IPV6_REGEX.test(s)
27-
return { isv4, isv6, valid: isv4 || isv6 }
28-
}
29-
3024
const invalidAddressError = { type: 'pattern', message: 'Not a valid IP address' }
3125

3226
const diffVersionError = {

libs/util/str.spec.ts

Lines changed: 23 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,7 @@
77
*/
88
import { describe, expect, it, test } from 'vitest'
99

10-
import {
11-
camelCase,
12-
capitalize,
13-
commaSeries,
14-
IPV4_REGEX,
15-
IPV6_REGEX,
16-
kebabCase,
17-
titleCase,
18-
} from './str'
10+
import { camelCase, capitalize, commaSeries, kebabCase, titleCase, validateIp } from './str'
1911

2012
describe('capitalize', () => {
2113
it('capitalizes the first letter', () => {
@@ -88,29 +80,12 @@ describe('titleCase', () => {
8880
// Rust playground comparing results with std::net::{Ipv4Addr, Ipv6Addr}
8981
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=493b3345b9f6c0b1c8ee91834e99ef7b
9082

91-
test.each(['123.4.56.7', '1.2.3.4'])('ipv4Regex passes: %s', (s) => {
92-
expect(IPV4_REGEX.test(s)).toBe(true)
93-
})
94-
95-
test.each([
96-
'',
97-
'1',
98-
'abc',
99-
'a.b.c.d',
100-
// some implementations (I think incorrectly) allow leading zeros but nexus does not
101-
'01.102.103.104',
102-
'::ffff:192.0.2.128',
103-
'127.0.0',
104-
'127.0.0.1.',
105-
'127.0.0.1 ',
106-
' 127.0.0.1',
107-
'10002.3.4',
108-
'1.2.3.4.5',
109-
'256.0.0.0',
110-
'260.0.0.0',
111-
])('ipv4Regex fails: %s', (s) => {
112-
expect(IPV4_REGEX.test(s)).toBe(false)
113-
})
83+
test.each(['123.4.56.7', '1.2.3.4'])(
84+
'validateIp catches valid IPV4 / invalid IPV6: %s',
85+
(s) => {
86+
expect(validateIp(s)).toStrictEqual({ isv4: true, isv6: false, valid: true })
87+
}
88+
)
11489

11590
test.each([
11691
'2001:db8:3333:4444:5555:6666:7777:8888',
@@ -129,15 +104,26 @@ test.each([
129104
'::ffff:255.255.255.255',
130105
'fe08::7:8',
131106
'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',
132-
])('ipv6Regex passes: %s', (s) => {
133-
expect(IPV6_REGEX.test(s)).toBe(true)
107+
])('validateIp catches invalid IPV4 / valid IPV6: %s', (s) => {
108+
expect(validateIp(s)).toStrictEqual({ isv4: false, isv6: true, valid: true })
134109
})
135110

136111
test.each([
137112
'',
138113
'1',
139114
'abc',
140-
'123.4.56.7',
115+
'a.b.c.d',
116+
// some implementations (I think incorrectly) allow leading zeros but nexus does not
117+
'01.102.103.104',
118+
'127.0.0',
119+
'127.0.0.1.',
120+
'127.0.0.1 ',
121+
' 127.0.0.1',
122+
'10002.3.4',
123+
'1.2.3.4.5',
124+
'256.0.0.0',
125+
'260.0.0.0',
126+
'256.1.1.1',
141127
'2001:0db8:85a3:0000:0000:8a2e:0370:7334 ',
142128
' 2001:db8::',
143129
'1:2:3:4:5:6:7:8:9',
@@ -151,6 +137,6 @@ test.each([
151137
'fe08::7:8%',
152138
'fe08::7:8i',
153139
'fe08::7:8interface',
154-
])('ipv6Regex fails: %s', (s) => {
155-
expect(IPV6_REGEX.test(s)).toBe(false)
140+
])('validateIp catches invalid IP: %s', (s) => {
141+
expect(validateIp(s)).toStrictEqual({ isv4: false, isv6: false, valid: false })
156142
})

libs/util/str.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,14 @@ export const titleCase = (text: string): string => {
5454
// but they didn't match results with std::new on simple test cases
5555
// https://github.com/fabian-hiller/valibot/blob/2554aea5/library/src/regex.ts#L43-L54
5656

57-
export const IPV4_REGEX =
57+
const IPV4_REGEX =
5858
/^(?:(?:[1-9]|1\d|2[0-4])?\d|25[0-5])(?:\.(?:(?:[1-9]|1\d|2[0-4])?\d|25[0-5])){3}$/u
5959

60-
export const IPV6_REGEX =
60+
const IPV6_REGEX =
6161
/^(?:(?:[\da-f]{1,4}:){7}[\da-f]{1,4}|(?:[\da-f]{1,4}:){1,7}:|(?:[\da-f]{1,4}:){1,6}:[\da-f]{1,4}|(?:[\da-f]{1,4}:){1,5}(?::[\da-f]{1,4}){1,2}|(?:[\da-f]{1,4}:){1,4}(?::[\da-f]{1,4}){1,3}|(?:[\da-f]{1,4}:){1,3}(?::[\da-f]{1,4}){1,4}|(?:[\da-f]{1,4}:){1,2}(?::[\da-f]{1,4}){1,5}|[\da-f]{1,4}:(?::[\da-f]{1,4}){1,6}|:(?:(?::[\da-f]{1,4}){1,7}|:)|fe80:(?::[\da-f]{0,4}){0,4}%[\da-z]+|::(?:f{4}(?::0{1,4})?:)?(?:(?:25[0-5]|(?:2[0-4]|1?\d)?\d)\.){3}(?:25[0-5]|(?:2[0-4]|1?\d)?\d)|(?:[\da-f]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1?\d)?\d)\.){3}(?:25[0-5]|(?:2[0-4]|1?\d)?\d))$/iu
62+
63+
export const validateIp = (ip: string) => {
64+
const isv4 = IPV4_REGEX.test(ip)
65+
const isv6 = !isv4 && IPV6_REGEX.test(ip)
66+
return { isv4, isv6, valid: isv4 || isv6 }
67+
}

0 commit comments

Comments
 (0)