def uses_localstorage(js_dest: str, exclude: list = None) -> bool: """ Search for ``localStorage`` calls in a JavaScript source file or directory. :param js_dest: Path to a JavaScript source file or directory. :param exclude: Paths that contains any string from this list are ignored. """ method = 'window.localStorage' tk_object = CaselessKeyword('localstorage') tk_method = Word(alphanums) lsto = tk_object + Literal('.') + tk_method + Suppress(nestedExpr()) result = False try: matches = lang.check_grammar(lsto, js_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not use {} method'.format(method), details=dict(code_dest=js_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=js_dest)) return False else: result = True show_open('Code uses {} method'.format(method), details=dict(matched=matches, total_vulns=len(matches))) return result
def uses_system_exit(java_dest: str, exclude: list = None) -> bool: """ Search for ``System.exit`` calls in a or package. :param java_dest: Path to a Java source file or package. :param exclude: Paths that contains any string from this list are ignored. """ method = 'System.exit' sys_exit = Literal(method) result = False try: matches = lang.check_grammar(sys_exit, java_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not use {} method'.format(method), details=dict(code_dest=java_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=java_dest)) return False else: result = True show_open('Code uses {} method'.format(method), details=dict(matched=matches, total_vulns=len(matches))) return result
def has_generic_exceptions(py_dest: str, exclude: list = None) -> bool: """ Search for generic exceptions in a Python script or package. :param py_dest: Path to a Python script or package. :param exclude: Paths that contains any string from this list are ignored. """ tk_except = CaselessKeyword('except') generic_exception = tk_except + Literal(':') result = False try: matches = lang.check_grammar(generic_exception, py_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not use generic exceptions', details=dict(code_dest=py_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=py_dest)) return False else: result = True show_open('Code uses generic exceptions', details=dict(file=matches, total_vulns=len(matches))) return result
def uses_insecure_hash(java_dest: str, algorithm: str, exclude: list = None) -> bool: """ Check if code uses an insecure hashing algorithm. See `REQ.150 <https://fluidattacks.com/web/rules/150/>`_. :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 = 'MessageDigest.getInstance("{}")'.format(algorithm.upper()) tk_mess_dig = CaselessKeyword('messagedigest') tk_get_inst = CaselessKeyword('getinstance') tk_alg = Literal('"') + CaselessKeyword(algorithm.lower()) + Literal('"') tk_params = Literal('(') + tk_alg + Literal(')') instance = tk_mess_dig + Literal('.') + tk_get_inst + tk_params result = False try: matches = lang.check_grammar(instance, java_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not use {} method'.format(method), details=dict(code_dest=java_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=java_dest)) return False else: result = True show_open('Code uses {} method'.format(method), details=dict(matched=matches, total_vulns=len(matches))) return result
def has_insecure_randoms(js_dest: str, exclude: list = None) -> bool: r""" Check if code uses ``Math.Random()``\ . See `REQ.224 <https://fluidattacks.com/web/rules/224/>`_. :param js_dest: Path to a JavaScript source file or package. :param exclude: Paths that contains any string from this list are ignored. """ method = 'Math.random()' tk_class = CaselessKeyword('math') tk_method = CaselessKeyword('random') tk_params = nestedExpr() call_function = tk_class + Literal('.') + tk_method + Suppress(tk_params) result = False try: matches = lang.check_grammar(call_function, js_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not use {} method'.format(method), details=dict(code_dest=js_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=js_dest)) return False else: result = True show_open('Code uses {} method'.format(method), details=dict(matched=matches, total_vulns=len(matches))) return result
def has_generic_exceptions(csharp_dest: str, exclude: list = None) -> bool: """ Search for generic exceptions in a C# source file or package. :param csharp_dest: Path to a C# source file or package. :param exclude: Paths that contains any string from this list are ignored. """ tk_catch = CaselessKeyword('catch') tk_generic_exc = CaselessKeyword('exception') tk_type = Word(alphas) tk_object_name = Word(alphas) tk_object = Word(alphas) generic_exception = Optional(Literal('}')) + tk_catch + Literal('(') + \ tk_generic_exc + Optional(Literal('(') + tk_type + Literal(')')) + \ Optional(tk_object_name) + \ Optional(Literal('(') + tk_object + Literal(')')) result = False try: matches = lang.check_grammar(generic_exception, csharp_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not use generic exceptions', details=dict(code_dest=csharp_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=csharp_dest)) result = False else: result = True show_open('Code uses generic exceptions', details=dict(matched=matches, total_vulns=len(matches))) return result
def uses_eval(js_dest: str, exclude: list = None) -> bool: """ Search for ``eval()`` calls in a JavaScript file or directory. :param js_dest: Path to a JavaScript source file or directory. :param exclude: Paths that contains any string from this list are ignored. """ method = 'eval()' tk_method = CaselessKeyword('eval') call_function = tk_method + Suppress(nestedExpr()) result = False try: matches = lang.check_grammar(call_function, js_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not use {} method'.format(method), details=dict(code_dest=js_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=js_dest)) return False else: result = True show_open('Code uses {} method'.format(method), details=dict(matched=matches, total_vulns=len(matches))) return result
def uses_ecb_encryption_mode(csharp_dest: str, exclude: list = None) -> bool: """ Check if code uses ECB as encryption mode. :param csharp_dest: Path to a C# source file or package. :param exclude: Paths that contains any string from this list are ignored. """ method = "Mode = CipherMode.ECB" tk_eq = Literal('=') tk_obj = SkipTo(tk_eq) tk_cm = CaselessKeyword('ciphermode') tk_ecb = CaselessKeyword('ecb') call_function = tk_obj + tk_eq + tk_cm + Literal('.') + tk_ecb result = False try: matches = lang.check_grammar(call_function, csharp_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not use {} method'.format(method), details=dict(code_dest=csharp_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=csharp_dest)) result = False else: result = True show_open('Code uses {} method'.format(method), details=dict(matched=matches, total_vulns=len(matches))) return result
def uses_console_writeline(csharp_dest: str, exclude: list = None) -> bool: """ Check if code uses Console.WriteLine method. :param csharp_dest: Path to a C# source file or package. :param exclude: Paths that contains any string from this list are ignored. """ method = "Console.WriteLine" tk_console = CaselessKeyword('console') tk_wrilin = CaselessKeyword('writeline') call_function = tk_console + Literal('.') + tk_wrilin result = False try: matches = lang.check_grammar(call_function, csharp_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not use {} method'.format(method), details=dict(code_dest=csharp_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=csharp_dest)) return False else: result = True show_open('Code uses {} method'.format(method), details=dict(matched=matches, total_vulns=len(matches))) return result
def uses_sha1_hash(csharp_dest: str, exclude: list = None) -> bool: """ Check if code uses SHA1 as hashing algorithm. See `REQ.150 <https://fluidattacks.com/web/rules/150/>`_. :param csharp_dest: Path to a C# source file or package. :param exclude: Paths that contains any string from this list are ignored. """ method = "new SHA1CryptoServiceProvider(), new SHA1Managed()" tk_new = CaselessKeyword('new') tk_sha1cry = CaselessKeyword('SHA1CryptoServiceProvider') tk_sha1man = CaselessKeyword('SHA1Managed') tk_params = nestedExpr() call_function = tk_new + MatchFirst([tk_sha1cry, tk_sha1man]) + tk_params result = False try: matches = lang.check_grammar(call_function, csharp_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not use {} method'.format(method), details=dict(code_dest=csharp_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=csharp_dest)) return False else: result = True show_open('Code uses {} method'.format(method), details=dict(matched=matches, total_vulns=len(matches))) return result
def has_generic_exceptions(rpg_dest: str, exclude: list = None) -> bool: """ Search for on-error empty. See `REQ. 161 <https://fluidattacks.com/web/rules/161/>`_. :param rpg_dest: Path to a RPG source or directory. :param exclude: Paths that contains any string from this list are ignored. """ tk_on = CaselessKeyword('on') tk_error = CaselessKeyword('error') tk_monitor = tk_on + Literal('-') + tk_error + Literal(';') result = False try: matches = lang.check_grammar(tk_monitor, rpg_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not have empty monitors', details=dict(code_dest=rpg_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=rpg_dest)) return False else: result = True show_open('Code has empty monitors', details=dict(matched=matches, total_vulns=len(matches))) return result
def has_dos_dow_sqlcod(rpg_dest: str, exclude: list = None) -> bool: r""" Search for DoS for using ``DoW SQLCOD = <ZERO>``\ . :param rpg_dest: Path to a RPG source or directory. :param exclude: Paths that contains any string from this list are ignored. """ tk_dow = CaselessKeyword('dow') tk_sqlcod = CaselessKeyword('sqlcod') tk_literal_zero = CaselessKeyword('*zeros') tk_zeros = MatchFirst([Literal('0'), tk_literal_zero]) dos_dow_sqlcod = tk_dow + tk_sqlcod + Literal('=') + tk_zeros result = False try: matches = lang.check_grammar(dos_dow_sqlcod, rpg_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not have DoS for using "DoW SQLCOD = 0"', details=dict(code_dest=rpg_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=rpg_dest)) return False else: result = True show_open('Code has DoS for using "DoW SQLCOD = 0"', details=dict(matched=matches, total_vulns=len(matches))) return result
def not_pinned(file_dest: str, exclude: list = None) -> bool: """ Check if the Dockerfile uses a ``FROM:...latest`` (unpinned) base image. :param file_dest: Path to the Dockerfile to be tested. :param exclude: Paths that contains any string from this list are ignored. :returns: True if unpinned (bad), False if pinned (good). """ tk_from = Literal('FROM') tk_image = Word(alphas) tk_version = Literal('latest') pinned = tk_from + tk_image + Literal(':') + tk_version result = False try: matches = lang.check_grammar(pinned, file_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Dockerfile has pinned base image(s)', details=dict(code_dest=file_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=file_dest)) return False else: result = True show_open('Dockerfile uses unpinned base image(s)', details=dict(file=matches, total_vulns=len(matches))) return result
def has_unitialized_vars(rpg_dest: str, exclude: list = None) -> bool: """ Search for unitialized variables. :param rpg_dest: Path to a RPG source or directory. :param exclude: Paths that contains any string from this list are ignored. """ tk_data = Keyword('D') tk_first = Word(alphas + "_", exact=1) tk_rest = Word(alphanums + "_") tk_vartype = Word(alphas, exact=1) tk_varlen = Word(nums) + Word(alphas, exact=1) tk_inz = CaselessKeyword('inz') tk_varname = tk_first + tk_rest unitialized = tk_data + tk_varname + Optional(tk_vartype) + \ Optional(tk_varlen) + Optional(Word(nums)) + NotAny(tk_inz) result = False try: matches = lang.check_grammar(unitialized, rpg_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not have unitialized variables', details=dict(code_dest=rpg_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=rpg_dest)) return False else: result = True show_open('Code has unitialized variables', details=dict(matched=matches, total_vulns=len(matches))) return result
def has_if_without_else(java_dest: str, exclude: list = None) -> bool: r""" Check if all ``if``\ s have an ``else`` clause. See `REQ.161 <https://fluidattacks.com/web/rules/161/>`_. :param java_dest: Path to a Java source file or package. :param exclude: Paths that contains any string from this list are ignored. """ args = nestedExpr(opener='(', closer=')') if_ = Keyword('if') + args if_line = Optional('}') + if_ + Optional('{') try: conds = lang.check_grammar(if_line, java_dest, LANGUAGE_SPECS, exclude) except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=java_dest)) return False else: if not conds: show_close('Code does not use "if" statements', details=dict(code_dest=java_dest)) return False block = nestedExpr(opener='{', closer='}') if_block = if_ + block else_if_block = Keyword('else') + Keyword('if') + args + block else_block = Keyword('else') + block cond_block = \ Suppress(if_block + ZeroOrMore(else_if_block)) + Optional(else_block) cond_block.ignore(javaStyleComment) cond_block.ignore(L_CHAR) cond_block.ignore(L_STRING) vulns = {} for code_file, val in conds.items(): vulns.update(lang.block_contains_empty_grammar(cond_block, code_file, val['lines'], _get_block)) if not vulns: show_close('Code has "if" with "else" clause', details=dict(file=java_dest, fingerprint=get_sha256(java_dest))) else: show_open('Code has "if" without "else" clause', details=dict(matched=vulns, total_vulns=len(vulns))) return True return False
def has_switch_without_default(csharp_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 csharp_dest: Path to a C# source file or package. :param exclude: Paths that contains any string from this list are ignored. """ switch = Keyword('switch') + nestedExpr(opener='(', closer=')') switch_line = Optional('}') + switch + Optional('{') result = False try: switches = lang.check_grammar(switch_line, csharp_dest, LANGUAGE_SPECS, exclude) except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=csharp_dest)) return False else: if not switches: show_close('Code does not have switches', details=dict(code_dest=csharp_dest)) return False switch_block = Suppress(switch) + nestedExpr(opener='{', closer='}') switch_block.ignore(javaStyleComment) switch_block.ignore(L_CHAR) switch_block.ignore(L_STRING) vulns = {} for code_file, val in switches.items(): vulns.update( lang.block_contains_grammar(switch_block, code_file, val['lines'], _get_block, should_not_have=r'(?:default\s*:)')) if not vulns: show_close('Code has "switch" with "default" clause', details=dict(file=csharp_dest, fingerprint=get_sha256(csharp_dest))) else: show_open('Code does not have "switch" with "default" clause', details=dict(matched=vulns, total_vulns=len(vulns))) result = True return result
def has_ssl_disabled(apphostconf_dest: str, exclude: list = None) -> bool: """ Check if SSL is disabled in ``ApplicationHost.config``. Search for access tag in security section in an ``ApplicationHost.config`` source file or package. :param apphostconf_dest: Path to an ``ApplicationHost.config`` source file or package. :param exclude: Paths that contains any string from this list are ignored. """ tk_tag_s, _ = makeXMLTags('security') tk_access, _ = makeXMLTags('access') tag_no_comm = tk_access.ignore(htmlComment) tk_access_none = copy(tag_no_comm) tk_access_none.setParseAction(withAttribute(sslFlags='None')) result = False try: sec_tag = lang.check_grammar(tk_tag_s, apphostconf_dest, LANGUAGE_SPECS, exclude) if not sec_tag: show_unknown('Not files matched', details=dict(code_dest=apphostconf_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=apphostconf_dest)) return False access_tags = {} none_sslflags = {} for code_file, val in sec_tag.items(): access_tags.update( lang.block_contains_grammar(tk_access, code_file, val['lines'], _get_block)) none_sslflags.update( lang.block_contains_grammar(tk_access_none, code_file, val['lines'], _get_block)) if not access_tags or none_sslflags: show_open('SSL is disabled', details=dict( matched=access_tags if access_tags else none_sslflags)) result = True else: show_close('SSL is enabled', details=dict(file=apphostconf_dest, fingerprint=get_sha256(apphostconf_dest))) return result
def swallows_exceptions(py_dest: str, exclude: list = None) -> bool: """ Search for swallowed exceptions. Identifies ``except`` blocks that are either empty or only contain comments or the ``pass`` statement. :param py_dest: Path to a Python script or package. :param exclude: Paths that contains any string from this list are ignored. """ tk_except = CaselessKeyword('except') tk_word = Word(alphas) + Optional('.') tk_pass = Literal('pass') tk_exc_obj = tk_word + Optional(Literal('as') + tk_word) parser_exception = tk_except + \ Optional('(') + \ Optional(delimitedList(tk_exc_obj)) + \ Optional(')') + Literal(':') empty_exception = (Suppress(parser_exception) + tk_pass).ignore(pythonStyleComment) result = False try: matches = lang.check_grammar(parser_exception, py_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not have excepts', details=dict(code_dest=py_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=py_dest)) return False vulns = {} for code_file, val in matches.items(): vulns.update( lang.block_contains_grammar(empty_exception, code_file, val['lines'], _get_block)) if not vulns: show_close('Code does not have empty "catches"', details=dict(file=py_dest, fingerprint=get_sha256(py_dest))) else: show_open('Code has empty "catches"', details=dict(matched=vulns, total_vulns=len(vulns))) result = True return result
def is_header_x_powered_by_present(webconf_dest: str, exclude: list = None) -> bool: """ Search for X-Powered-By headers in a Web.config source file or package. :param webconf_dest: Path to a Web.config source file or package. :param exclude: Paths that contains any string from this list are ignored. """ tk_tag_s, _ = makeXMLTags('customHeaders') tk_add_tag, _ = makeXMLTags('add') tk_clear_tag, _ = makeXMLTags('clear') tk_remove_tag, _ = makeXMLTags('remove') tk_remove_tag.setParseAction(withAttribute(name='X-Powered-By')) tk_child_tag = MatchFirst( [Suppress(tk_add_tag), Suppress(tk_clear_tag), tk_remove_tag]) result = False try: custom_headers = lang.check_grammar(tk_tag_s, webconf_dest, LANGUAGE_SPECS, exclude) if not custom_headers: show_unknown('Not files matched', details=dict(code_dest=webconf_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=webconf_dest)) return False tk_rem = Suppress(tk_tag_s) + OneOrMore(tk_child_tag) vulns = {} for code_file, val in custom_headers.items(): vulns.update( lang.block_contains_empty_grammar(tk_rem, code_file, val['lines'], _get_block)) if vulns: show_open('Header "X-Powered-By" is present', details=dict(matched=vulns, total_lines=len(custom_headers))) result = True else: show_close('Header "X-Powered-By" is not present', details=dict(file=webconf_dest, fingerprint=get_sha256(webconf_dest))) return result
def has_if_without_else(csharp_dest: str, exclude: list = None) -> bool: r""" Check if all ``if``\ s have an ``else`` clause. See `REQ.161 <https://fluidattacks.com/web/rules/161/>`_. :param csharp_dest: Path to a C# source file or package. :param exclude: Paths that contains any string from this list are ignored. """ tk_if = CaselessKeyword('if') tk_else = CaselessKeyword('else') block = nestedExpr(opener='{', closer='}') prsr_if = tk_if + nestedExpr() + block prsr_else = Suppress(tk_else) + (prsr_if | block) if_head = tk_if + nestedExpr() + Optional(Literal('{')) if_wout_else = (Suppress(prsr_if) + prsr_else).ignore(cppStyleComment) result = False try: conds = lang.check_grammar(if_head, csharp_dest, LANGUAGE_SPECS, exclude) if not conds: show_close('Code does not have conditionals', details=dict(code_dest=csharp_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=csharp_dest)) return False vulns = {} for code_file, val in conds.items(): vulns.update( lang.block_contains_empty_grammar(if_wout_else, code_file, val['lines'], _get_block_as_one_liner)) if not vulns: show_close('Code has "if" with "else" clauses', details=dict(file=csharp_dest, fingerprint=get_sha256(csharp_dest))) else: show_open('Code does not have "if" with "else" clauses', details=dict(matched=vulns, total_vulns=len(vulns))) result = True return result
def swallows_exceptions(csharp_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/>`_. :param csharp_dest: Path to a C# source file or package. :param exclude: Paths that contains any string from this list are ignored. """ tk_catch = CaselessKeyword('catch') tk_word = Word(alphas) parser_catch = (Optional(Literal('}')) + tk_catch + Literal('(') + tk_word + Optional(Literal('(') + tk_word + Literal(')')) + Optional(tk_word) + Literal(')')) empty_catch = (Suppress(parser_catch) + nestedExpr(opener='{', closer='}')).ignore(cppStyleComment) result = False try: catches = lang.check_grammar(parser_catch, csharp_dest, LANGUAGE_SPECS, exclude) if not catches: show_close('Code does not have catches', details=dict(code_dest=csharp_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=csharp_dest)) return False vulns = {} for code_file, val in catches.items(): vulns.update( lang.block_contains_empty_grammar(empty_catch, code_file, val['lines'], _get_block_as_one_liner)) if not vulns: show_close('Code does not have empty catches', details=dict(file=csharp_dest, fingerprint=get_sha256(csharp_dest))) else: show_open('Code has empty catches', details=dict(matches=vulns, total_vulns=len(vulns))) result = True return result
def has_debug_enabled(webconf_dest: str, exclude: list = None) -> bool: """ Check if debug flag is enabled in Web.config. Search for debug tag in compilation section in a Web.config source file or package. :param webconf_dest: Path to a Web.config source file or package. :param exclude: Paths that contains any string from this list are ignored. """ tk_tag_s, _ = makeXMLTags('system.web') tk_compilation, _ = makeXMLTags('compilation') tag_no_comm = tk_compilation.ignore(htmlComment) tk_comp_debug = copy(tag_no_comm) tk_comp_debug.setParseAction(withAttribute(debug='true')) result = False try: sysweb_tag = lang.check_grammar(tk_tag_s, webconf_dest, LANGUAGE_SPECS, exclude) if not sysweb_tag: show_unknown('Not files matched', details=dict(code_dest=webconf_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=webconf_dest)) return False debug_tags = {} for code_file, val in sysweb_tag.items(): debug_tags.update( lang.block_contains_grammar(tk_comp_debug, code_file, val['lines'], _get_block)) if debug_tags: show_open('Debug is enabled', details=dict(matched=debug_tags, total_lines=len(sysweb_tag))) result = True else: show_close('Debug is disabled', details=dict(file=webconf_dest, fingerprint=get_sha256(webconf_dest))) return result
def swallows_exceptions(rpg_dest: str, exclude: list = None) -> bool: """ Search for on-error without code. See `REQ.075 <https://fluidattacks.com/web/rules/075>`_. :param rpg_dest: Path to a RPG source or directory. :param exclude: Paths that contains any string from this list are ignored. """ tk_on = CaselessKeyword('on') tk_error = CaselessKeyword('error') tk_code = Word(nums) tk_monitor = tk_on + Literal('-') + tk_error + Optional(tk_code) + \ Literal(';') tk_end_mon = CaselessKeyword('endmon') + Literal(';') prs_sw = (tk_monitor + tk_end_mon).ignore(cppStyleComment) result = False try: matches = lang.check_grammar(tk_monitor, rpg_dest, LANGUAGE_SPECS, exclude) if not matches: show_unknown('Code does not have error handling', details=dict(code_dest=rpg_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=rpg_dest)) return False vulns = {} for code_file, val in matches.items(): vulns.update( lang.block_contains_grammar(prs_sw, code_file, val['lines'], _get_block)) if vulns: show_open('Code swallows exceptions', details=dict(matched=vulns, total_vulns=len(vulns))) result = True else: show_close('Code does not swallow exceptions', details=dict(file=rpg_dest, fingerprint=get_sha256(rpg_dest))) return result
def not_custom_errors(webconf_dest: str, exclude: list = None) -> bool: """ Check if customErrors flag is set to off in Web.config. CWE-12: ASP.NET Misconfiguration: Missing Custom Error Page :param webconf_dest: Path to a Web.config source file or package. :param exclude: Paths that contains any string from this list are ignored. """ tk_tag_s, _ = makeXMLTags('system.web') tk_custom_errors, _ = makeXMLTags('customErrors') tag_no_comm = tk_custom_errors.ignore(htmlComment) tk_comp_custom_errors = copy(tag_no_comm) tk_comp_custom_errors.setParseAction(withAttribute(mode='Off')) result = False try: sysweb_tag = lang.check_grammar(tk_tag_s, webconf_dest, LANGUAGE_SPECS, exclude) if not sysweb_tag: show_unknown('Not files matched', details=dict(code_dest=webconf_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=webconf_dest)) return False vulns = {} for code_file, val in sysweb_tag.items(): vulns.update( lang.block_contains_grammar(tk_comp_custom_errors, code_file, val['lines'], _get_block)) if vulns: show_open('Custom errors are not enabled', details=dict(matches=vulns, total_lines=len(sysweb_tag))) result = True else: show_close('Custom errors are enabled', details=dict(file=webconf_dest, fingerprint=get_sha256(webconf_dest))) return result
def uses_md5_hash(csharp_dest: str, exclude: list = None) -> bool: """ Check if code uses MD5 as hashing algorithm. See `REQ.150 <https://fluidattacks.com/web/rules/150/>`_. :param csharp_dest: Path to a C# source file or package. :param exclude: Paths that contains any string from this list are ignored. """ method = 'MD5.Create(), new MD5CryptoServiceProvider()' tk_md5 = CaselessKeyword('md5') tk_create = CaselessKeyword('create') tk_params = nestedExpr() fn_1 = tk_md5 + Literal('.') + tk_create + tk_params tk_new = CaselessKeyword('new') tk_md5cry = CaselessKeyword('MD5CryptoServiceProvider') tk_params = nestedExpr() fn_2 = tk_new + tk_md5cry + tk_params call_function = MatchFirst([fn_1, fn_2]) result = False try: matches = lang.check_grammar(call_function, csharp_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code uses {} method'.format(method), details=dict(code_dest=csharp_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=csharp_dest)) result = False else: result = True show_open('Code uses {} method'.format(method), details=dict(matched=matches, total_vulns=len(matches))) return result
def has_log_injection(java_dest: str, exclude: list = None) -> bool: """ Search code injection. Check if the code does not neutralize or incorrectly neutralizes output that is written to logs. See `CWE-117 <https://cwe.mitre.org/data/definitions/117.html>`_. :param java_dest: Path to a Java source file or package. :param exclude: Paths that contains any string from this list are ignored. """ log_variable = CaselessKeyword('log') log_level = oneOf('trace debug info warn error fatal') log_object = log_variable + Literal('.') + log_level tk_string = QuotedString('"') tk_var = Word(alphanums) pst = log_object + Literal('(') + tk_string + Literal('+') + tk_var result = False try: matches = lang.check_grammar(pst, java_dest, LANGUAGE_SPECS, exclude) if not matches: show_close('Code does not allow logs injection', details=dict(code_dest=java_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=java_dest)) return False else: result = True show_open('Code allows logs injection', details=dict(matched=matches, total_vulns=len(matches))) return result
def has_insecure_randoms(csharp_dest: str, exclude: list = None) -> bool: """ Check if code instantiates ``Random`` class. See `REQ.224 <https://fluidattacks.com/web/rules/224/>`_. :param csharp_dest: Path to a C# source file or package. :param exclude: Paths that contains any string from this list are ignored. """ tk_new = Keyword('new') tk_var = Keyword('var') tk_equal = Literal('=') tk_params = nestedExpr() tk_random = Keyword('Random') tk_variable = Word(alphas + '_', alphanums + '_') instantiation = (tk_var | tk_random) + tk_variable + tk_equal + tk_new + \ tk_random + Suppress(tk_params) result = False try: random_new = lang.check_grammar(instantiation, csharp_dest, LANGUAGE_SPECS, exclude) if not random_new: show_close('Code does not generate insecure random numbers', details=dict(code_dest=csharp_dest)) return False except FileNotFoundError: show_unknown('File does not exist', details=dict(code_dest=csharp_dest)) result = False else: result = True show_open('Code generates insecure random numbers', details=dict(matched=random_new, total_vulns=len(random_new))) return result