예제 #1
0
def is_taintable_function(ast_node):
    """Returns only functions without a sanitization decorator"""
    for decorator in ast_node.decorator_list:
        if isinstance(decorator, ast.Call):
            if _get_last_of_iterable(get_call_names(
                    decorator.func)) in safe_decorators:
                return False
            # Flask route and Django tag
            if _get_last_of_iterable(get_call_names(decorator.func)) in [
                    "route",
                    "simple_tag",
                    "inclusion_tag",
                    "to_end_tag",
                    "expose",
                    "view_config",
                    "template",
                    "get",
                    "post",
                    "put",
                    "delete",
                    "middleware",
                    "api_view",
                    "action",
                    "csrf_exempt",
                    "deserialise_with",
                    "marshal_with",
                    "before",
                    "csrf_protect",
                    "requires_csrf_token",
                    "xframe_options_exempt",
                    "xframe_options_deny",
                    "xframe_options_sameorigin",
                    "before_first_request",
            ]:
                return True
    # Ignore database functions
    if len(ast_node.args.args):
        first_arg_name = ast_node.args.args[0].arg
        if first_arg_name == "self" and len(ast_node.args.args) > 1:
            first_arg_name = ast_node.args.args[1].arg
        # Common view functions such as django, starlette, falcon
        if first_arg_name in ["req", "request", "context", "scope"]:
            return True
        # Ignore dao classes due to potential FP
        if first_arg_name in [
                "conn", "connection", "cls", "session", "session_cls"
        ]:
            return False
    # Ignore internal functions prefixed with _
    if is_function_with_leading_(ast_node):
        return False
    # Ignore known validation and sanitization functions
    for n in ["valid", "sanitize", "sanitise", "is_", "set_", "assert"]:
        if n in ast_node.name:
            return False
    # Should we limit the scan only to web routes?
    web_route_only = os.environ.get("WEB_ROUTE_ONLY", False)
    if web_route_only:
        return False
    return True
예제 #2
0
def is_taintable_function(ast_node):
    """Returns only functions without a sanitization decorator"""
    for decorator in ast_node.decorator_list:
        if isinstance(decorator, ast.Call):
            if _get_last_of_iterable(get_call_names(decorator.func)) in safe_decorators:
                return False
            # Flask route and Django tag
            if _get_last_of_iterable(get_call_names(decorator.func)) in [
                "route",
                "simple_tag",
                "inclusion_tag",
                "to_end_tag",
                "expose",
            ]:
                return True
    # Ignore database functions
    if len(ast_node.args.args):
        first_arg_name = ast_node.args.args[0].arg
        # Common view functions such as django, starlette
        if first_arg_name in ["request", "context", "scope"]:
            return True
    # Ignore internal functions prefixed with _
    if is_function_with_leading_(ast_node):
        return False
    # Ignore known validation and sanitization functions
    for n in ["valid", "sanitize", "sanitise", "is_", "set_"]:
        if ast_node.name.startswith(n):
            return False
    return True
예제 #3
0
def is_flask_route_function(ast_node):
    """Check whether function uses a route decorator."""
    for decorator in ast_node.decorator_list:
        if isinstance(decorator, ast.Call):
            if _get_last_of_iterable(get_call_names(decorator.func)) == "route":
                return True
    return False
예제 #4
0
def is_taintable_function(ast_node):
    """Returns only functions without a sanitization decorator"""
    for decorator in ast_node.decorator_list:
        if isinstance(decorator, ast.Call):
            if _get_last_of_iterable(get_call_names(
                    decorator.func)) in safe_decorators:
                return False
            # Flask route and Django tag
            if _get_last_of_iterable(get_call_names(decorator.func)) in [
                    "route",
                    "simple_tag",
                    "inclusion_tag",
                    "to_end_tag",
                    "expose",
                    "view_config",
                    "template",
                    "get",
                    "post",
                    "put",
                    "delete",
                    "middleware",
                    "api_view",
                    "action",
            ]:
                return True
    # Ignore database functions
    if len(ast_node.args.args):
        first_arg_name = ast_node.args.args[0].arg
        # Common view functions such as django, starlette
        if first_arg_name in ["request", "context", "scope"]:
            return True
    # Ignore internal functions prefixed with _
    if is_function_with_leading_(ast_node):
        return False
    # Ignore known validation and sanitization functions
    for n in ["valid", "sanitize", "sanitise", "is_", "set_"]:
        if ast_node.name.startswith(n):
            return False
    # Should we limit the scan only to web routes?
    web_route_only = os.environ.get("WEB_ROUTE_ONLY", False)
    if web_route_only:
        return False
    return True
