def test_clear(self): dl = DiskList() dl.append('a') dl.append('b') self.assertEqual(len(dl), 2) dl.clear() self.assertEqual(len(dl), 0)
class html_file(OutputPlugin): """ Generate HTML report with identified vulnerabilities and log messages. :author: Andres Riancho (([email protected])) """ def __init__(self): OutputPlugin.__init__(self) # Internal variables self._initialized = False self._additional_info = DiskList(table_prefix='html_file') self._enabled_plugins = {} self.template_root = os.path.join(ROOT_PATH, 'plugins', 'output', 'html_file', 'templates') # User configured parameters self._verbose = False self._output_file_name = '~/report.html' self._template = os.path.join(self.template_root, 'complete.html') def debug(self, message, new_line=True): """ This method is called from the output object. The output object was called from a plugin or from the framework. This method should take an action for debug messages. """ if self._verbose: to_print = self._clean_string(message) self._append_additional_info(to_print, 'debug') def do_nothing(self, *args, **kwargs): pass information = vulnerability = do_nothing def error(self, message, new_line=True): """ This method is called from the output object. The output object was called from a plugin or from the framework. This method should take an action for error messages. """ to_print = self._clean_string(message) self._append_additional_info(to_print, 'error') def console(self, message, new_line=True): """ This method is used by the w3af console to print messages to the outside. """ to_print = self._clean_string(message) self._append_additional_info(to_print, 'console') def _append_additional_info(self, message, msg_type): """ Add a message to the debug table. :param message: The message to add to the table. It's in HTML. :param msg_type: The type of message """ now = time.localtime(time.time()) the_time = time.strftime("%c", now) self._additional_info.append((the_time, msg_type, message)) def set_options(self, option_list): """ Sets the Options given on the OptionList to self. The options are the result of a user entering some data on a window that was constructed using the XML Options that was retrieved from the plugin using get_options() This method MUST be implemented on every plugin. :return: No value is returned. """ self._output_file_name = option_list['output_file'].get_value() self._verbose = option_list['verbose'].get_value() self._template = option_list['template'].get_value() def get_options(self): """ :return: A list of option objects for this plugin. """ ol = OptionList() d = 'The path to the HTML template used to render the report.' o = opt_factory('template', self._template, d, INPUT_FILE) ol.add(o) d = 'File name where this plugin will write to' o = opt_factory('output_file', self._output_file_name, d, OUTPUT_FILE) ol.add(o) d = 'True if debug information will be appended to the report.' o = opt_factory('verbose', self._verbose, d, 'boolean') ol.add(o) return ol def log_enabled_plugins(self, plugins_dict, options_dict): """ This method is called from the output manager object. This method should take an action for the enabled plugins and their configuration. Usually, write the info to a file or print it somewhere. :param plugins_dict: A dict with all the plugin types and the enabled plugins for that type of plugin. :param options_dict: A dict with the options for every plugin. """ self._enabled_plugins = {} # TODO: Improve so it contains the plugin configuration too for plugin_type, enabled in plugins_dict.iteritems(): self._enabled_plugins[plugin_type] = enabled def end(self): try: self.flush() finally: self._additional_info.clear() self._enabled_plugins = {} def flush(self): """ This method is called when we want to write the data to the html, performs these main tasks: * Get the target URLs * Get the enabled plugins * Get the vulnerabilities and infos from the KB * Get the debug data * Send all the data to jinja2 for rendering the template """ target_urls = [t.url_string for t in cf.cf.get('targets')] target_domain = 'unknown' target_domains = cf.cf.get('target_domains') if target_domains and len(target_domains) > 0: target_domain = target_domains[0] enabled_plugins = self._enabled_plugins findings = kb.kb.get_all_findings_iter() debug_log = ((t, l, smart_unicode(m)) for (t, l, m) in self._additional_info) known_urls = kb.kb.get_all_known_urls() context = { 'target_urls': target_urls, 'target_domain': target_domain, 'enabled_plugins': enabled_plugins, 'findings': findings, 'debug_log': debug_log, 'known_urls': known_urls } # The file was verified to exist when setting the plugin configuration template_fh = file(os.path.expanduser(self._template), 'r') output_fh = file(os.path.expanduser(self._output_file_name), 'w') self._render_html_file(template_fh, context, output_fh) def _render_html_file(self, template_fh, context, output_fh): """ Renders the HTML file using the configured template. Separated as a method to be able to easily test. :param context: A dict containing target urls, enabled plugins, etc. :return: True on successful rendering """ severity_icon = functools.partial(get_severity_icon, self.template_root) env_config = { 'undefined': StrictUndefined, 'trim_blocks': True, 'autoescape': True, 'lstrip_blocks': True } try: jinja2_env = Environment(**env_config) except TypeError: # Kali uses a different jinja2 version, which doesn't have the same # Environment kwargs, so we first try with the version we expect # to have available, and then if it doesn't work apply this # workaround for Kali # # https://github.com/andresriancho/w3af/issues/9552 env_config.pop('lstrip_blocks') jinja2_env = Environment(**env_config) jinja2_env.filters['render_markdown'] = render_markdown jinja2_env.filters['request'] = request_dump jinja2_env.filters['response'] = response_dump jinja2_env.filters['severity_icon'] = severity_icon jinja2_env.filters['severity_text'] = get_severity_text jinja2_env.globals['get_current_date'] = get_current_date jinja2_env.loader = FileSystemLoader(self.template_root) template = jinja2_env.from_string(template_fh.read()) report_stream = template.stream(context) report_stream.enable_buffering(5) for report_section in report_stream: output_fh.write(report_section.encode('utf-8')) return True def get_long_desc(self): """ :return: A DETAILED description of the plugin functions and features. """ return """
class html_file(OutputPlugin): """ Generate HTML report with identified vulnerabilities and log messages. :author: Andres Riancho (([email protected])) """ def __init__(self): OutputPlugin.__init__(self) # Internal variables self._initialized = False self._additional_info = DiskList(table_prefix='html_file') self._enabled_plugins = {} self.template_root = os.path.join(ROOT_PATH, 'plugins', 'output', 'html_file', 'templates') # User configured parameters self._verbose = False self._output_file_name = '~/report.html' self._template = os.path.join(self.template_root, 'complete.html') def debug(self, message, new_line=True): """ This method is called from the output object. The output object was called from a plugin or from the framework. This method should take an action for debug messages. """ if self._verbose: to_print = self._clean_string(message) self._append_additional_info(to_print, 'debug') def do_nothing(self, *args, **kwargs): pass information = vulnerability = do_nothing def error(self, message, new_line=True): """ This method is called from the output object. The output object was called from a plugin or from the framework. This method should take an action for error messages. """ to_print = self._clean_string(message) self._append_additional_info(to_print, 'error') def console(self, message, new_line=True): """ This method is used by the w3af console to print messages to the outside. """ to_print = self._clean_string(message) self._append_additional_info(to_print, 'console') def _append_additional_info(self, message, msg_type): """ Add a message to the debug table. :param message: The message to add to the table. It's in HTML. :param msg_type: The type of message """ now = time.localtime(time.time()) the_time = time.strftime("%c", now) self._additional_info.append((the_time, msg_type, message)) def set_options(self, option_list): """ Sets the Options given on the OptionList to self. The options are the result of a user entering some data on a window that was constructed using the XML Options that was retrieved from the plugin using get_options() This method MUST be implemented on every plugin. :return: No value is returned. """ self._output_file_name = option_list['output_file'].get_value() self._verbose = option_list['verbose'].get_value() self._template = option_list['template'].get_value() def get_options(self): """ :return: A list of option objects for this plugin. """ ol = OptionList() d = 'The path to the HTML template used to render the report.' o = opt_factory('template', self._template, d, INPUT_FILE) ol.add(o) d = 'File name where this plugin will write to' o = opt_factory('output_file', self._output_file_name, d, OUTPUT_FILE) ol.add(o) d = 'True if debug information will be appended to the report.' o = opt_factory('verbose', self._verbose, d, 'boolean') ol.add(o) return ol def log_enabled_plugins(self, plugins_dict, options_dict): """ This method is called from the output manager object. This method should take an action for the enabled plugins and their configuration. Usually, write the info to a file or print it somewhere. :param plugins_dict: A dict with all the plugin types and the enabled plugins for that type of plugin. :param options_dict: A dict with the options for every plugin. """ self._enabled_plugins = {} # TODO: Improve so it contains the plugin configuration too for plugin_type, enabled in plugins_dict.iteritems(): self._enabled_plugins[plugin_type] = enabled def end(self): try: self.flush() finally: self._additional_info.clear() self._enabled_plugins = {} def flush(self): """ This method is called when we want to write the data to the html, performs these main tasks: * Get the target URLs * Get the enabled plugins * Get the vulnerabilities and infos from the KB * Get the debug data * Send all the data to jinja2 for rendering the template """ target_urls = [t.url_string for t in cf.cf.get('targets')] target_domain = cf.cf.get('target_domains')[0] enabled_plugins = self._enabled_plugins findings = kb.kb.get_all_findings() debug_log = ((t, l, smart_unicode(m)) for (t, l, m) in self._additional_info) known_urls = kb.kb.get_all_known_urls() context = {'target_urls': target_urls, 'target_domain': target_domain, 'enabled_plugins': enabled_plugins, 'findings': findings, 'debug_log': debug_log, 'known_urls': known_urls} # The file was verified to exist when setting the plugin configuration template_fh = file(os.path.expanduser(self._template), 'r') output_fh = file(os.path.expanduser(self._output_file_name), 'w') self._render_html_file(template_fh, context, output_fh) def _render_html_file(self, template_fh, context, output_fh): """ Renders the HTML file using the configured template. Separated as a method to be able to easily test. :param context: A dict containing target urls, enabled plugins, etc. :return: True on successful rendering """ severity_icon = functools.partial(get_severity_icon, self.template_root) env_config = {'undefined': StrictUndefined, 'trim_blocks': True, 'autoescape': True, 'lstrip_blocks': True} try: jinja2_env = Environment(**env_config) except TypeError: # Kali uses a different jinja2 version, which doesn't have the same # Environment kwargs, so we first try with the version we expect # to have available, and then if it doesn't work apply this # workaround for Kali # # https://github.com/andresriancho/w3af/issues/9552 env_config.pop('lstrip_blocks') jinja2_env = Environment(**env_config) jinja2_env.filters['render_markdown'] = render_markdown jinja2_env.filters['request'] = request_dump jinja2_env.filters['response'] = response_dump jinja2_env.filters['severity_icon'] = severity_icon jinja2_env.filters['severity_text'] = get_severity_text jinja2_env.globals['get_current_date'] = get_current_date jinja2_env.loader = FileSystemLoader(self.template_root) template = jinja2_env.from_string(template_fh.read()) try: rendered_output = template.render(context) except Exception, e: msg = u'Failed to render html report template. Exception: "%s"' om.out.error(msg % e) return False try: output_fh.write(rendered_output.encode('utf-8')) except Exception, e: msg = u'Failed to write html report to output file. Exception: "%s"' om.out.error(msg % e) return False