Beispiel #1
0
class WebServerProcessTests(BaseTestCase):
    def before(self):
        self.server = WebServerProcess([(r"/", TestHandler)])
        self.server.start()

    def after(self):
        self.server.stop()

    def test_that_the_process_starts_succesfully(self):
        assert_that(self.server.is_alive())

    def test_that_the_process_stops_succesfully(self):
        self.server.stop()
        assert_that(self.server.is_alive(), is_(False))

    def test_that_the_server_works(self):
        connection = httplib.HTTPConnection(host="localhost", port=PORT)
        connection.request("GET", "/")
        response = connection.getresponse().read()

        assert_that(response, equal_to(MESSAGE))
Beispiel #2
0
class WebServerProcessTests(BaseTestCase):

    def before(self):
        self.server = WebServerProcess([(r"/", TestHandler)])
        self.server.start()

    def after(self):
        self.server.stop()

    def test_that_the_process_starts_succesfully(self):
        assert_that(self.server.is_alive())

    def test_that_the_process_stops_succesfully(self):
        self.server.stop()
        assert_that(self.server.is_alive(), is_(False))

    def test_that_the_server_works(self):
        connection = httplib.HTTPConnection(host="localhost", port=PORT)
        connection.request("GET", "/")
        response = connection.getresponse().read()

        assert_that(response, equal_to(MESSAGE))
