From bf3df2b707223d030a83881e6c1c99d57e487340 Mon Sep 17 00:00:00 2001 From: FelisCatus Date: Sun, 26 Feb 2017 00:32:37 -0500 Subject: [PATCH] Handle IPv6 address brackets correctly in bypass list. Fix #998. --- omega-pac/src/conditions.coffee | 50 ++++++++++++------- omega-pac/src/profiles.coffee | 2 +- omega-pac/test/conditions.coffee | 33 ++++++++++++ .../src/options.coffee | 8 ++- 4 files changed, 73 insertions(+), 20 deletions(-) diff --git a/omega-pac/src/conditions.coffee b/omega-pac/src/conditions.coffee index 23df3cb..8f45389 100644 --- a/omega-pac/src/conditions.coffee +++ b/omega-pac/src/conditions.coffee @@ -306,6 +306,7 @@ module.exports = exports = ip: null scheme: null url: null + normalizedPattern: '' server = condition.pattern if server == '' cache.host = server @@ -313,6 +314,7 @@ module.exports = exports = parts = server.split '://' if parts.length > 1 cache.scheme = parts[0] + cache.normalizedPattern = cache.scheme + '://' server = parts[1] parts = server.split '/' @@ -322,36 +324,43 @@ module.exports = exports = if addr and not isNaN(prefixLen) cache.ip = conditionType: 'IpCondition' - ip: parts[0] + ip: @normalizeIp addr prefixLength: prefixLen + cache.normalizedPattern += cache.ip.ip + '/' + cache.ip.prefixLength return cache - if server.charCodeAt(server.length - 1) != ']'.charCodeAt(0) + # The server can be an IP address with or without brackets. + serverIp = @parseIp(server) + if not serverIp? pos = server.lastIndexOf(':') if pos >= 0 matchPort = server.substring(pos + 1) server = server.substring(0, pos) - serverIp = @parseIp server - serverRegex = null + serverIp = @parseIp server if serverIp? - if serverIp.regularExpressionString? - regexStr = serverIp.regularExpressionString(true) - serverRegex = '\\[' + regexStr + '\\]' + server = @normalizeIp serverIp + if serverIp.v4 + cache.normalizedPattern += server else - server = @normalizeIp serverIp - else if server.charCodeAt(0) == '.'.charCodeAt(0) - server = '*' + server + cache.normalizedPattern += '[' + server + ']' + else + if server.charCodeAt(0) == '.'.charCodeAt(0) + server = '*' + server + cache.normalizedPattern = server + if matchPort - if not serverRegex? - serverRegex = shExp2RegExp(server) - serverRegex = serverRegex.substring(1, serverRegex.length - 1) + cache.port = matchPort + cache.normalizedPattern += ':' + cache.port + # In URL, IPv6 server addresses need to be bracketed. + if serverIp? and not serverIp.v4 + server = '[' + server + ']' + serverRegex = shExp2RegExp(server) + serverRegex = serverRegex.substring(1, serverRegex.length - 1) scheme = cache.scheme ? '[^:]+' cache.url = @safeRegex('^' + scheme + ':\\/\\/' + serverRegex + ':' + matchPort + '\\/') else if server != '*' - if serverRegex - serverRegex = '^' + serverRegex + '$' - else - serverRegex = shExp2RegExp server, trimAsterisk: true + # In host, IPv6 server addresses are never bracketed. + serverRegex = shExp2RegExp server, trimAsterisk: true cache.host = @safeRegex(serverRegex) return cache match: (condition, request, cache) -> @@ -374,6 +383,13 @@ module.exports = exports = return false if not cache.host.test(request.host) return false if cache.url? and !cache.url.test(request.url) return true + str: (condition) -> + analyze = @_handler(condition).analyze + cache = analyze.call(exports, condition) + if cache.normalizedPattern + return cache.normalizedPattern + else + return condition.pattern compile: (condition, cache) -> cache = cache.analyzed if cache.url? diff --git a/omega-pac/src/profiles.coffee b/omega-pac/src/profiles.coffee index 4c52a99..951c69a 100644 --- a/omega-pac/src/profiles.coffee +++ b/omega-pac/src/profiles.coffee @@ -246,7 +246,7 @@ module.exports = exports = } { conditionType: 'BypassCondition' - pattern: '::1' + pattern: '[::1]' } { conditionType: 'BypassCondition' diff --git a/omega-pac/test/conditions.coffee b/omega-pac/test/conditions.coffee index b28682b..b012037 100644 --- a/omega-pac/test/conditions.coffee +++ b/omega-pac/test/conditions.coffee @@ -183,6 +183,13 @@ describe 'Conditions', -> result = Conditions.analyze(cond) testCond(cond, 'http://[::1]:8080/', 'match') testCond(cond, 'http://[1::1]:8080/', not 'match') + it 'should correctly support IPv6 canonicalization 2', -> + cond = + conditionType: 'BypassCondition' + pattern: '[::1]' + result = Conditions.analyze(cond) + testCond(cond, 'http://[::1]:8080/', 'match') + testCond(cond, 'http://[1::1]:8080/', not 'match') it 'should parse IPv4 CIDR notation', -> cond = @@ -559,6 +566,32 @@ describe 'Conditions', -> result.should.equal('HostWildcard: ' + condition.pattern) cond = Conditions.fromStr(result) cond.should.eql(condition) + it 'should encode & decode BypassCondition correctly', -> + condition = + conditionType: 'BypassCondition' + pattern: '127.0.0.1/16' + result = Conditions.str(condition) + result.should.equal('Bypass: 127.0.0.1/16') + cond = Conditions.fromStr(result) + cond.should.eql(condition) + it 'should add brackets for IPv6 hosts in BypassCondition', -> + condition = + conditionType: 'BypassCondition' + pattern: '::1' + result = Conditions.str(condition) + result.should.equal('Bypass: [::1]') + cond = Conditions.fromStr(result) + cond.conditionType.should.equal('BypassCondition') + cond.pattern.should.equal('[::1]') + it 'should add brackets for IPv6 hosts with scheme in BypassCondition', -> + condition = + conditionType: 'BypassCondition' + pattern: 'http://::1' + result = Conditions.str(condition) + result.should.equal('Bypass: http://[::1]') + cond = Conditions.fromStr(result) + cond.conditionType.should.equal('BypassCondition') + cond.pattern.should.equal('http://[::1]') it 'should encode & decode IpCondition correctly', -> condition = conditionType: 'IpCondition' diff --git a/omega-target-chromium-extension/src/options.coffee b/omega-target-chromium-extension/src/options.coffee index 2ae07b4..1375e1a 100644 --- a/omega-target-chromium-extension/src/options.coffee +++ b/omega-target-chromium-extension/src/options.coffee @@ -74,6 +74,10 @@ class ChromeOptions extends OmegaTarget.Options chrome.browserAction.setBadgeText(text: '') return + _formatBypassItem: (condition) -> + str = OmegaPac.Conditions.str(condition) + i = str.indexOf(' ') + return str.substr(i + 1) _fixedProfileConfig: (profile) -> config = {} config['mode'] = 'fixed_servers' @@ -101,8 +105,8 @@ class ChromeOptions extends OmegaTarget.Options if config['mode'] != 'direct' rules['bypassList'] = bypassList = [] - for rule in profile.bypassList - bypassList.push(rule.pattern) + for condition in profile.bypassList + bypassList.push(@_formatBypassItem(condition)) config['rules'] = rules return config