Skip to content
This repository has been archived by the owner on Oct 6, 2020. It is now read-only.

Commit

Permalink
Fix #41, lint for regular expression catastrophic backtracking in re …
Browse files Browse the repository at this point in the history
…module
  • Loading branch information
mschwager committed Dec 27, 2019
1 parent 159d143 commit 4d0f79f
Show file tree
Hide file tree
Showing 10 changed files with 991 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- `DUO137`: lint for insecure itsdangerous kwarg usage ([#36](https://github.com/duo-labs/dlint/issues/36))
- `DUO138`: lint for regular expression catastrophic backtracking in re module ([#41](https://github.com/duo-labs/dlint/issues/41))

### Fixed
- False positive for `DUO137` when kwarg missing ([#39](https://github.com/duo-labs/dlint/issues/39))
Expand Down
1 change: 1 addition & 0 deletions dlint/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from . import linters # noqa F401
from . import multi # noqa F401
from . import namespace # noqa F401
from . import redos # noqa F401
from . import test # noqa F401
from . import tree # noqa F401
from . import util # noqa F401
Expand Down
2 changes: 2 additions & 0 deletions dlint/linters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from .bad_os_use import BadOSUseLinter
from .bad_popen2_use import BadPopen2UseLinter
from .bad_random_generator_use import BadRandomGeneratorUseLinter
from .bad_re_catastrophic_use import BadReCatastrophicUseLinter
from .bad_requests_use import BadRequestsUseLinter
from .bad_shelve_use import BadShelveUseLinter
from .bad_subprocess_use import BadSubprocessUseLinter
Expand Down Expand Up @@ -66,6 +67,7 @@
BadOSUseLinter,
BadPopen2UseLinter,
BadRandomGeneratorUseLinter,
BadReCatastrophicUseLinter,
BadRequestsUseLinter,
BadShelveUseLinter,
BadSSLModuleAttributeUseLinter,
Expand Down
76 changes: 76 additions & 0 deletions dlint/linters/bad_re_catastrophic_use.py
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)
]
8 changes: 8 additions & 0 deletions dlint/redos/__init__.py
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
69 changes: 69 additions & 0 deletions dlint/redos/__main__.py
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()
Loading

0 comments on commit 4d0f79f

Please sign in to comment.