mirror of
https://github.com/nmap/nmap.git
synced 2026-05-13 08:46:45 +00:00
2930 - Sanitize sorting and comparisons
Change 1: added missing less-than method for sorting The ScriptResult class in nmap/ndiff/ndiff.py is not specifying a __lt__ (less-than) method by which the precedence of its instances could be compared when sorting. This can lead to an error on the sort method call in line 1420. self.current_port.script_results.sort() The error occurs when multiple script results are exported to xml and parsed by the underlying xml.sax library. Change 2: added missing len function call when comparing list to int in conditional statement Conditional statement in line 983 throws a TypeError when script results have differences for the same port. Change 3: implemented host object sorting and comparative logic in OO style The Host class does now define the equals and less-than methods. This allows for the get_id() method of the Host class to be treated as private and comparisons to be made directly against between instances. The `Scan` objects list of addresses is not being mutated in-place, but instead wrapped in a `sorted` function call and passed to `host_pairs()` in line 552. Change 4: Removed OO boilerplate Python3 classes inherit from the object base class implicitly, therefore `class A (object)` can be reduced to `class A`. See accepted answer in https://stackoverflow.com/questions/4015417/why-do-python-classes-inherit-object and In Python3 the not-equals method implicitly delegates to the equals method. Therefore it is unnecessary to explicitly define `__ne__` as the negation of the corresponding `__eq__` method. See https://docs.python.org/3/reference/datamodel.html#object.__eq__
This commit is contained in:
parent
66bbf3dc96
commit
a32d26fad7
1 changed files with 25 additions and 32 deletions
|
|
@ -41,7 +41,7 @@ class OverrideEntityResolver(xml.sax.handler.EntityResolver):
|
|||
return OverrideEntityResolver.empty
|
||||
|
||||
|
||||
class Scan(object):
|
||||
class Scan:
|
||||
"""A single Nmap scan, corresponding to a single invocation of Nmap. It is
|
||||
a container for a list of hosts. It also has utility methods to load itself
|
||||
from an Nmap XML file."""
|
||||
|
|
@ -55,9 +55,6 @@ class Scan(object):
|
|||
self.pre_script_results = []
|
||||
self.post_script_results = []
|
||||
|
||||
def sort_hosts(self):
|
||||
self.hosts.sort(key=lambda h: h.get_id())
|
||||
|
||||
def load(self, f):
|
||||
"""Load a scan from the Nmap XML in the file-like object f."""
|
||||
parser = xml.sax.make_parser()
|
||||
|
|
@ -107,7 +104,7 @@ class Scan(object):
|
|||
return frag
|
||||
|
||||
|
||||
class Host(object):
|
||||
class Host:
|
||||
"""A single host, with a state, addresses, host names, a dict mapping port
|
||||
specs to Ports, and a list of OS matches. Host states are strings, or None
|
||||
for "unknown"."""
|
||||
|
|
@ -120,7 +117,7 @@ class Host(object):
|
|||
self.os = []
|
||||
self.script_results = []
|
||||
|
||||
def get_id(self):
|
||||
def _get_id(self):
|
||||
"""Return an id that is used to determine if hosts are "the same"
|
||||
across scans."""
|
||||
hid = None
|
||||
|
|
@ -130,6 +127,12 @@ class Host(object):
|
|||
return (hid or " " * 40) + str(sorted(self.hostnames)[0])
|
||||
return hid or id(self)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self._get_id() == other._get_id()
|
||||
|
||||
def __lt__(self, other):
|
||||
return self._get_id() < other._get_id()
|
||||
|
||||
def format_name(self):
|
||||
"""Return a human-readable identifier for this host."""
|
||||
address_s = ", ".join(a.s for a in sorted(self.addresses))
|
||||
|
|
@ -237,16 +240,13 @@ class Host(object):
|
|||
return frag
|
||||
|
||||
|
||||
class Address(object):
|
||||
class Address:
|
||||
def __init__(self, s):
|
||||
self.s = s
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.sort_key() == other.sort_key()
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.sort_key())
|
||||
|
||||
|
|
@ -303,7 +303,7 @@ class MACAddress(Address):
|
|||
return (2, self.s)
|
||||
|
||||
|
||||
class Port(object):
|
||||
class Port:
|
||||
"""A single port, consisting of a port specification, a state, and a
|
||||
service version. A specification, or "spec," is the 2-tuple (number,
|
||||
protocol). So (10, "tcp") corresponds to the port 10/tcp. Port states are
|
||||
|
|
@ -346,7 +346,7 @@ class Port(object):
|
|||
return frag
|
||||
|
||||
|
||||
class Service(object):
|
||||
class Service:
|
||||
"""A service version as determined by -sV scan. Also contains the looked-up
|
||||
port name if -sV wasn't used."""
|
||||
def __init__(self):
|
||||
|
|
@ -368,9 +368,6 @@ class Service(object):
|
|||
and self.version == other.version \
|
||||
and self.extrainfo == other.extrainfo
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def name_string(self):
|
||||
parts = []
|
||||
if self.tunnel is not None:
|
||||
|
|
@ -421,8 +418,8 @@ class ScriptResult(object):
|
|||
def __eq__(self, other):
|
||||
return self.id == other.id and self.output == other.output
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
def __lt__(self, other):
|
||||
return self.id < other.id
|
||||
|
||||
def get_lines(self):
|
||||
result = []
|
||||
|
|
@ -505,13 +502,12 @@ def host_pairs(a, b):
|
|||
Otherwise the one with the smaller id is returned, with an empty host as
|
||||
its counterpart, and the one with the higher id will remain in its list for
|
||||
a later iteration."""
|
||||
i = 0
|
||||
j = 0
|
||||
i,j = 0,0
|
||||
while i < len(a) and j < len(b):
|
||||
if a[i].get_id() < b[j].get_id():
|
||||
if a[i] < b[j]:
|
||||
yield a[i], Host()
|
||||
i += 1
|
||||
elif a[i].get_id() > b[j].get_id():
|
||||
elif a[i] > b[j]:
|
||||
yield Host(), b[j]
|
||||
j += 1
|
||||
else:
|
||||
|
|
@ -526,7 +522,7 @@ def host_pairs(a, b):
|
|||
j += 1
|
||||
|
||||
|
||||
class ScanDiff(object):
|
||||
class ScanDiff:
|
||||
"""An abstract class for different diff output types. Subclasses must
|
||||
define various output methods."""
|
||||
def __init__(self, scan_a, scan_b, f=sys.stdout):
|
||||
|
|
@ -537,11 +533,7 @@ class ScanDiff(object):
|
|||
self.f = f
|
||||
|
||||
def output(self):
|
||||
self.scan_a.sort_hosts()
|
||||
self.scan_b.sort_hosts()
|
||||
|
||||
self.output_beginning()
|
||||
|
||||
pre_script_result_diffs = ScriptResultDiff.diff_lists(
|
||||
self.scan_a.pre_script_results, self.scan_b.pre_script_results)
|
||||
self.output_pre_scripts(pre_script_result_diffs)
|
||||
|
|
@ -549,7 +541,8 @@ class ScanDiff(object):
|
|||
cost = 0
|
||||
# Currently we never consider diffing hosts with a different id
|
||||
# (address or host name), which could lead to better diffs.
|
||||
for host_a, host_b in host_pairs(self.scan_a.hosts, self.scan_b.hosts):
|
||||
for host_a, host_b in host_pairs(sorted(self.scan_a.hosts),
|
||||
sorted(self.scan_b.hosts)):
|
||||
h_diff = HostDiff(host_a, host_b)
|
||||
cost += h_diff.cost
|
||||
if h_diff.cost > 0 or verbose:
|
||||
|
|
@ -657,7 +650,7 @@ class ScanDiffXML(ScanDiff):
|
|||
self.writer.endDocument()
|
||||
|
||||
|
||||
class HostDiff(object):
|
||||
class HostDiff:
|
||||
"""A diff of two Hosts. It contains the two hosts, variables describing
|
||||
what changed, and a list of PortDiffs and OS differences."""
|
||||
def __init__(self, host_a, host_b):
|
||||
|
|
@ -939,7 +932,7 @@ class HostDiff(object):
|
|||
return frag
|
||||
|
||||
|
||||
class PortDiff(object):
|
||||
class PortDiff:
|
||||
"""A diff of two Ports. It contains the two ports and the cost of changing
|
||||
one into the other. If the cost is 0 then the two ports are the same."""
|
||||
def __init__(self, port_a, port_b):
|
||||
|
|
@ -980,7 +973,7 @@ class PortDiff(object):
|
|||
self.port_b.service.name_string(),
|
||||
self.port_b.service.version_string()]
|
||||
if a_columns == b_columns:
|
||||
if verbose or self.script_result_diffs > 0:
|
||||
if verbose or len(self.script_result_diffs) > 0:
|
||||
table.append([" "] + a_columns)
|
||||
else:
|
||||
if not host_a.is_extraports(self.port_a.state):
|
||||
|
|
@ -1030,7 +1023,7 @@ class PortDiff(object):
|
|||
return frag
|
||||
|
||||
|
||||
class ScriptResultDiff(object):
|
||||
class ScriptResultDiff:
|
||||
def __init__(self, sr_a, sr_b):
|
||||
"""One of sr_a and sr_b may be None."""
|
||||
self.sr_a = sr_a
|
||||
|
|
@ -1104,7 +1097,7 @@ class ScriptResultDiff(object):
|
|||
return frag
|
||||
|
||||
|
||||
class Table(object):
|
||||
class Table:
|
||||
"""A table of character data, like NmapOutputTable."""
|
||||
def __init__(self, template):
|
||||
"""template is a string consisting of "*" and other characters. Each
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue