Skip to content

Commit 724775e

Browse files
S410lidel
S410
andauthored
fix: IP support on opt-in and opt-out lists (#945)
Co-authored-by: Marcin Rataj <[email protected]>
1 parent 2ee6b90 commit 724775e

File tree

5 files changed

+49
-16
lines changed

5 files changed

+49
-16
lines changed

add-on/src/lib/options.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict'
22

3+
const isIP = require('is-ip')
34
const isFQDN = require('is-fqdn')
45
const { hasChromeSocketsForTcp } = require('./runtime-checks')
56

@@ -117,11 +118,23 @@ function guiURLString (url, opts) {
117118
exports.safeURL = safeURL
118119
exports.guiURLString = guiURLString
119120

121+
// ensure value is a valid URL.hostname (FQDN || ipv4 || ipv6 WITH brackets)
122+
exports.isHostname = x => isFQDN(x) || isIP.v4(x) || (!isIP.v6(x) && isIP.v6(x.replace(/[[\]]+/g, '')))
123+
120124
// convert JS array to multiline textarea
121125
function hostArrayCleanup (array) {
122126
array = array.map(host => host.trim().toLowerCase())
127+
// normalize/extract hostnames (just domain/ip, drop ports etc), if provided
128+
array = array.map(x => {
129+
try {
130+
if (isIP.v6(x)) x = `[${x}]`
131+
return new URL(`http://${x}`).hostname
132+
} catch (_) {
133+
return undefined
134+
}
135+
})
136+
array = array.filter(Boolean).filter(exports.isHostname)
123137
array = [...new Set(array)] // dedup
124-
array = array.filter(Boolean).filter(isFQDN)
125138
array.sort()
126139
return array
127140
}

add-on/src/lib/state.js

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
'use strict'
22
/* eslint-env browser, webextensions */
33

4-
const isFQDN = require('is-fqdn')
5-
const { safeURL } = require('./options')
4+
const { safeURL, isHostname } = require('./options')
65
const { braveJsIpfsWebuiCid } = require('./precache')
76
const offlinePeerCount = -1
87

@@ -32,11 +31,11 @@ function initState (options, overrides) {
3231
state.activeIntegrations = (url) => {
3332
if (!state.active) return false
3433
try {
35-
const fqdn = isFQDN(url) ? url : new URL(url).hostname
34+
const hostname = isHostname(url) ? url : new URL(url).hostname
3635
// opt-out has more weight, we also match parent domains
37-
const disabledDirectlyOrIndirectly = state.disabledOn.some(host => fqdn.endsWith(host))
36+
const disabledDirectlyOrIndirectly = state.disabledOn.some(optout => hostname.endsWith(optout))
3837
// ..however direct opt-in should overwrite parent's opt-out
39-
const enabledDirectly = state.enabledOn.some(host => host === fqdn)
38+
const enabledDirectly = state.enabledOn.some(optin => optin === hostname)
4039
return !(disabledDirectlyOrIndirectly && !enabledDirectly)
4140
} catch (_) {
4241
return false

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
"ipfs-http-client": "48.0.0",
140140
"ipfs-postmsg-proxy": "3.1.1",
141141
"is-fqdn": "2.0.1",
142+
"is-ip": "3.1.0",
142143
"is-ipfs": "2.0.0",
143144
"it-all": "1.0.4",
144145
"it-concat": "1.0.2",

test/functional/lib/options.test.js

+23-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ const { describe, it, beforeEach, after } = require('mocha')
33
const { expect } = require('chai')
44
const sinon = require('sinon')
55
const browser = require('sinon-chrome')
6-
const { storeMissingOptions, optionDefaults, hostTextToArray, hostArrayToText } = require('../../../add-on/src/lib/options')
6+
const { storeMissingOptions, optionDefaults, isHostname, hostTextToArray, hostArrayToText } = require('../../../add-on/src/lib/options')
7+
const { URL } = require('url')
78

89
describe('storeMissingOptions()', function () {
910
beforeEach(() => {
11+
global.URL = URL
1012
browser.flush()
1113
})
1214

@@ -59,10 +61,28 @@ describe('storeMissingOptions()', function () {
5961
})
6062
})
6163

64+
describe('isHostname()', function () {
65+
it('should return false for invalid URL.hostname', () => {
66+
expect(isHostname('random text with whitespaces')).to.equal(false)
67+
})
68+
it('should return true for a valid URL.hostname (FQDN)', () => {
69+
expect(isHostname('example.com')).to.equal(true)
70+
})
71+
it('should return true for a valid URL.hostname (ipv4)', () => {
72+
expect(isHostname('192.168.1.1')).to.equal(true)
73+
})
74+
it('should return true for valid URL.hostname (ipv6 in brackets)', () => {
75+
expect(isHostname('[fe80::bb67:770c:8a97:1]')).to.equal(true)
76+
})
77+
it('should return false for invalid URL.hostname (ipv6 without brackets)', () => {
78+
expect(isHostname('fe80::bb67:770c:8a97:1')).to.equal(false)
79+
})
80+
})
81+
6282
describe('hostTextToArray()', function () {
6383
it('should sort, dedup hostnames, drop non-FQDNs and produce an array', () => {
64-
const text = 'zombo.com\n two.com \n totally not a FQDN \none.pl \nTWO.com\n\n'
65-
const array = ['one.pl', 'two.com', 'zombo.com']
84+
const text = 'zombo.com\n TwO.com \n 192.168.1.1:58080 \n192.168.1.2\n[fe80::bb67:770c:8a97:1]:58080\nfe80::bb67:770c:8a97:2\n[fe80::bb67:770c:8a97:3]\n totally not a FQDN \none.pl \nTWO.com\n\n'
85+
const array = ['192.168.1.1', '192.168.1.2', '[fe80::bb67:770c:8a97:1]', '[fe80::bb67:770c:8a97:2]', '[fe80::bb67:770c:8a97:3]', 'one.pl', 'two.com', 'zombo.com']
6686
expect(hostTextToArray(text)).to.be.an('array').to.have.ordered.members(array)
6787
})
6888
})

yarn.lock

+7-7
Original file line numberDiff line numberDiff line change
@@ -8081,20 +8081,20 @@ is-interactive@^1.0.0:
80818081
resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
80828082
integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
80838083

8084+
[email protected], is-ip@^3.1.0:
8085+
version "3.1.0"
8086+
resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8"
8087+
integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==
8088+
dependencies:
8089+
ip-regex "^4.0.0"
8090+
80848091
is-ip@^2.0.0:
80858092
version "2.0.0"
80868093
resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-2.0.0.tgz#68eea07e8a0a0a94c2d080dd674c731ab2a461ab"
80878094
integrity sha1-aO6gfooKCpTC0IDdZ0xzGrKkYas=
80888095
dependencies:
80898096
ip-regex "^2.0.0"
80908097

8091-
is-ip@^3.1.0:
8092-
version "3.1.0"
8093-
resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8"
8094-
integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==
8095-
dependencies:
8096-
ip-regex "^4.0.0"
8097-
80988098
[email protected], is-ipfs@^2.0.0:
80998099
version "2.0.0"
81008100
resolved "https://registry.yarnpkg.com/is-ipfs/-/is-ipfs-2.0.0.tgz#c046622e4daf5435b671aeb9739a832107e06805"

0 commit comments

Comments
 (0)