def _check_flask_common_misconfig(ast_tree, path): """Look for common security misconfiguration in Flask apps""" violations = [] has_flask_run = has_method_call("app.run(??)", ast_tree) if has_import_like("flask", ast_tree) and has_flask_run: config_method_patterns = [ "??.from_file(??)", "??.from_json(??)", "??.from_envvar(??)", "??.from_mapping(??)", "??.from_object(??)", "??.from_pyfile(??)", ] uses_config_import = False for cmpattern in config_method_patterns: uses_config_import = has_method_call(cmpattern, ast_tree) if uses_config_import: break all_keys = [] config_dict = get_assignments_as_dict("?.config[?] = ?", ast_tree) for k, v in config_dict.items(): all_keys.append(k) # Static configs if k in rules.flask_nostatic_config: source, sink = convert_node_source_sink(v, path) if sink.trigger_word: obfuscated_label = sink.label if len(obfuscated_label) > 4: obfuscated_label = obfuscated_label[:4] + "****" violations.append( Insight( f"Security Misconfiguration with the config `{source.label}` set to a static value `{obfuscated_label}`", "Security Misconfiguration", "flask-misconfiguration-static", "CWE-732", "LOW", "a6-misconfiguration", source, sink, rules. rules_message_map["flask-misconfiguration-static"], )) # Do not set configs if k in rules.flask_noset_config: source, sink = convert_node_source_sink(v, path) if sink.trigger_word: violations.append( Insight( f"Security Misconfiguration with the config `{source.label}` set to a value `{sink.label}` meant for development use", "Security Misconfiguration", "flask-misconfiguration-insecure", "CWE-732", "LOW", "a6-misconfiguration", source, sink, rules.rules_message_map[ "flask-misconfiguration-insecure"], )) # Must set configs if not uses_config_import: must_configs = rules.flask_mustset_config.keys() for mc in must_configs: if mc not in all_keys: rsetting = rules.flask_mustset_config[mc] source, sink = convert_dict_source_sink( { "source_type": "Config", "source_trigger": mc, "source_line_number": 1, "sink_type": "Constant", "sink_trigger": rsetting.get("default"), "sink_line_number": 1, }, path, ) violations.append( Insight( f"""Security Misconfiguration with the config `{mc}` not set to the recommended value `{rsetting.get("recommended")}` for production use""", "Security Misconfiguration", "flask-misconfiguration-recommended", "CWE-732", "MEDIUM", "a6-misconfiguration", source, sink, rules.rules_message_map[ "flask-misconfiguration-recommended"], )) # Check for flask security if not has_import_like("flask_security", ast_tree) and not has_import_like( "flask_talisman", ast_tree): source, sink = convert_dict_source_sink( { "source_type": "Extension", "source_trigger": "flask_security", "source_line_number": 1, "sink_type": "Extension", "sink_trigger": None, "sink_line_number": 1, }, path, ) violations.append( Insight( "Consider adding Flask-Security and Flask-Talisman security extensions to your Flask apps", "Missing Security Controls", "flask-misconfiguration-controls", "CWE-732", "LOW", "a6-misconfiguration", source, sink, rules.rules_message_map["flask-misconfiguration-controls"], )) # Check for xss protection headers set to 0 # response.headers['X-XSS-Protection'] = '0' xss_protect_dict = get_assignments_as_dict( "??.headers['X-XSS-Protection'] = ??", ast_tree) if xss_protect_dict: xssh_key = xss_protect_dict.get("X-XSS-Protection").get( "left_hand_side") xssh_value = xss_protect_dict.get("X-XSS-Protection").get( "right_hand_side") if hasattr(xssh_value, "value") and not xssh_value.value: source, sink = convert_dict_source_sink( { "source_type": "Header", "source_trigger": "X-XSS-Protection", "source_line_number": xssh_key.lineno, "sink_type": "Constant", "sink_trigger": None, "sink_line_number": xssh_key.lineno, }, path, ) violations.append( Insight( "Disabling XSS protection directly in the code would make the application more vulnerable to XSS attacks", "Security Misconfiguration", "flask-misconfiguration-insecure", "CWE-732", "MEDIUM", "a6-misconfiguration", source, sink, rules. rules_message_map["flask-misconfiguration-insecure"], )) # Flask jwt checks if (has_import_like("flask_jwt_extended", ast_tree) or has_import_like( "flask_jwt", ast_tree)) and not uses_config_import: must_configs = rules.flask_jwt_mustset_config.keys() for mc in must_configs: if mc not in all_keys: rsetting = rules.flask_jwt_mustset_config[mc] source, sink = convert_dict_source_sink( { "source_type": "Config", "source_trigger": mc, "source_line_number": 1, "sink_type": "Constant", "sink_trigger": rsetting.get("default"), "sink_line_number": 1, }, path, ) violations.append( Insight( f"""Security Misconfiguration with the config `{mc}` not set to the recommended value `{rsetting.get("recommended")}` for production use""", "Missing Security Controls", "flask-misconfiguration-jwt", "CWE-732", "MEDIUM", "a6-misconfiguration", source, sink, rules. rules_message_map["flask-misconfiguration-jwt"], )) if mc == "JWT_SECRET_KEY" and mc in all_keys: # Discourage symmetric key source, sink = convert_dict_source_sink( { "source_type": "Config", "source_trigger": mc, "source_line_number": 1, "sink_type": "Constant", "sink_trigger": "", "sink_line_number": 1, }, path, ) violations.append( Insight( "Use an asymmetric RSA based algorithm such as RS512 for JWT", "Security Misconfiguration", "flask-misconfiguration-jwt", "CWE-327", "MEDIUM", "a6-misconfiguration", source, sink, rules. rules_message_map["flask-misconfiguration-jwt"], )) return violations
def _check_pymongo_common_misconfig(ast_tree, path): violations = [] if has_import_like("pymongo", ast_tree): method_obj_list = get_method_as_dict("??.MongoClient(??)", ast_tree) if not method_obj_list: method_obj_list = get_method_as_dict("MongoClient(??)", ast_tree) if not method_obj_list: return None method_obj = method_obj_list[0] start_line = method_obj.get("lineno") source, sink = convert_dict_source_sink( { "source_type": "Config", "source_trigger": "MongoClient", "source_line_number": start_line, "sink_type": "Constant", "sink_trigger": "", "sink_line_number": start_line, }, path, ) if not method_obj.get("args"): # pymongo connection to local mongodb instance violations.append( Insight( "Connection to a MongoDB instance running in default mode without any authentication", "Security Misconfiguration", "pymongo-misconfiguration-insecure", "CWE-732", "LOW", "a6-misconfiguration", source, sink, rules. rules_message_map["pymongo-misconfiguration-insecure"], )) elif method_obj.get("args") and method_obj.get("keywords"): # ssl checks args = method_obj.get("args") hostname = args[0].get("value", "localhost") if not isinstance(hostname, str): return violations # query_args = "" # if "?" in hostname: # query_args = hostname.split("?")[1] hostname = hostname.replace("mongodb://", "").split("/")[0] keywords = method_obj.get("keywords") for kw in keywords: arg = kw.get("arg") arg_value = "" if kw["value"]["_type"] == "Constant": arg_value = kw["value"]["value"] if kw["value"]["_type"] == "Attribute": arg_value = kw["value"]["attr"] # ssl is off if arg == "ssl" and not arg_value: violations.append( Insight( f"Connection to a MongoDB instance at `{hostname}` running in default mode without tls encryption", "Security Misconfiguration", "pymongo-misconfiguration-insecure", "CWE-732", "LOW", "a6-misconfiguration", source, sink, rules.rules_message_map[ "pymongo-misconfiguration-insecure"], )) if arg == "ssl_cert_reqs" and arg_value == "CERT_NONE": violations.append( Insight( f"Connection to a MongoDB instance at `{hostname}` running in default mode without tls certificate verification", "Security Misconfiguration", "pymongo-misconfiguration-insecure", "CWE-732", "LOW", "a6-misconfiguration", source, sink, rules.rules_message_map[ "pymongo-misconfiguration-insecure"], )) if arg == "authMechanism" and arg_value == "MONGODB-CR": violations.append( Insight( f"Connection to a MongoDB instance at `{hostname}` with a deprecated authentication method", "Security Misconfiguration", "pymongo-misconfiguration-insecure", "CWE-732", "LOW", "a6-misconfiguration", source, sink, rules.rules_message_map[ "pymongo-misconfiguration-insecure"], )) if not has_import_like("ClientEncryption", ast_tree) or not has_method_call( "ClientEncryption(??)", ast_tree): # client encryption checks violations.append( Insight( "Client-side Field Level Encryption allows an application to encrypt specific data fields based on the compliance needs", "Security Misconfiguration", "pymongo-misconfiguration-insecure", "CWE-732", "LOW", "a6-misconfiguration", source, sink, rules. rules_message_map["pymongo-misconfiguration-insecure"], )) return violations