예제 #5
0
    def add_blackbox_or_builtin_call(self, node, blackbox):  # noqa: C901
        """Processes a blackbox or builtin function when it is called.
        Nothing gets assigned to ret_func_foo in the builtin/blackbox case.

        Increments self.function_call_index each time it is called, we can refer to it as N in the comments.
        Create e.g. ~call_1 = ret_func_foo RestoreNode.

        Create e.g. temp_N_def_arg1 = call_arg1_label_visitor.result for each argument.
        Visit the arguments if they're calls. (save_def_args_in_temp)

        I do not think I care about this one actually -- Create e.g. def_arg1 = temp_N_def_arg1 for each argument.
        (create_local_scope_from_def_args)

        Add RestoreNode to the end of the Nodes.

        Args:
            node(ast.Call) : The node that calls the definition.
            blackbox(bool): Whether or not it is a builtin or blackbox call.
        Returns:
            call_node(BBorBInode): The call node.
        """
        self.function_call_index += 1
        saved_function_call_index = self.function_call_index
        self.undecided = False

        call_label_visitor = LabelVisitor()
        call_label_visitor.visit(node)

        call_function_label = call_label_visitor.result[
            : call_label_visitor.result.find("(")
        ]

        # Check if function call matches a blackbox/built-in alias and if so, resolve it
        # This resolves aliases like "from os import system as mysys" as: mysys -> os.system
        local_definitions = self.module_definitions_stack[-1]
        call_function_label = fully_qualify_alias_labels(
            call_function_label, local_definitions.import_alias_mapping
        )

        # Create e.g. ~call_1 = ret_func_foo
        LHS = CALL_IDENTIFIER + "call_" + str(saved_function_call_index)
        RHS = "ret_" + call_function_label + "("

        call_node = BBorBInode(
            label="",
            left_hand_side=LHS,
            ast_node=node,
            right_hand_side_variables=[],
            line_number=node.lineno,
            path=self.filenames[-1],
            func_name=call_function_label,
        )
        visual_args = list()
        rhs_vars = list()
        last_return_value_of_nested_call = None

        for arg_node in itertools.chain(node.args, node.keywords):
            arg = arg_node.value if isinstance(arg_node, ast.keyword) else arg_node
            if isinstance(arg, ast.Call):
                return_value_of_nested_call = self.visit(arg)

                if last_return_value_of_nested_call:
                    # connect inner to other_inner in e.g.
                    # `scrypt.outer(scrypt.inner(image_name), scrypt.other_inner(image_name))`
                    # I should probably loop to the inner most call of other_inner here.
                    try:
                        last_return_value_of_nested_call.connect(
                            return_value_of_nested_call.first_node
                        )
                    except AttributeError:
                        last_return_value_of_nested_call.connect(
                            return_value_of_nested_call
                        )
                else:
                    # I should only set this once per loop, inner in e.g.
                    # `scrypt.outer(scrypt.inner(image_name), scrypt.other_inner(image_name))`
                    # (inner_most_call is used when predecessor is a ControlFlowNode in connect_control_flow_node)
                    call_node.inner_most_call = return_value_of_nested_call
                last_return_value_of_nested_call = return_value_of_nested_call

                if isinstance(arg_node, ast.keyword) and arg_node.arg is not None:
                    visual_args.append(
                        arg_node.arg + "=" + return_value_of_nested_call.left_hand_side
                    )
                else:
                    if hasattr(return_value_of_nested_call, "left_hand_side"):
                        visual_args.append(return_value_of_nested_call.left_hand_side)
                if hasattr(return_value_of_nested_call, "left_hand_side"):
                    rhs_vars.append(return_value_of_nested_call.left_hand_side)
            else:
                label = LabelVisitor()
                label.visit(arg_node)
                visual_args.append(label.result)

                vv = VarsVisitor()
                vv.visit(arg_node)
                rhs_vars.extend(vv.result)
        if last_return_value_of_nested_call:
            # connect other_inner to outer in e.g.
            # `scrypt.outer(scrypt.inner(image_name), scrypt.other_inner(image_name))`
            last_return_value_of_nested_call.connect(call_node)

        call_names = list(get_call_names(node.func))
        if len(call_names) > 1:
            # taint is a RHS variable (self) of taint.lower()
            rhs_vars.append(call_names[0])

        if len(visual_args) > 0:
            for arg in visual_args:
                RHS = RHS + arg + ", "
            # Replace the last ", " with a )
            RHS = RHS[: len(RHS) - 2] + ")"
        else:
            RHS = RHS + ")"
        call_node.label = LHS + " = " + RHS

        call_node.right_hand_side_variables = rhs_vars
        # Used in get_sink_args
        rhs_visitor = RHSVisitor()
        rhs_visitor.visit(node)
        call_node.args = rhs_visitor.result

        if blackbox:
            self.blackbox_assignments.add(call_node)

        self.connect_if_allowed(self.nodes[-1], call_node)
        self.nodes.append(call_node)

        return call_node
예제 #6
0
 def visit_Subscript(self, node):
     if isinstance(node.value, ast.Attribute):
         # foo.bar[1]
         self.result.append(list(get_call_names(node.value))[0])
     self.visit(node.value)
     self.slicev(node.slice)