This repository has been archived by the owner on Oct 6, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix #41, lint for regular expression catastrophic backtracking in re …
…module
- Loading branch information
Showing
10 changed files
with
991 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
#!/usr/bin/env python | ||
|
||
from __future__ import ( | ||
absolute_import, | ||
division, | ||
print_function, | ||
unicode_literals, | ||
) | ||
|
||
import ast | ||
|
||
from .helpers import bad_module_attribute_use | ||
from . import base | ||
from .. import redos | ||
|
||
|
||
class BadReCatastrophicUseLinter(bad_module_attribute_use.BadModuleAttributeUseLinter): | ||
"""This linter looks for regular expression catastrophic backtracking in | ||
the re module. Catastrophic backtracking can cause denial-of-service. | ||
""" | ||
off_by_default = False | ||
|
||
_code = 'DUO138' | ||
_error_tmpl = 'DUO138 catastrophic "re" usage - denial-of-service possible' | ||
|
||
@property | ||
def illegal_module_attributes(self): | ||
return { | ||
're': [ | ||
'compile', | ||
'search', | ||
'match', | ||
'fullmatch', | ||
'split', | ||
'findall', | ||
'finditer', | ||
'sub', | ||
'subn', | ||
], | ||
} | ||
|
||
def __init__(self, *args, **kwargs): | ||
self.calls = {} | ||
|
||
super(BadReCatastrophicUseLinter, self).__init__(*args, **kwargs) | ||
|
||
def visit_Call(self, node): | ||
self.generic_visit(node) | ||
|
||
self.calls[node.func] = node | ||
|
||
def get_results(self): | ||
pattern_argument_number = 0 | ||
|
||
def pattern_is_catastrophic(node): | ||
call = self.calls.get(node) | ||
if call is None or not call.args: | ||
return False | ||
|
||
pattern = call.args[pattern_argument_number] | ||
|
||
# Only handle string literals for now | ||
if not isinstance(pattern, ast.Str): | ||
return False | ||
|
||
return redos.detect.catastrophic(pattern.s) | ||
|
||
return [ | ||
base.Flake8Result( | ||
lineno=node.lineno, | ||
col_offset=node.col_offset, | ||
message=self._error_tmpl | ||
) | ||
for node in self.bad_nodes | ||
if pattern_is_catastrophic(node) | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from __future__ import ( | ||
absolute_import, | ||
division, | ||
print_function, | ||
unicode_literals, | ||
) | ||
|
||
from . import detect # noqa F401 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
#!/usr/bin/env python | ||
|
||
from __future__ import ( | ||
absolute_import, | ||
division, | ||
print_function, | ||
unicode_literals, | ||
) | ||
|
||
import argparse | ||
import sys | ||
|
||
from dlint import redos | ||
|
||
|
||
def parse_args(): | ||
p = argparse.ArgumentParser(description=''' | ||
Detect redos regular expression patterns from the command-line. | ||
''', formatter_class=argparse.RawTextHelpFormatter) | ||
|
||
p.add_argument( | ||
'-p', | ||
'--pattern', | ||
action='store', | ||
required=True, | ||
help='regular expression pattern' | ||
) | ||
|
||
dump_group = p.add_mutually_exclusive_group() | ||
|
||
dump_group.add_argument( | ||
'-d', | ||
'--dump', | ||
action='store_true', | ||
help='print parsed pattern' | ||
) | ||
dump_group.add_argument( | ||
'-t', | ||
'--dump-tree', | ||
action='store_true', | ||
help='print parsed op node tree' | ||
) | ||
|
||
args = p.parse_args() | ||
|
||
return args | ||
|
||
|
||
def main(): | ||
args = parse_args() | ||
|
||
if args.dump: | ||
redos.detect.dump(args.pattern) | ||
return | ||
|
||
if args.dump_tree: | ||
redos.detect.dump_tree(args.pattern) | ||
return | ||
|
||
if args.pattern == '-': | ||
pattern = sys.stdin.read() | ||
else: | ||
pattern = args.pattern | ||
|
||
print((pattern, redos.detect.catastrophic(pattern))) | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |
Oops, something went wrong.