class WebPluginTestCase(BaseTestCase):

    TARGET = "localhost:8888"
    DYNAMIC_METHOD_REGEX = "^set_(head|get|post|put|delete|options|connect)_response"
    ANCHOR_PATTERN = "href=\"%s\""
    OWTF_REVIEW_FOLDER = "owtf_review"
    LOG_FILE = "logfile"
    NOT_INTERACTIVE = " -i no "
    FROM_FILE_SUFFIX = "_from_file"
    CORE_PROXY_NAME = "core_instance_proxy"

    core_instance_proxy = None

    @classmethod
    def setUpClass(cls):
        cls.clean_temp_files()  # Cleans logfile and owtf_review/ before starting
        cls.core_instance_proxy = ExpensiveResourceProxy(CoreInitialiser("http://" + cls.TARGET))
        super(WebPluginTestCase, cls).setUpClass()

    @classmethod
    def tearDownClass(cls):
        try:
            if cls.core_instance_can_be_destroyed():
                core = cls.core_instance_proxy.get_instance()
                core.Finish(Report=False)  # Avoid finish reporting, it's faster
        except SystemExit:
            pass  # Exit is invoked from the core.Finish method
        finally:
            cls.clean_temp_files()  # Cleans logfile and owtf_review/ before finishing
            super(WebPluginTestCase, cls).tearDownClass()

    @classmethod
    def clean_temp_files(cls):
        if os.path.isdir(cls.OWTF_REVIEW_FOLDER):
            shutil.rmtree(cls.OWTF_REVIEW_FOLDER)
        if os.path.isfile(cls.LOG_FILE):
            os.remove(cls.LOG_FILE)

    @classmethod
    def core_instance_can_be_destroyed(cls):
        core_instance_proxy_is_defined = hasattr(cls, cls.CORE_PROXY_NAME)
        is_not_none = (getattr(cls, cls.CORE_PROXY_NAME) is not None)
        return core_instance_proxy_is_defined and is_not_none

    def setUp(self):
        """Initialise the WebPluginTestCase instance variables."""
        self.responses = {}
        self.server = None
        self.custom_handlers = []
        self.core_instance = self.core_instance_proxy.get_instance()
        self.owtf_output = ""
        super(WebPluginTestCase, self).setUp()

    def tearDown(self):
        if self.server is not None:
            self.stop_server()
        super(WebPluginTestCase, self).tearDown()

    def __getattr__(self, name):
        """
            If the method name matches with set_post_response, set_put_response,
            set_post_response_from_file, etc. generates a dynamic method.
        """
        dynamic_method_matcher = re.match(self.DYNAMIC_METHOD_REGEX, name)
        if dynamic_method_matcher is not None:
            method_name = dynamic_method_matcher.group(1)
            from_file = name.endswith(self.FROM_FILE_SUFFIX)
            return self.generate_callable_for_set_response(method_name, from_file)
        else:
            raise AttributeError("'WebPluginTestCase' object has no attribute '" + name + "'")

    def generate_callable_for_set_response(self, method_name, from_file):
        """Returns a function that will be called to set a response."""
        def dynamic_method(path, content="", headers={}, status_code=200):
                if from_file:
                    self.set_response_from_file(path, content, headers, method_name, status_code)
                else:
                    self.set_response(path, content, headers, method_name, status_code)
        return dynamic_method

    def set_response(self, path, content="", headers={}, method="get", status_code=200):
        """
            Sets the response for the server in the given path. Optionally, it
            is possible to specify the headers to be changed, the HTTP method
            to answer to, and the response HTTP status code.
        """
        if not (path in self.responses):
            self.responses[path] = {}
        self.responses[path][method] = {"content": content,
                                        "headers": headers,
                                        "code": status_code}

    def set_response_from_file(self, path, file_path, headers={}, method="get", status_code=200):
        """
            Sets the response for the server in the given path using the
            content of a file. Optionally, it is possible to specify the
            headers to be changed, the HTTP method to answer to, and the
            response HTTP status code.
        """
        response_file = open(file_path, "r")
        self.set_response(path, response_file.read(), headers, method, status_code)
        response_file.close()

    def set_custom_handler(self, path, handler_class, init_params={}):
        """Allows the use of an already implemented tornado RequestHandler"""
        self.custom_handlers.append((path, handler_class, init_params),)

    def start_server(self):
        """Creates a server process with the provided handlers and starts it."""
        autogenerated_handlers = self.build_handlers()
        handlers = autogenerated_handlers + self.custom_handlers
        self.server = WebServerProcess(handlers)
        self.server.start()

    def build_handlers(self):
        """
            For each recorded response, generates a (path, handler) tuple which
            will be passed to the Tornado web server.
        """
        handlers = []
        handler_builder = HandlerBuilder()
        for path, params in self.responses.items():
            handlers.append((path, handler_builder.get_handler(params)))
        return handlers

    def stop_server(self):
        self.server.stop()

    def owtf(self, args_string=""):
        """Runs OWTF against the test server and stores the result."""
        processed_options = self.process_options(args_string + self.NOT_INTERACTIVE + self.TARGET)
        plugin_dir, plugin = self.get_dir_and_plugin_from_options(processed_options)
        result = self.run_plugin(plugin_dir, plugin)
        self.owtf_output = "\n".join(result[:])

    def process_options(self, options):
        """Parses the command line options passed to the owtf method."""
        args = shlex.split(options)
        return ProcessOptions(self.core_instance, args)

    def get_dir_and_plugin_from_options(self, options):
        """
            Performs a plugins search through the plugin handler with the given
            criteria.
        """
        criteria = self.get_criteria_from_options(options)
        directory = os.path.abspath("../plugins/" + criteria["Group"])
        return directory, self.core_instance.Config.Plugin.GetPlugins(criteria)[0]

    def get_criteria_from_options(self, options):
        return {"Group": options["PluginGroup"],
                "Type": options["PluginType"],
                "Name": options["OnlyPlugins"][0]}

    def run_plugin(self, plugin_dir, plugin):
        """
            Runs a plugin with the current core instance and returns a list 
            with the output of the plugin and the stdout content.
        """
        result = []
        self.init_stdout_recording()
        try:
            result.append(str(self.core_instance.PluginHandler.ProcessPlugin(plugin_dir, plugin, {})))
        except:
            result.append(str(sys.exc_info()[0]))
        finally:
            stdout_output = self.get_recorded_stdout_and_close()
            result.append(str(stdout_output))
        return result

    def assert_that_output_contains(self, substring, times=None):
        assert_that(self.owtf_output, contains_string(substring))
        if times is not None:
            assert_that(self.owtf_output.count(substring), equal_to(times))

    def generate_random_token(self, length=15):
        """Useful to identify the specified content in the plugin output."""
        charset = string.ascii_uppercase + string.ascii_lowercase + string.digits
        choices = []
        for i in range(length):
            choices.append(random.choice(charset))
        return ''.join(choices)

    def get_url(self, web_path, https=False):
        protocol = "https" if https else "http"
        return protocol + "://" + self.TARGET + web_path

    def visit_url(self, url, method=''):
        """
            Grep plugins don't make requests, so the transactions have to be
            stored previously. By using the core.Requester object, all the
            transactions will get logged.
        """
        self.core_instance.Requester.GetTransaction(True, url, Method=method)

    def generate_headers_with_tokens(self, header_names):
        headers = {}
        for name in header_names:
            headers[name] = self.generate_random_token()
        return headers
