import re

import gixy
from gixy.core.context import get_context
from gixy.core.variable import compile_script
from gixy.plugins.plugin import Plugin


class ssrf(Plugin):
    """
    Insecure examples:
        location ~ /proxy/(.*)/(.*)/(.*)$ {
            set $scheme $1;
            set $host $2;
            set $path $3;
            proxy_pass $scheme://$host/$path;
        }

        location /proxy/ {
            proxy_pass $arg_some;
        }
    """

    summary = "Possible SSRF (Server Side Request Forgery) vulnerability."
    severity = gixy.severity.HIGH
    description = "The configuration may allow an attacker to make arbitrary requests from the vulnerable server."
    help_url = "https://gixy.io/plugins/ssrf/"
    directives = ["proxy_pass"]

    def __init__(self, config):
        super(ssrf, self).__init__(config)
        self.parse_uri_re = re.compile(r"(?P<scheme>[^?#/)]+://)?(?P<host>[^?#/)]+)")

    def audit(self, directive):
        value = directive.args[0]
        if not value:
            return

        context = get_context()
        if context.block.name == "location" and context.block.is_internal:
            # Exclude internal locations
            return

        parsed = self.parse_uri_re.match(value)
        if not parsed:
            return

        res = self._check_script(parsed.group("scheme"), directive)
        if not res:
            self._check_script(parsed.group("host"), directive)

    def _check_script(self, script, directive):
        for var in compile_script(script):
            if var.must_contain("/"):
                # Skip variable checks
                return False
            if var.can_contain("."):
                # Yay! Our variable can contain any symbols!
                reason = "Variable `${var}` can contain untrusted user input.".format(
                    var=var.name
                )
                self.add_issue(directive=[directive] + var.providers, reason=reason)

                return True
        return False
