ZeroOmega/omega-pac/test/conditions.coffee

277 lines
11 KiB
CoffeeScript

chai = require 'chai'
should = chai.should()
describe 'Conditions', ->
Conditions = require '../src/conditions'
U2 = require 'uglify-js'
testCond = (condition, request, should_match) ->
o_request = request
should_match = !!should_match
if typeof request == 'string'
request = Conditions.requestFromUrl(request)
matchResult = Conditions.match(condition, request)
condExpr = Conditions.compile(condition, request)
testFunc = new U2.AST_Function(
argnames: [
new U2.AST_SymbolFunarg name: 'url'
new U2.AST_SymbolFunarg name: 'host'
new U2.AST_SymbolFunarg name: 'scheme'
]
body: [
new U2.AST_Return value: condExpr
]
)
testFunc = eval '(' + testFunc.print_to_string() + ')'
compileResult = testFunc(request.url, request.host, request.scheme)
friendlyError = (compiled) ->
# Try to give friendly assert messages instead of something like
# "expect true to be false".
printCond = JSON.stringify(condition)
printCompiled = if compiled then 'COMPILED ' else ''
printMatch = if should_match then 'to match' else 'not to match'
msg = ("expect #{printCompiled}condition #{printCond} " +
"#{printMatch} request #{o_request}")
chai.assert(false, msg)
if matchResult != should_match
friendlyError()
if compileResult != should_match
friendlyError('compiled')
return matchResult
describe 'TrueCondition', ->
it 'should always return true', ->
testCond({conditionType: 'TrueCondition'}, {}, 'match')
describe 'FalseCondition', ->
it 'should always return false', ->
testCond({conditionType: 'FalseCondition'}, {}, not 'match')
describe 'UrlRegexCondition', ->
cond =
conditionType: 'UrlRegexCondition'
pattern: 'example\\.com'
it 'should match requests based on regex pattern', ->
testCond(cond, 'http://www.example.com/', 'match')
it 'should not match requests not matching the pattern', ->
testCond(cond, 'http://www.example.net/', not 'match')
it 'should support regex meta chars', ->
con =
conditionType: 'UrlRegexCondition'
pattern: 'exam.*\\.com'
testCond(con, 'http://www.example.com/', 'match')
it 'should fallback to not match if pattern is invalid', ->
con =
conditionType: 'UrlRegexCondition'
pattern: ')Invalid('
testCond(con, 'http://www.example.com/', not 'match')
describe 'UrlWildcardCondition', ->
cond =
conditionType: 'UrlWildcardCondition'
pattern: '*example.com*'
it 'should match requests based on wildcard pattern', ->
testCond(cond, 'http://www.example.com/', 'match')
it 'should not match requests not matching the pattern', ->
testCond(cond, 'http://www.example.net/', not 'match')
it 'should support wildcard question marks', ->
cond =
conditionType: 'UrlWildcardCondition'
pattern: '*exam???.com*'
testCond(cond, 'http://www.example.com/', 'match')
it 'should not support regex meta chars', ->
cond =
conditionType: 'UrlWildcardCondition'
pattern: '.*example.com.*'
testCond(cond, 'http://example.com/', not 'match')
it 'should support multiple patterns in one condition', ->
cond =
conditionType: 'UrlWildcardCondition'
pattern: '*.example.com/*|*.example.net/*'
testCond(cond, 'http://a.example.com/abc', 'match')
testCond(cond, 'http://b.example.net/def', 'match')
testCond(cond, 'http://c.example.org/ghi', not 'match')
describe 'HostRegexCondition', ->
cond =
conditionType: 'HostRegexCondition'
pattern: '.*\\.example\\.com'
it 'should match requests based on regex pattern', ->
testCond(cond, 'http://www.example.com/', 'match')
it 'should not match requests not matching the pattern', ->
testCond(cond, 'http://example.com/', not 'match')
it 'should not match URL parts other than the host', ->
testCond(cond, 'http://example.net/www.example.com')
.should.be.false
describe 'HostWildcardCondition', ->
cond =
conditionType: 'HostWildcardCondition'
pattern: '*.example.com'
it 'should match requests based on wildcard pattern', ->
testCond(cond, 'http://www.example.com/', 'match')
it 'should also match hostname without the optional level', ->
# https://github.com/FelisCatus/SwitchyOmega/wiki/Host-wildcard-condition
testCond(cond, 'http://example.com/', 'match')
it 'should process patterns like *.*example.com correctly', ->
# https://github.com/FelisCatus/SwitchyOmega/issues/158
con =
conditionType: 'HostWildcardCondition'
pattern: '*.*example.com'
testCond(con, 'http://example.com/', 'match')
testCond(con, 'http://www.example.com/', 'match')
testCond(con, 'http://www.some-example.com/', 'match')
testCond(con, 'http://xample.com/', not 'match')
it 'should allow override of the magical behavior', ->
con =
conditionType: 'HostWildcardCondition'
pattern: '**.example.com'
testCond(con, 'http://www.example.com/', 'match')
testCond(con, 'http://example.com/', not 'match')
it 'should not match URL parts other than the host', ->
testCond(cond, 'http://example.net/www.example.com')
.should.be.false
it 'should support multiple patterns in one condition', ->
cond =
conditionType: 'HostWildcardCondition'
pattern: '*.example.com|*.example.net'
testCond(cond, 'http://a.example.com/abc', 'match')
testCond(cond, 'http://example.net/def', 'match')
testCond(cond, 'http://c.example.org/ghi', not 'match')
describe 'BypassCondition', ->
# See https://developer.chrome.com/extensions/proxy#bypass_list
it 'should correctly support patterns containing hosts', ->
cond =
conditionType: 'BypassCondition'
pattern: '.example.com'
testCond(cond, 'http://www.example.com/', 'match')
testCond(cond, 'http://example.com/', not 'match')
cond.pattern = '*.example.com'
testCond(cond, 'http://www.example.com/', 'match')
testCond(cond, 'http://example.com/', not 'match')
cond.pattern = 'example.com'
testCond(cond, 'http://example.com/', 'match')
testCond(cond, 'http://www.example.com/', not 'match')
cond.pattern = '*example.com'
testCond(cond, 'http://example.com/', 'match')
testCond(cond, 'http://www.example.com/', 'match')
testCond(cond, 'http://anotherexample.com/', 'match')
it 'should match the scheme specified in the pattern', ->
cond =
conditionType: 'BypassCondition'
pattern: 'http://example.com'
testCond(cond, 'http://example.com/', 'match')
testCond(cond, 'https://example.com/', not 'match')
it 'should match the port specified in the pattern', ->
cond =
conditionType: 'BypassCondition'
pattern: 'http://example.com:8080'
testCond(cond, 'http://example.com:8080/', 'match')
testCond(cond, 'http://example.com:888/', not 'match')
it 'should correctly support patterns using IPv4 literals', ->
cond =
conditionType: 'BypassCondition'
pattern: 'http://127.0.0.1:8080'
testCond(cond, 'http://127.0.0.1:8080/', 'match')
testCond(cond, 'http://127.0.0.2:8080/', not 'match')
it 'should correctly support IPv6 canonicalization', ->
cond =
conditionType: 'BypassCondition'
pattern: 'http://[0:0::1]:8080'
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 =
conditionType: 'BypassCondition'
pattern: '192.168.0.0/16'
result = Conditions.analyze(cond).analyzed
should.exist(result.ip)
result.ip.should.eql({
conditionType: 'IpCondition'
ip: '192.168.0.0'
prefixLength: 16
})
it 'should parse IPv6 CIDR notation', ->
cond =
conditionType: 'BypassCondition'
pattern: 'fefe:13::abc/33'
result = Conditions.analyze(cond).analyzed
should.exist(result.ip)
result.ip.should.eql({
conditionType: 'IpCondition'
ip: 'fefe:13::abc'
prefixLength: 33
})
it 'should parse IPv6 CIDR notation with zero prefixLength', ->
cond =
conditionType: 'BypassCondition'
pattern: '::/0'
result = Conditions.analyze(cond).analyzed
should.exist(result.ip)
result.ip.should.eql({
conditionType: 'IpCondition'
ip: '::'
prefixLength: 0
})
describe 'IpCondition', ->
# IpCondition requires isInNetEx or isInNet function provided by the PAC
# runner, which is not available in the unit test. So We can't use testCond
# here.
it 'should support IPv4 subnet', ->
cond =
conditionType: "IpCondition"
ip: '192.168.1.1'
prefixLength: 16
request = Conditions.requestFromUrl('http://192.168.4.4/')
Conditions.match(cond, request).should.be.true
compiled = Conditions.compile(cond).print_to_string()
compiled.should.equal('isInNet(host,"192.168.1.1","255.255.0.0")')
it 'should support IPv6 subnet', ->
cond =
conditionType: "IpCondition"
ip: 'fefe:13::abc'
prefixLength: 33
request = Conditions.requestFromUrl('http://[fefe:13::def]/')
Conditions.match(cond, request).should.be.true
compiled = Conditions.compile(cond).print_to_string()
compiled_args = compiled.substr(compiled.lastIndexOf('('))
compiled_args.should.eql('(host,"fefe:13::abc","ffff:ffff:8000::")')
it 'should support IPv6 subnet with zero prefixLength', ->
cond =
conditionType: "IpCondition"
ip: '::'
prefixLength: 0
request = Conditions.requestFromUrl('http://[fefe:13::def]/')
Conditions.match(cond, request).should.be.true
compiled = Conditions.compile(cond).print_to_string()
compiled.indexOf('indexOf(').should.be.above(0)
it 'should not match domain name to IP subnet', ->
cond =
conditionType: "IpCondition"
ip: '::'
prefixLength: 0
request = Conditions.requestFromUrl('http://www.example.com/')
Conditions.match(cond, request).should.be.false
describe 'KeywordCondition', ->
cond =
conditionType: 'KeywordCondition'
pattern: 'example.com'
it 'should match requests based on substring', ->
testCond(cond, 'http://www.example.com/', 'match')
testCond(cond, 'http://www.example.net/', not 'match')
it 'should not match HTTPS requests', ->
testCond(cond, 'https://example.com/', not 'match')
testCond(cond, 'https://example.net/', not 'match')