class WebPluginTestCase(BaseTestCase):

    TARGET = "localhost:8888"
    DYNAMIC_METHOD_REGEX = "^set_(head|get|post|put|delete|options|connect)_response"
    ANCHOR_PATTERN = "href=\"%s\""
    OWTF_REVIEW_FOLDER = "owtf_review"
    LOG_FILE = "logfile"
    NOT_INTERACTIVE = " -i no "
    FROM_FILE_SUFFIX = "_from_file"
    CORE_PROXY_NAME = "core_instance_proxy"

    core_instance_proxy = None

    @classmethod
    def setUpClass(cls):
        cls.clean_temp_files(
        )  # Cleans logfile and owtf_review/ before starting
        cls.core_instance_proxy = ExpensiveResourceProxy(
            CoreInitialiser("http://" + cls.TARGET))
        super(WebPluginTestCase, cls).setUpClass()

    @classmethod
    def tearDownClass(cls):
        try:
            if cls.core_instance_can_be_destroyed():
                core = cls.core_instance_proxy.get_instance()
                core.Finish(
                    Report=False)  # Avoid finish reporting, it's faster
        except SystemExit:
            pass  # Exit is invoked from the core.Finish method
        finally:
            cls.clean_temp_files(
            )  # Cleans logfile and owtf_review/ before finishing
            super(WebPluginTestCase, cls).tearDownClass()

    @classmethod
    def clean_temp_files(cls):
        if os.path.isdir(cls.OWTF_REVIEW_FOLDER):
            shutil.rmtree(cls.OWTF_REVIEW_FOLDER)
        if os.path.isfile(cls.LOG_FILE):
            os.remove(cls.LOG_FILE)

    @classmethod
    def core_instance_can_be_destroyed(cls):
        core_instance_proxy_is_defined = hasattr(cls, cls.CORE_PROXY_NAME)
        is_not_none = (getattr(cls, cls.CORE_PROXY_NAME) is not None)
        return core_instance_proxy_is_defined and is_not_none

    def setUp(self):
        """Initialise the WebPluginTestCase instance variables."""
        self.responses = {}
        self.server = None
        self.custom_handlers = []
        self.core_instance = self.core_instance_proxy.get_instance()
        self.owtf_output = ""
        super(WebPluginTestCase, self).setUp()

    def tearDown(self):
        if self.server is not None:
            self.stop_server()
        super(WebPluginTestCase, self).tearDown()

    def __getattr__(self, name):
        """
            If the method name matches with set_post_response, set_put_response,
            set_post_response_from_file, etc. generates a dynamic method.
        """
        dynamic_method_matcher = re.match(self.DYNAMIC_METHOD_REGEX, name)
        if dynamic_method_matcher is not None:
            method_name = dynamic_method_matcher.group(1)
            from_file = name.endswith(self.FROM_FILE_SUFFIX)
            return self.generate_callable_for_set_response(
                method_name, from_file)
        else:
            raise AttributeError(
                "'WebPluginTestCase' object has no attribute '" + name + "'")

    def generate_callable_for_set_response(self, method_name, from_file):
        """Returns a function that will be called to set a response."""
        def dynamic_method(path, content="", headers={}, status_code=200):
            if from_file:
                self.set_response_from_file(path, content, headers,
                                            method_name, status_code)
            else:
                self.set_response(path, content, headers, method_name,
                                  status_code)

        return dynamic_method

    def set_response(self,
                     path,
                     content="",
                     headers={},
                     method="get",
                     status_code=200):
        """
            Sets the response for the server in the given path. Optionally, it
            is possible to specify the headers to be changed, the HTTP method
            to answer to, and the response HTTP status code.
        """
        if not (path in self.responses):
            self.responses[path] = {}
        self.responses[path][method] = {
            "content": content,
            "headers": headers,
            "code": status_code
        }

    def set_response_from_file(self,
                               path,
                               file_path,
                               headers={},
                               method="get",
                               status_code=200):
        """
            Sets the response for the server in the given path using the
            content of a file. Optionally, it is possible to specify the
            headers to be changed, the HTTP method to answer to, and the
            response HTTP status code.
        """
        response_file = open(file_path, "r")
        self.set_response(path, response_file.read(), headers, method,
                          status_code)
        response_file.close()

    def set_custom_handler(self, path, handler_class, init_params={}):
        """Allows the use of an already implemented tornado RequestHandler"""
        self.custom_handlers.append((path, handler_class, init_params), )

    def start_server(self):
        """Creates a server process with the provided handlers and starts it."""
        autogenerated_handlers = self.build_handlers()
        handlers = autogenerated_handlers + self.custom_handlers
        self.server = WebServerProcess(handlers)
        self.server.start()

    def build_handlers(self):
        """
            For each recorded response, generates a (path, handler) tuple which
            will be passed to the Tornado web server.
        """
        handlers = []
        handler_builder = HandlerBuilder()
        for path, params in self.responses.items():
            handlers.append((path, handler_builder.get_handler(params)))
        return handlers

    def stop_server(self):
        self.server.stop()

    def owtf(self, args_string=""):
        """Runs OWTF against the test server and stores the result."""
        processed_options = self.process_options(args_string +
                                                 self.NOT_INTERACTIVE +
                                                 self.TARGET)
        plugin_dir, plugin = self.get_dir_and_plugin_from_options(
            processed_options)
        result = self.run_plugin(plugin_dir, plugin)
        self.owtf_output = "\n".join(result[:])

    def process_options(self, options):
        """Parses the command line options passed to the owtf method."""
        args = shlex.split(options)
        return ProcessOptions(self.core_instance, args)

    def get_dir_and_plugin_from_options(self, options):
        """
            Performs a plugins search through the plugin handler with the given
            criteria.
        """
        criteria = self.get_criteria_from_options(options)
        directory = os.path.abspath("../plugins/" + criteria["Group"])
        return directory, self.core_instance.Config.Plugin.GetPlugins(
            criteria)[0]

    def get_criteria_from_options(self, options):
        return {
            "Group": options["PluginGroup"],
            "Type": options["PluginType"],
            "Name": options["OnlyPlugins"][0]
        }

    def run_plugin(self, plugin_dir, plugin):
        """
            Runs a plugin with the current core instance and returns a list 
            with the output of the plugin and the stdout content.
        """
        result = []
        self.init_stdout_recording()
        try:
            result.append(
                str(
                    self.core_instance.PluginHandler.ProcessPlugin(
                        plugin_dir, plugin, {})))
        except:
            result.append(str(sys.exc_info()[0]))
        finally:
            stdout_output = self.get_recorded_stdout_and_close()
            result.append(str(stdout_output))
        return result

    def assert_that_output_contains(self, substring, times=None):
        assert_that(self.owtf_output, contains_string(substring))
        if times is not None:
            assert_that(self.owtf_output.count(substring), equal_to(times))

    def generate_random_token(self, length=15):
        """Useful to identify the specified content in the plugin output."""
        charset = string.ascii_uppercase + string.ascii_lowercase + string.digits
        choices = []
        for i in range(length):
            choices.append(random.choice(charset))
        return ''.join(choices)

    def get_url(self, web_path, https=False):
        protocol = "https" if https else "http"
        return protocol + "://" + self.TARGET + web_path

    def visit_url(self, url, method=''):
        """
            Grep plugins don't make requests, so the transactions have to be
            stored previously. By using the core.Requester object, all the
            transactions will get logged.
        """
        self.core_instance.Requester.GetTransaction(True, url, Method=method)

    def generate_headers_with_tokens(self, header_names):
        headers = {}
        for name in header_names:
            headers[name] = self.generate_random_token()
        return headers
