lint: Do not allow non-printable characters in configuration

This commit is contained in:
ZyX 2014-12-04 21:14:13 +03:00
parent 2656953f24
commit f37efeac5b
5 changed files with 50 additions and 18 deletions

View File

@ -41,7 +41,7 @@ def generate_json_config_loader(lhadproblem):
function_name_re = '^(\w+\.)*[a-zA-Z_]\w*$' function_name_re = '^(\w+\.)*[a-zA-Z_]\w*$'
divider_spec = Spec().type(unicode).len( divider_spec = Spec().printable().len(
'le', 3, (lambda value: 'Divider {0!r} is too large!'.format(value))).copy 'le', 3, (lambda value: 'Divider {0!r} is too large!'.format(value))).copy
ext_theme_spec = Spec().type(unicode).func(lambda *args: check_config('themes', *args)).copy ext_theme_spec = Spec().type(unicode).func(lambda *args: check_config('themes', *args)).copy
top_theme_spec = Spec().type(unicode).func(check_top_theme).copy top_theme_spec = Spec().type(unicode).func(check_top_theme).copy
@ -211,12 +211,12 @@ segment_spec = Spec(
display=Spec().type(bool).optional(), display=Spec().type(bool).optional(),
module=segment_module_spec(), module=segment_module_spec(),
priority=Spec().type(int, float, type(None)).optional(), priority=Spec().type(int, float, type(None)).optional(),
after=Spec().type(unicode).optional(), after=Spec().printable().optional(),
before=Spec().type(unicode).optional(), before=Spec().printable().optional(),
width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(), width=Spec().either(Spec().unsigned(), Spec().cmp('eq', 'auto')).optional(),
align=Spec().oneof(set('lr')).optional(), align=Spec().oneof(set('lr')).optional(),
args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_function, *args, **kwargs)), args=args_spec().func(lambda *args, **kwargs: check_args(get_one_segment_function, *args, **kwargs)),
contents=Spec().type(unicode).optional(), contents=Spec().printable().optional(),
highlight_group=Spec().list( highlight_group=Spec().list(
highlight_group_spec().re( highlight_group_spec().re(
'^(?:(?!:divider$).)+$', '^(?:(?!:divider$).)+$',
@ -243,11 +243,11 @@ divside_spec = Spec(
soft=divider_spec(), soft=divider_spec(),
).copy ).copy
segment_data_value_spec = Spec( segment_data_value_spec = Spec(
after=Spec().type(unicode).optional(), after=Spec().printable().optional(),
before=Spec().type(unicode).optional(), before=Spec().printable().optional(),
display=Spec().type(bool).optional(), display=Spec().type(bool).optional(),
args=args_spec().func(lambda *args, **kwargs: check_args(get_all_possible_functions, *args, **kwargs)), args=args_spec().func(lambda *args, **kwargs: check_args(get_all_possible_functions, *args, **kwargs)),
contents=Spec().type(unicode).optional(), contents=Spec().printable().optional(),
).copy ).copy
dividers_spec = Spec( dividers_spec = Spec(
left=divside_spec(), left=divside_spec(),

View File

@ -7,7 +7,7 @@ import re
from powerline.lib.unicode import unichr from powerline.lib.unicode import unichr
NON_PRINTABLE = re.compile( NON_PRINTABLE_STR = (
'[^' '[^'
# ASCII control characters: 0x00-0x19 # ASCII control characters: 0x00-0x19
+ '\t\n' # Tab, newline: allowed ASCII control characters + '\t\n' # Tab, newline: allowed ASCII control characters
@ -26,6 +26,7 @@ NON_PRINTABLE = re.compile(
'' ''
)) ))
) )
NON_PRINTABLE_RE = re.compile(NON_PRINTABLE_STR)
def repl(s): def repl(s):
@ -33,7 +34,7 @@ def repl(s):
def strtrans(s): def strtrans(s):
return NON_PRINTABLE.sub(repl, s.replace('\t', '>---')) return NON_PRINTABLE_RE.sub(repl, s.replace('\t', '>---'))
class Mark: class Mark:
@ -73,6 +74,13 @@ class Mark:
+ ' ' * (indent + len(head) + len(snippet[0])) + '^' + ' ' * (indent + len(head) + len(snippet[0])) + '^'
) )
def advance_string(self, diff):
ret = self.copy()
# FIXME Currently does not work properly with escaped strings.
ret.column += diff
ret.pointer += diff
return ret
def __str__(self): def __str__(self):
snippet = self.get_snippet() snippet = self.get_snippet()
where = (' in "%s", line %d, column %d' % ( where = (' in "%s", line %d, column %d' % (

View File

@ -33,12 +33,7 @@ class MarkedUnicode(unicode):
pointdiff = 1 pointdiff = 1
r = [] r = []
for s in part_result: for s in part_result:
mark = self.mark.copy() r.append(MarkedUnicode(s, self.mark.advance_string(pointdiff)))
# XXX Does not work properly with escaped strings, but this requires
# saving much more information in mark.
mark.column += pointdiff
mark.pointer += pointdiff
r.append(MarkedUnicode(s, mark))
pointdiff += len(s) pointdiff += len(s)
return tuple(r) return tuple(r)

View File

@ -3,7 +3,7 @@ from __future__ import (unicode_literals, division, absolute_import, print_funct
import codecs import codecs
from powerline.lint.markedjson.error import MarkedError, Mark, NON_PRINTABLE from powerline.lint.markedjson.error import MarkedError, Mark, NON_PRINTABLE_RE
from powerline.lib.unicode import unicode from powerline.lib.unicode import unicode
@ -84,7 +84,7 @@ class Reader(object):
return Mark(self.name, self.line, self.column, self.full_buffer, self.full_pointer) return Mark(self.name, self.line, self.column, self.full_buffer, self.full_pointer)
def check_printable(self, data): def check_printable(self, data):
match = NON_PRINTABLE.search(data) match = NON_PRINTABLE_RE.search(data)
if match: if match:
self.update_pointer(match.start()) self.update_pointer(match.start())
raise ReaderError( raise ReaderError(

View File

@ -7,10 +7,19 @@ import re
from copy import copy from copy import copy
from powerline.lib.unicode import unicode from powerline.lib.unicode import unicode
from powerline.lint.markedjson.error import echoerr, DelayedEchoErr from powerline.lint.markedjson.error import echoerr, DelayedEchoErr, NON_PRINTABLE_STR
from powerline.lint.selfcheck import havemarks from powerline.lint.selfcheck import havemarks
NON_PRINTABLE_RE = re.compile(
NON_PRINTABLE_STR.translate({
ord('\t'): None,
ord('\n'): None,
0x0085: None,
})
)
class Spec(object): class Spec(object):
'''Class that describes some JSON value '''Class that describes some JSON value
@ -342,6 +351,26 @@ class Spec(object):
return False, hadproblem return False, hadproblem
return True, hadproblem return True, hadproblem
def check_printable(self, value, context_mark, data, context, echoerr, _):
'''Check that given unicode string contains only printable characters
'''
hadproblem = False
for match in NON_PRINTABLE_RE.finditer(value):
hadproblem = True
echoerr(
context=self.cmsg.format(key=context.key),
context_mark=value.mark,
problem='found not printable character U+{0:04x} in a configuration string'.format(
ord(match.group(0))),
problem_mark=value.mark.advance_string(match.start() + 1)
)
return True, hadproblem
def printable(self, *args):
self.type(unicode)
self.checks.append(('check_printable', args))
return self
def type(self, *args): def type(self, *args):
'''Describe value that has one of the types given in arguments '''Describe value that has one of the types given in arguments