Exemplo n.º 1
0
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
Exemplo n.º 2
0
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