def swallows_exceptions(js_dest: str, exclude: list = None) -> bool: """ Search for ``catch`` blocks that are empty or only have comments. See `REQ.161 <https://fluidattacks.com/web/rules/161/>`_. See `CWE-391 <https://cwe.mitre.org/data/definitions/391.html>`_. :param js_dest: Path to a JavaScript source file or package. :param exclude: Paths that contains any string from this list are ignored. """ # Empty() grammar matches 'anything' # ~Empty() grammar matches 'not anything' or 'nothing' classic = Suppress(Keyword('catch')) + nestedExpr(opener='(', closer=')') \ + nestedExpr(opener='{', closer='}', content=~Empty()) modern = Suppress('.' + Keyword('catch')) + nestedExpr( opener='(', closer=')', content=~Empty()) grammar = MatchFirst([classic, modern]) grammar.ignore(cppStyleComment) try: matches = lang.path_contains_grammar(grammar, js_dest, LANGUAGE_SPECS, exclude) except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=js_dest)) else: if matches: show_open('Code has empty "catch" blocks', details=dict(matched=matches)) return True show_close('Code does not have empty "catch" blocks', details=dict(code_dest=js_dest)) return False
def has_switch_without_default(js_dest: str, exclude: list = None) -> bool: r""" Check if all ``switch``\ es have a ``default`` clause. See `REQ.161 <https://fluidattacks.com/web/rules/161/>`_. See `CWE-478 <https://cwe.mitre.org/data/definitions/478.html>`_. :param js_dest: Path to a JavaScript source file or package. :param exclude: Paths that contains any string from this list are ignored. """ switch = Keyword('switch') + nestedExpr(opener='(', closer=')') switch_block = Suppress(switch) + nestedExpr(opener='{', closer='}') switch_block.ignore(cppStyleComment) switch_block.ignore(L_CHAR) switch_block.ignore(L_STRING) switch_block.addCondition(lambda x: not RE_HAVES_DEFAULT.search(str(x))) try: matches = lang.path_contains_grammar(switch_block, js_dest, LANGUAGE_SPECS, exclude) except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=js_dest)) return False if matches: show_open('Code does not have "switch" with "default" clause', details=dict(matched=matches)) return True show_close('Code has "switch" with "default" clause', details=dict(code_dest=js_dest)) return False
def _declares_catch_for_exceptions( java_dest: str, exceptions_list: list, open_msg: str, closed_msg: str, exclude: list = None) -> bool: """Search for the declaration of catch for the given exceptions.""" any_exception = L_VAR_CHAIN_NAME provided_exception = MatchFirst( [Keyword(exception) for exception in exceptions_list]) exception_group = delimitedList(expr=any_exception, delim='|') exception_group.addCondition( # Ensure that at least one exception in the group is the provided one lambda tokens: any(provided_exception.matches(tok) for tok in tokens)) grammar = Suppress(Keyword('catch')) + nestedExpr( opener='(', closer=')', content=( exception_group + Suppress(Optional(L_VAR_NAME)))) grammar.ignore(javaStyleComment) grammar.ignore(L_STRING) grammar.ignore(L_CHAR) try: matches = lang.path_contains_grammar(grammar, java_dest, LANGUAGE_SPECS, exclude) except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=java_dest)) else: if matches: show_open(open_msg, details=dict(matched=matches)) return True show_close(closed_msg, details=dict(code_dest=java_dest)) return False
def uses_print_stack_trace(java_dest: str, exclude: list = None) -> bool: """ Search for ``printStackTrace`` calls in a path. See `CWE-209 <https://cwe.mitre.org/data/definitions/209.html>`_. :param java_dest: Path to a Java source file or package. :param exclude: Paths that contains any string from this list are ignored. """ grammar = L_VAR_NAME + '.' + Keyword('printStackTrace') grammar.ignore(javaStyleComment) grammar.ignore(L_STRING) grammar.ignore(L_CHAR) try: matches = lang.path_contains_grammar(grammar, java_dest, LANGUAGE_SPECS, exclude) except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=java_dest)) else: if matches: show_open('Code uses Throwable.printStackTrace() method', details=dict(matched=matches, total_vulns=len(matches))) return True show_close('Code does not use Throwable.printStackTrace() method', details=dict(code_dest=java_dest)) return False
def uses_unencrypted_sockets(code_dest: str, exclude: list = None, lang_specs: dict = None) -> bool: """ Check if there are unencrypted web sockets URI schemes in code (`ws://`). :param code_dest: Path to the file or directory to be tested. :param exclude: Paths that contains any string from this list are ignored. :param lang_specs: Specifications of the language, see fluidasserts.lang.java.LANGUAGE_SPECS for an example. """ encrypted_re = re.compile(r'^wss://.*$', flags=re.I) unencrypted_re = re.compile(r'^ws://.*$', flags=re.I) encrypted_grammar = MatchFirst([QuotedString('"'), QuotedString("'")]) unencrypted_grammar = MatchFirst([QuotedString('"'), QuotedString("'")]) encrypted_grammar.addCondition(lambda x: encrypted_re.search(x[0])) unencrypted_grammar.addCondition(lambda x: unencrypted_re.search(x[0])) try: unencrypted = lang.path_contains_grammar(unencrypted_grammar, code_dest, LANGUAGE_SPECS, exclude) except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=code_dest)) return False if unencrypted: show_open('Code uses web sockets over an unencrypted channel', details=dict(vulnerable_uris=unencrypted)) return True encrypted = lang.path_contains_grammar(encrypted_grammar, code_dest, LANGUAGE_SPECS, exclude) if encrypted: msg = 'Code uses web sockets over an encrypted channel' else: msg = 'Cose does not use web sockets' show_close(msg, details=dict(code_dest=code_dest, checked_uris=encrypted)) return False
def has_insecure_randoms(java_dest: str, exclude: list = None) -> bool: r""" Check if code uses insecure random generators. - ``java.util.Random()``. - ``java.lang.Math.random()``. See `REQ.224 <https://fluidattacks.com/web/rules/224/>`_. :param java_dest: Path to a Java source file or package. :param exclude: Paths that contains any string from this list are ignored. """ _java = Keyword('java') _util = Keyword('util') _lang = Keyword('lang') _math = Keyword('Math') _import = Keyword('import') _random_minus = Keyword('random') _random_mayus = Keyword('Random') _args = nestedExpr() insecure_randoms = MatchFirst([ # util.Random() _util + '.' + _random_mayus + _args, # Math.random() _math + '.' + _random_minus + _args, # import java.util.Random _import + _java + '.' + _util + '.' + _random_mayus, # import java.lang.Math.random _import + _java + '.' + _lang + '.' + _math + '.' + _random_minus, ]) insecure_randoms.ignore(javaStyleComment) insecure_randoms.ignore(L_CHAR) insecure_randoms.ignore(L_STRING) try: matches = lang.path_contains_grammar(insecure_randoms, java_dest, LANGUAGE_SPECS, exclude) except FileNotFoundError: show_unknown('File does not exist', details=dict(location=java_dest)) return False if not matches: show_close('Code does not use insecure random generators', details=dict(location=java_dest)) return False show_open('Code uses insecure random generators', details=dict(matches=matches)) return True
def uses_insecure_cipher(java_dest: str, algorithm: str, exclude: list = None) -> bool: """ Check if code uses an insecure cipher algorithm. See `REQ.148 <https://fluidattacks.com/web/rules/148/>`_. See `REQ.149 <https://fluidattacks.com/web/rules/149/>`_. :param java_dest: Path to a Java source file or package. :param algorithm: Insecure algorithm. :param exclude: Paths that contains any string from this list are ignored. """ method = 'Cipher.getInstance("{}")'.format(algorithm.upper()) op_mode = '/' + oneOf('CBC ECB', caseless=True) padding = '/' + oneOf('NoPadding PKCS5Padding', caseless=True) algorithm = '"' + CaselessKeyword(algorithm) + Optional( op_mode + Optional(padding)) + '"' grammar = Suppress(Keyword('Cipher') + '.' + Keyword('getInstance')) + \ nestedExpr() grammar.ignore(javaStyleComment) grammar.addCondition( # Ensure that at least one token is the provided algorithm lambda tokens: tokens.asList() and any( algorithm.matches(tok) for tok in tokens[0])) try: matches = lang.path_contains_grammar(grammar, java_dest, LANGUAGE_SPECS, exclude) except FileNotFoundError: show_unknown('File does not exist', details=dict(location=java_dest)) return False if not matches: show_close('Code does not use {} method'.format(method), details=dict(location=java_dest)) return False show_open('Code uses {} method'.format(method), details=dict(matches=matches)) return True