Source code for ptp.tools.wapiti.parser
"""
:synopsis: Specialized :class:`ptp.libptp.parser.AbstractParser` classes for the tool Wapiti.
.. moduleauthor:: Tao Sauvage
"""
import re
from lxml.etree import XMLSyntaxError
from ptp.libptp.exceptions import NotSupportedVersionError
from ptp.libptp.constants import UNKNOWN
from ptp.libptp.parser import XMLParser
from ptp.tools.wapiti.signatures import SIGNATURES
[docs]class WapitiXMLParser(XMLParser):
"""Wapiti XML specialized parser."""
__tool__ = 'wapiti'
__format__ = 'xml'
__version__ = r'^\D*(2\.3(\.[0-9]+)?)$'
[docs] @classmethod
def is_mine(cls, pathname, filename='*.xml', light=True, first=True):
"""Check if it can handle the report file.
:param str pathname: Path to the report directory.
:param str filename: Regex matching the report file.
:param bool light: `True` to only parse the ranking of the findings from the report.
:param bool first: Only process first file (``True``) or each file that matched (``False``).
:raises IOError: when the report file cannot be found.
:raises OSError: when the report file cannot be found.
:return: `True` if it supports the report, `False` otherwise.
:rtype: :class:`bool`
"""
try:
stream = cls.handle_file(pathname, filename, first=first)
except (TypeError, XMLSyntaxError):
return False
raw_metadata = stream.find('.//report_infos')
if raw_metadata is None:
return False
metadata = {el.get('name'): el.text for el in raw_metadata}
if metadata.get('generatorName', '').lower() != cls.__tool__:
return False
if not re.match(cls.__version__, metadata.get('generatorVersion', '')):
return False
return True
[docs] def parse_report(self):
"""Parse the results of the report.
:return: List of dicts where each one represents a discovery.
:rtype: :class:`list`
"""
section_vuln = self.stream.find('.//vulnerabilities')
if section_vuln is None:
return []
vulns = []
for category in section_vuln.findall('.//vulnerability'):
entries = category.find('.//entries')
if not len(entries):
pass
# Ensure that there are 'entry' sub-sections that represent the
# actual discoveries/vulnerabilities.
if len(entries.findall('.//entry')) and category.get('name') in SIGNATURES:
vulns.append({
'name': category.get('name'),
'ranking': SIGNATURES.get(category.get('name'), UNKNOWN),
'description': category.find('.//description').text})
self.vulns = vulns
return vulns
[docs]class Wapiti221XMLParser(XMLParser):
"""Wapiti XML specialized parser."""
#: :class:`str` -- Name of the tool.
__tool__ = 'wapiti'
#: :class:`str` -- Format of Wapiti reports it supports.
__format__ = 'xml'
#: :class:`list` -- Wapiti versions it supports.
__version__ = r'2\.2(\.[0-9]+)?'
[docs] @classmethod
def is_mine(cls, pathname, filename='*.xml', light=True, first=True):
"""Check if it is a supported Wapiti report.
:param str pathname: Path to the report directory.
:param str filename: Regex matching the report file.
:param bool light: `True` to only parse the ranking of the findings from the report.
:param bool first: Only process first file (``True``) or each file that matched (``False``).
:return: `True` if it supports the report, `False` otherwise.
:rtype: :class:`bool`
"""
try:
stream = cls.handle_file(pathname, filename, first=first)
except (IOError, XMLSyntaxError):
return False
raw_metadata = stream.find('.//generatedBy')
if raw_metadata is None:
return False
metadata = raw_metadata.get('id')
if not re.findall(cls.__version__, metadata, re.IGNORECASE):
return False
return True
[docs] def parse_report(self):
"""Parse the results of the report.
:return: List of dicts where each one represents a discovery.
:rtype: :class:`list`
"""
section_vuln = self.stream.find('.//bugTypeList')
vulns = []
for category in section_vuln.findall('.//bugType'):
entries = category.find('.//bugList')
if not len(entries):
pass
# Ensure that there are 'entry' sub-sections that represent the actual discoveries/vulnerabilities.
if len(entries.findall('.//bug')) and category.get('name') in SIGNATURES:
vulns.append({
'name': category.get('name'),
'ranking': SIGNATURES.get(category.get('name'), UNKNOWN),
'description': category.find('.//description').text})
self.vulns = vulns
return vulns