Beispiel #5
0
class WebPluginTestCase(PluginTestCase):

    HOST = "localhost"
    IP = "127.0.0.1"
    PORT = "8888"
    TARGET = "%s:%s" % (HOST, PORT)
    ANCHOR_PATTERN = "href=\"%s\""
    PASSIVE_LINK_PATTERN = (ANCHOR_PATTERN % (".*" + HOST + "|" + IP + "|\?.+=" + HOST + ".*"))
    DYNAMIC_METHOD_REGEX = "^set_(head|get|post|put|delete|options|connect)_response"
    FROM_FILE_SUFFIX = "_from_file"

    @classmethod
    def setUpClass(cls):
        super(WebPluginTestCase, cls).setUpClass()

    @classmethod
    def tearDownClass(cls):
        super(WebPluginTestCase, cls).tearDownClass()

    def setUp(self):
        """Initialise the WebPluginTestCase instance variables."""
        super(WebPluginTestCase, self).setUp()
        self.responses = {}
        self.server = None
        self.custom_handlers = []

    def tearDown(self):
        if self.server is not None:
            self.stop_server()
        super(WebPluginTestCase, self).tearDown()

    def __getattr__(self, name):
        """
            If the method name matches with set_post_response, set_put_response,
            set_post_response_from_file, etc. generates a dynamic method.
        """
        dynamic_method_matcher = re.match(self.DYNAMIC_METHOD_REGEX, name)
        if dynamic_method_matcher is not None:
            method_name = dynamic_method_matcher.group(1)
            from_file = name.endswith(self.FROM_FILE_SUFFIX)
            return self.generate_callable_for_set_response(method_name, from_file)
        else:
            raise AttributeError("'WebPluginTestCase' object has no attribute '" + name + "'")

    def generate_callable_for_set_response(self, method_name, from_file):
        """Returns a function that will be called to set a response."""
        def dynamic_method(path, content="", headers={}, status_code=200):
                if from_file:
                    self.set_response_from_file(path, content, headers, method_name, status_code)
                else:
                    self.set_response(path, content, headers, method_name, status_code)
        return dynamic_method

    def set_response(self, path, content="", headers={}, method="get", status_code=200):
        """
            Sets the response for the server in the given path. Optionally, it
            is possible to specify the headers to be changed, the HTTP method
            to answer to, and the response HTTP status code.
        """
        if not (path in self.responses):
            self.responses[path] = {}
        self.responses[path][method] = {"content": content,
                                        "headers": headers,
                                        "code": status_code}

    def set_response_from_file(self, path, file_path, headers={}, method="get", status_code=200):
        """
            Sets the response for the server in the given path using the
            content of a file. Optionally, it is possible to specify the
            headers to be changed, the HTTP method to answer to, and the
            response HTTP status code.
        """
        response_file = open(file_path, "r")
        self.set_response(path, response_file.read(), headers, method, status_code)
        response_file.close()

    def set_custom_handler(self, path, handler_class, init_params={}):
        """Allows the use of an already implemented tornado RequestHandler"""
        self.custom_handlers.append((path, handler_class, init_params),)

    def start_server(self):
        """Creates a server process with the provided handlers and starts it."""
        autogenerated_handlers = self.build_handlers()
        handlers = autogenerated_handlers + self.custom_handlers
        self.server = WebServerProcess(handlers)
        self.server.start()

    def build_handlers(self):
        """
            For each recorded response, generates a (path, handler) tuple which
            will be passed to the Tornado web server.
        """
        handlers = []
        handler_builder = HandlerBuilder()
        for path, params in self.responses.items():
            handlers.append((path, handler_builder.get_handler(params)))
        return handlers

    def stop_server(self):
        self.server.stop()

    def check_link_generation_for_resources(self, resources_name):
        """Check that the given resources are correctly linked in the output of the plugin."""
        if not isinstance(resources_name, list):
            resources_name = [resources_name]
        for resource in resources_name:
            times = len(self.get_resources(resource))
            # The pattern should match at least as many times as resources for that name
            self.assert_that_output_matches_more_than(self.PASSIVE_LINK_PATTERN, times)

    def get_url(self, web_path, https=False):
        """Get an absolute URL from a relative URL."""
        protocol = "https" if https else "http"
        return protocol + "://" + self.TARGET + web_path

    def visit_url(self, url, method=''):
        """
            Grep plugins don't make requests, so the transactions have to be
            stored previously. By using the core.Requester object, all the
            transactions will get logged.
        """
        self.core_instance.Requester.GetTransaction(True, url, Method=method)

    def generate_headers_with_tokens(self, header_names):
        """
            Generates a dictionary using the header_names as keys and random strings as
            values.
        """
        headers = {}
        for name in header_names:
            headers[name] = self.generate_random_token()
        return headers
