mirror of
https://github.com/zero-peak/ZeroOmega.git
synced 2025-01-22 15:08:12 -05:00
Improve error handling for rule lists.
This commit is contained in:
parent
674f501ada
commit
054d1c8b58
@ -389,6 +389,8 @@ module.exports = exports =
|
|||||||
profile.matchProfileName ?= 'direct'
|
profile.matchProfileName ?= 'direct'
|
||||||
profile.ruleList ?= ''
|
profile.ruleList ?= ''
|
||||||
directReferenceSet: (profile) ->
|
directReferenceSet: (profile) ->
|
||||||
|
refs = RuleList[profile.format]?.directReferenceSet?(profile)
|
||||||
|
return refs if refs
|
||||||
refs = {}
|
refs = {}
|
||||||
for name in [profile.matchProfileName, profile.defaultProfileName]
|
for name in [profile.matchProfileName, profile.defaultProfileName]
|
||||||
refs[exports.nameAsKey(name)] = name
|
refs[exports.nameAsKey(name)] = name
|
||||||
@ -407,7 +409,7 @@ module.exports = exports =
|
|||||||
formatHandler = RuleList[format]
|
formatHandler = RuleList[format]
|
||||||
if not formatHandler
|
if not formatHandler
|
||||||
throw new Error "Unsupported rule list format #{format}!"
|
throw new Error "Unsupported rule list format #{format}!"
|
||||||
ruleList = profile.ruleList
|
ruleList = profile.ruleList?.trim() || ''
|
||||||
if formatHandler.preprocess?
|
if formatHandler.preprocess?
|
||||||
ruleList = formatHandler.preprocess(ruleList)
|
ruleList = formatHandler.preprocess(ruleList)
|
||||||
return formatHandler.parse(ruleList, profile.matchProfileName,
|
return formatHandler.parse(ruleList, profile.matchProfileName,
|
||||||
@ -418,6 +420,7 @@ module.exports = exports =
|
|||||||
exports.compile(profile, 'SwitchProfile')
|
exports.compile(profile, 'SwitchProfile')
|
||||||
updateUrl: (profile) -> profile.sourceUrl
|
updateUrl: (profile) -> profile.sourceUrl
|
||||||
update: (profile, data) ->
|
update: (profile, data) ->
|
||||||
|
data = data.trim()
|
||||||
original = profile.format ? exports.formatByType[profile.profileType]
|
original = profile.format ? exports.formatByType[profile.profileType]
|
||||||
profile.profileType = 'RuleListProfile'
|
profile.profileType = 'RuleListProfile'
|
||||||
format = original
|
format = original
|
||||||
|
@ -14,12 +14,10 @@ module.exports = exports =
|
|||||||
return true
|
return true
|
||||||
return
|
return
|
||||||
preprocess: (text) ->
|
preprocess: (text) ->
|
||||||
text = text.trim()
|
|
||||||
if strStartsWith(text, exports['AutoProxy'].magicPrefix)
|
if strStartsWith(text, exports['AutoProxy'].magicPrefix)
|
||||||
text = new Buffer(text, 'base64').toString('utf8')
|
text = new Buffer(text, 'base64').toString('utf8')
|
||||||
return text
|
return text
|
||||||
parse: (text, matchProfileName, defaultProfileName) ->
|
parse: (text, matchProfileName, defaultProfileName) ->
|
||||||
text = text.trim()
|
|
||||||
normal_rules = []
|
normal_rules = []
|
||||||
exclusive_rules = []
|
exclusive_rules = []
|
||||||
for line in text.split(/\n|\r/)
|
for line in text.split(/\n|\r/)
|
||||||
@ -55,21 +53,36 @@ module.exports = exports =
|
|||||||
|
|
||||||
'Switchy':
|
'Switchy':
|
||||||
omegaPrefix: '[SwitchyOmega Conditions'
|
omegaPrefix: '[SwitchyOmega Conditions'
|
||||||
|
specialLineStart: "[;#@!"
|
||||||
|
|
||||||
detect: (text) ->
|
detect: (text) ->
|
||||||
text = text.trim()
|
|
||||||
if strStartsWith(text, exports['Switchy'].omegaPrefix)
|
if strStartsWith(text, exports['Switchy'].omegaPrefix)
|
||||||
return true
|
return true
|
||||||
return
|
return
|
||||||
|
|
||||||
parse: (text, matchProfileName, defaultProfileName) ->
|
parse: (text, matchProfileName, defaultProfileName) ->
|
||||||
text = text.trim()
|
|
||||||
switchy = exports['Switchy']
|
switchy = exports['Switchy']
|
||||||
parser = 'parseOmega'
|
parser = switchy.getParser(text)
|
||||||
if not strStartsWith(text, switchy.omegaPrefix)
|
|
||||||
if text[0] == '#' or text.indexOf('\n#') >= 0
|
|
||||||
parser = 'parseLegacy'
|
|
||||||
return switchy[parser](text, matchProfileName, defaultProfileName)
|
return switchy[parser](text, matchProfileName, defaultProfileName)
|
||||||
|
|
||||||
|
directReferenceSet: ({ruleList, matchProfileName, defaultProfileName}) ->
|
||||||
|
text = ruleList.trim()
|
||||||
|
switchy = exports['Switchy']
|
||||||
|
parser = switchy.getParser(text)
|
||||||
|
return unless parser == 'parseOmega'
|
||||||
|
return unless /(^|\n)@with\s+results?(\r|\n)/i.test(text)
|
||||||
|
refs = {}
|
||||||
|
for line in text.split(/\n|\r/)
|
||||||
|
line = line.trim()
|
||||||
|
if switchy.specialLineStart.indexOf(line[0]) < 0
|
||||||
|
iSpace = line.lastIndexOf(' +')
|
||||||
|
if iSpace < 0
|
||||||
|
profile = defaultProfileName || 'direct'
|
||||||
|
else
|
||||||
|
profile = line.substr(iSpace + 2).trim()
|
||||||
|
refs['+' + profile] = profile
|
||||||
|
refs
|
||||||
|
|
||||||
# For the omega rule list format, please see the following wiki page:
|
# For the omega rule list format, please see the following wiki page:
|
||||||
# https://github.com/FelisCatus/SwitchyOmega/wiki/SwitchyOmega-conditions-format
|
# https://github.com/FelisCatus/SwitchyOmega/wiki/SwitchyOmega-conditions-format
|
||||||
compose: ({rules, defaultProfileName}, {withResult, useExclusive} = {}) ->
|
compose: ({rules, defaultProfileName}, {withResult, useExclusive} = {}) ->
|
||||||
@ -80,23 +93,31 @@ module.exports = exports =
|
|||||||
ruleList += '@with result' + eol + eol
|
ruleList += '@with result' + eol + eol
|
||||||
else
|
else
|
||||||
ruleList += eol
|
ruleList += eol
|
||||||
|
specialLineStart = exports['Switchy'].specialLineStart + '+'
|
||||||
for rule in rules
|
for rule in rules
|
||||||
line = Conditions.str(rule.condition)
|
line = Conditions.str(rule.condition)
|
||||||
if line[0] == '#' or line[0] == '+'
|
|
||||||
# Escape leading # to avoid being detected as legacy format.
|
|
||||||
# Reserve leading + for condition results.
|
|
||||||
line = ': ' + line
|
|
||||||
if useExclusive and rule.profileName == defaultProfileName
|
if useExclusive and rule.profileName == defaultProfileName
|
||||||
line = '!' + line
|
line = '!' + line
|
||||||
else if withResult
|
else
|
||||||
# TODO(catus): What if rule.profileName contains ' +' or new lines?
|
if specialLineStart.indexOf(line[0]) >= 0
|
||||||
line += ' +' + rule.profileName
|
line = ': ' + line
|
||||||
|
if withResult
|
||||||
|
# TODO(catus): What if rule.profileName contains ' +' or new lines?
|
||||||
|
line += ' +' + rule.profileName
|
||||||
ruleList += line + eol
|
ruleList += line + eol
|
||||||
if withResult
|
if withResult
|
||||||
# TODO(catus): Also special chars and sequences in defaultProfileName.
|
# TODO(catus): Also special chars and sequences in defaultProfileName.
|
||||||
ruleList += '* +' + defaultProfileName + eol
|
ruleList += '* +' + defaultProfileName + eol
|
||||||
return ruleList
|
return ruleList
|
||||||
|
|
||||||
|
getParser: (text) ->
|
||||||
|
switchy = exports['Switchy']
|
||||||
|
parser = 'parseOmega'
|
||||||
|
if not strStartsWith(text, switchy.omegaPrefix)
|
||||||
|
if text[0] == '#' or text.indexOf('\n#') >= 0
|
||||||
|
parser = 'parseLegacy'
|
||||||
|
return parser
|
||||||
|
|
||||||
conditionFromLegacyWildcard: (pattern) ->
|
conditionFromLegacyWildcard: (pattern) ->
|
||||||
if pattern[0] == '@'
|
if pattern[0] == '@'
|
||||||
pattern = pattern.substring(1)
|
pattern = pattern.substring(1)
|
||||||
@ -151,10 +172,12 @@ module.exports = exports =
|
|||||||
# Exclusive rules have higher priority, so they come first.
|
# Exclusive rules have higher priority, so they come first.
|
||||||
return exclusive_rules.concat normal_rules
|
return exclusive_rules.concat normal_rules
|
||||||
|
|
||||||
parseOmega: (text, matchProfileName, defaultProfileName) ->
|
parseOmega: (text, matchProfileName, defaultProfileName, args = {}) ->
|
||||||
|
{strict} = args
|
||||||
rules = []
|
rules = []
|
||||||
rulesWithDefaultProfile = []
|
rulesWithDefaultProfile = []
|
||||||
withResult = false
|
withResult = false
|
||||||
|
exclusiveProfile = null
|
||||||
for line in text.split(/\n|\r/)
|
for line in text.split(/\n|\r/)
|
||||||
line = line.trim()
|
line = line.trim()
|
||||||
continue if line.length == 0
|
continue if line.length == 0
|
||||||
@ -183,16 +206,18 @@ module.exports = exports =
|
|||||||
else if withResult
|
else if withResult
|
||||||
iSpace = line.lastIndexOf(' +')
|
iSpace = line.lastIndexOf(' +')
|
||||||
if iSpace < 0
|
if iSpace < 0
|
||||||
throw new Error("Missing result profile name: " + line)
|
throw new Error("Missing result profile name: " + line) if strict
|
||||||
|
continue
|
||||||
profile = line.substr(iSpace + 2).trim()
|
profile = line.substr(iSpace + 2).trim()
|
||||||
line = line.substr(0, iSpace).trim()
|
line = line.substr(0, iSpace).trim()
|
||||||
defaultProfileName = profile if line == '*'
|
exclusiveProfile = profile if line == '*'
|
||||||
else
|
else
|
||||||
profile = matchProfileName
|
profile = matchProfileName
|
||||||
|
|
||||||
cond = Conditions.fromStr(line)
|
cond = Conditions.fromStr(line)
|
||||||
if not cond
|
if not cond
|
||||||
throw new Error("Invalid rule: " + line)
|
throw new Error("Invalid rule: " + line) if strict
|
||||||
|
continue
|
||||||
|
|
||||||
rule = {condition: cond, profileName: profile, source: source ? line}
|
rule = {condition: cond, profileName: profile, source: source ? line}
|
||||||
rules.push(rule)
|
rules.push(rule)
|
||||||
@ -200,8 +225,10 @@ module.exports = exports =
|
|||||||
rulesWithDefaultProfile.push(rule)
|
rulesWithDefaultProfile.push(rule)
|
||||||
|
|
||||||
if withResult
|
if withResult
|
||||||
if not defaultProfileName
|
if not exclusiveProfile
|
||||||
throw new Error("Missing default rule with catch-all '*' condition!")
|
if strict
|
||||||
|
throw new Error("Missing default rule with catch-all '*' condition")
|
||||||
|
exclusiveProfile = defaultProfileName || 'direct'
|
||||||
for rule in rulesWithDefaultProfile
|
for rule in rulesWithDefaultProfile
|
||||||
rule.profileName = defaultProfileName
|
rule.profileName = exclusiveProfile
|
||||||
return rules
|
return rules
|
||||||
|
@ -209,6 +209,24 @@ describe 'Profiles', ->
|
|||||||
'+example': 'example'
|
'+example': 'example'
|
||||||
'+default': 'default'
|
'+default': 'default'
|
||||||
)
|
)
|
||||||
|
it 'should calulate referenced profiles for rule list with results', ->
|
||||||
|
set = Profiles.directReferenceSet({
|
||||||
|
profileType: 'RuleListProfile'
|
||||||
|
format: 'Switchy'
|
||||||
|
matchProfileName: 'ignored'
|
||||||
|
defaultProfileName: 'alsoIgnored'
|
||||||
|
ruleList: '''
|
||||||
|
[SwitchyOmega Conditions]
|
||||||
|
@with result
|
||||||
|
!*.example.org
|
||||||
|
*.example.com +ABC
|
||||||
|
* +DEF
|
||||||
|
'''
|
||||||
|
})
|
||||||
|
set.should.eql(
|
||||||
|
'+ABC': 'ABC'
|
||||||
|
'+DEF': 'DEF'
|
||||||
|
)
|
||||||
it 'should match requests based on the rule list', ->
|
it 'should match requests based on the rule list', ->
|
||||||
testProfile(profile, 'http://localhost/example.com',
|
testProfile(profile, 'http://localhost/example.com',
|
||||||
ruleListResult('example', 'example.com'))
|
ruleListResult('example', 'example.com'))
|
||||||
|
@ -291,6 +291,17 @@ describe 'RuleList', ->
|
|||||||
result = parse(list, 'match', 'notmatch')
|
result = parse(list, 'match', 'notmatch')
|
||||||
result.should.have.length(1)
|
result.should.have.length(1)
|
||||||
result[0].should.eql(rule)
|
result[0].should.eql(rule)
|
||||||
|
it 'should compose and parse conditions starting with special chars', ->
|
||||||
|
rule =
|
||||||
|
source: ': ;abc'
|
||||||
|
condition:
|
||||||
|
conditionType: 'HostWildcardCondition',
|
||||||
|
pattern: ';abc'
|
||||||
|
profileName: 'match'
|
||||||
|
list = compose({rules: [rule], defaultProfileName: 'notmatch'})
|
||||||
|
result = parse(list, 'match', 'notmatch')
|
||||||
|
result.should.have.length(1)
|
||||||
|
result[0].should.eql(rule)
|
||||||
it 'should parse multiple conditions', ->
|
it 'should parse multiple conditions', ->
|
||||||
rules = [{
|
rules = [{
|
||||||
source: '*.example.com'
|
source: '*.example.com'
|
||||||
|
Loading…
Reference in New Issue
Block a user