Beispiel #6
0
class WebPluginTestCase(PluginTestCase):

    HOST = "localhost"
    IP = "127.0.0.1"
    PORT = "8888"
    TARGET = "%s:%s" % (HOST, PORT)
    ANCHOR_PATTERN = "href=\"%s\""
    PASSIVE_LINK_PATTERN = (ANCHOR_PATTERN %
                            (".*" + HOST + "|" + IP + "|\?.+=" + HOST + ".*"))
    DYNAMIC_METHOD_REGEX = "^set_(head|get|post|put|delete|options|connect)_response"
    FROM_FILE_SUFFIX = "_from_file"

    @classmethod
    def setUpClass(cls):
        super(WebPluginTestCase, cls).setUpClass()

    @classmethod
    def tearDownClass(cls):
        super(WebPluginTestCase, cls).tearDownClass()

    def setUp(self):
        """Initialise the WebPluginTestCase instance variables."""
        super(WebPluginTestCase, self).setUp()
        self.responses = {}
        self.server = None
        self.custom_handlers = []

    def tearDown(self):
        if self.server is not None:
            self.stop_server()
        super(WebPluginTestCase, self).tearDown()

    def __getattr__(self, name):
        """
            If the method name matches with set_post_response, set_put_response,
            set_post_response_from_file, etc. generates a dynamic method.
        """
        dynamic_method_matcher = re.match(self.DYNAMIC_METHOD_REGEX, name)
        if dynamic_method_matcher is not None:
            method_name = dynamic_method_matcher.group(1)
            from_file = name.endswith(self.FROM_FILE_SUFFIX)
            return self.generate_callable_for_set_response(
                method_name, from_file)
        else:
            raise AttributeError(
                "'WebPluginTestCase' object has no attribute '" + name + "'")

    def generate_callable_for_set_response(self, method_name, from_file):
        """Returns a function that will be called to set a response."""
        def dynamic_method(path, content="", headers={}, status_code=200):
            if from_file:
                self.set_response_from_file(path, content, headers,
                                            method_name, status_code)
            else:
                self.set_response(path, content, headers, method_name,
                                  status_code)

        return dynamic_method

    def set_response(self,
                     path,
                     content="",
                     headers={},
                     method="get",
                     status_code=200):
        """
            Sets the response for the server in the given path. Optionally, it
            is possible to specify the headers to be changed, the HTTP method
            to answer to, and the response HTTP status code.
        """
        if not (path in self.responses):
            self.responses[path] = {}
        self.responses[path][method] = {
            "content": content,
            "headers": headers,
            "code": status_code
        }

    def set_response_from_file(self,
                               path,
                               file_path,
                               headers={},
                               method="get",
                               status_code=200):
        """
            Sets the response for the server in the given path using the
            content of a file. Optionally, it is possible to specify the
            headers to be changed, the HTTP method to answer to, and the
            response HTTP status code.
        """
        response_file = open(file_path, "r")
        self.set_response(path, response_file.read(), headers, method,
                          status_code)
        response_file.close()

    def set_custom_handler(self, path, handler_class, init_params={}):
        """Allows the use of an already implemented tornado RequestHandler"""
        self.custom_handlers.append((path, handler_class, init_params), )

    def start_server(self):
        """Creates a server process with the provided handlers and starts it."""
        autogenerated_handlers = self.build_handlers()
        handlers = autogenerated_handlers + self.custom_handlers
        self.server = WebServerProcess(handlers)
        self.server.start()

    def build_handlers(self):
        """
            For each recorded response, generates a (path, handler) tuple which
            will be passed to the Tornado web server.
        """
        handlers = []
        handler_builder = HandlerBuilder()
        for path, params in self.responses.items():
            handlers.append((path, handler_builder.get_handler(params)))
        return handlers

    def stop_server(self):
        self.server.stop()

    def check_link_generation_for_resources(self, resources_name):
        """Check that the given resources are correctly linked in the output of the plugin."""
        if not isinstance(resources_name, list):
            resources_name = [resources_name]
        for resource in resources_name:
            times = len(self.get_resources(resource))
            # The pattern should match at least as many times as resources for that name
            self.assert_that_output_matches_more_than(
                self.PASSIVE_LINK_PATTERN, times)

    def get_url(self, web_path, https=False):
        """Get an absolute URL from a relative URL."""
        protocol = "https" if https else "http"
        return protocol + "://" + self.TARGET + web_path

    def visit_url(self, url, method=''):
        """
            Grep plugins don't make requests, so the transactions have to be
            stored previously. By using the core.Requester object, all the
            transactions will get logged.
        """
        self.core_instance.Requester.GetTransaction(True, url, Method=method)

    def generate_headers_with_tokens(self, header_names):
        """
            Generates a dictionary using the header_names as keys and random strings as
            values.
        """
        headers = {}
        for name in header_names:
            headers[name] = self.generate_random_token()
        return headers