From 9da48bf4789f7ef905b93fe132e212a4d72c003e Mon Sep 17 00:00:00 2001 From: Ronald Date: Sun, 22 Dec 2013 20:38:55 +0100 Subject: [PATCH 001/206] fix issue #6 --- libnmap/process.py | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/libnmap/process.py b/libnmap/process.py index 3cd163d..1d3b44c 100644 --- a/libnmap/process.py +++ b/libnmap/process.py @@ -85,6 +85,8 @@ def __init__(self, targets="127.0.0.1", self.__io_queue = Queue() self.__ioerr_queue = Queue() self.__process_killed = threading.Event() + self.__thread_stdout = None + self.__thread_stderr = None # API usable in callback function self.__state = self.READY @@ -97,7 +99,6 @@ def __init__(self, targets="127.0.0.1", self.__summary = '' self.__stdout = '' self.__stderr = '' - self.initial_threads = 0 def _run_init(self): """ @@ -215,13 +216,18 @@ def stream_reader(thread_stdout, io_queue): stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0) - self.initial_threads = threading.active_count() - Thread(target=stream_reader, name='stdout-reader', - args=(self.__nmap_proc.stdout, - self.__io_queue)).start() - Thread(target=stream_reader, name='stderr-reader', - args=(self.__nmap_proc.stderr, - self.__ioerr_queue)).start() + self.__thread_stdout = Thread(target=stream_reader, + name='stdout-reader', + args=(self.__nmap_proc.stdout, + self.__io_queue)) + self.__thread_stderr = Thread(target=stream_reader, + name='stderr-reader', + args=(self.__nmap_proc.stderr, + self.__ioerr_queue)) + + self.__thread_stdout.start() + self.__thread_stderr.start() + self.__state = self.RUNNING except OSError: self.__state = self.FAILED @@ -229,6 +235,10 @@ def stream_reader(thread_stdout, io_queue): return self.__wait() + def active_fd(self): + return (self.__thread_stdout.is_alive() or + self.__thread_stderr.is_alive()) + def __wait(self): """ Private method, called by run() which will loop and @@ -243,8 +253,9 @@ def __wait(self): """ thread_stream = '' while (self.__nmap_proc.poll() is None or - threading.active_count() != self.initial_threads or - not self.__io_queue.empty()): + self.active_fd() is True or + not self.__io_queue.empty() or + not self.__ioerr_queue.empty()): if self.__process_killed.isSet(): break try: @@ -276,7 +287,6 @@ def __wait(self): return self.rc def run_background(self): - self.daemon = True super(NmapProcess, self).start() def is_running(self): From 8aec99d18e32c78ab57627e26dd7d6c0b513a1ce Mon Sep 17 00:00:00 2001 From: Ronald Date: Thu, 26 Dec 2013 18:47:22 +0100 Subject: [PATCH 002/206] fix issue6 --- CHANGES.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index 943b2c0..f7ce1a1 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,5 @@ +v0.4.1, 26/10/2013 -- Bug corrected: issue6 + Infinite loop while launching several nmap scans in background v0.4.0, 28/10/2013 -- Bug corrected in missing data from nmap scan output Added stop() to terminate nmap scan running in background v0.3.1, 17/06/2013 -- Refactory of objects to isolate each nmap object in a From 4a2f9cd3e1e0481d8e3cbaba4d1dcfd34e2dd349 Mon Sep 17 00:00:00 2001 From: Ronald Date: Thu, 26 Dec 2013 19:18:35 +0100 Subject: [PATCH 003/206] fix issue8: set NmapProcess.summary as deprecated (see changelog) --- CHANGES.txt | 7 +++++++ libnmap/process.py | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index f7ce1a1..4308360 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,10 @@ +v0.4.2, 26/10/2013 -- Bug corrected: issue 8 + There is no guarantee that "finished" or "runstats" will be + received by event parser of process.py. + Summary functions are now flagged as deprecated. To use data + from scan summary of numbers of hosts up, the user of the + lib will have to use NmapParser.parse() and the appropriate + accessors. v0.4.1, 26/10/2013 -- Bug corrected: issue6 Infinite loop while launching several nmap scans in background v0.4.0, 28/10/2013 -- Bug corrected in missing data from nmap scan output diff --git a/libnmap/process.py b/libnmap/process.py index 1d3b44c..1773637 100644 --- a/libnmap/process.py +++ b/libnmap/process.py @@ -6,6 +6,7 @@ import threading from threading import Thread from xml.dom import pulldom +import warnings try: from Queue import Queue, Empty, Full except ImportError: @@ -423,6 +424,7 @@ def endtime(self): :return: string. Unix timestamp """ + warnings.warn("data collected from finished events are deprecated. Use NmapParser.parse()", DeprecationWarning) return self.__endtime @property @@ -432,6 +434,7 @@ def elapsed(self): :return: string """ + warnings.warn("data collected from finished events are deprecated. Use NmapParser.parse()", DeprecationWarning) return self.__elapsed @property @@ -441,6 +444,7 @@ def summary(self): :return: string """ + warnings.warn("data collected from finished events are deprecated. Use NmapParser.parse()", DeprecationWarning) return self.__summary @property From 22d6beb3799e6b9675d9f9758b12c513924c5c37 Mon Sep 17 00:00:00 2001 From: Ronald Date: Fri, 27 Dec 2013 04:22:07 +0100 Subject: [PATCH 004/206] fixed pep8 issue --- libnmap/process.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libnmap/process.py b/libnmap/process.py index 1773637..cc0aee5 100644 --- a/libnmap/process.py +++ b/libnmap/process.py @@ -424,7 +424,8 @@ def endtime(self): :return: string. Unix timestamp """ - warnings.warn("data collected from finished events are deprecated. Use NmapParser.parse()", DeprecationWarning) + warnings.warn("data collected from finished events are deprecated." + "Use NmapParser.parse()", DeprecationWarning) return self.__endtime @property @@ -434,7 +435,8 @@ def elapsed(self): :return: string """ - warnings.warn("data collected from finished events are deprecated. Use NmapParser.parse()", DeprecationWarning) + warnings.warn("data collected from finished events are deprecated." + "Use NmapParser.parse()", DeprecationWarning) return self.__elapsed @property @@ -444,7 +446,8 @@ def summary(self): :return: string """ - warnings.warn("data collected from finished events are deprecated. Use NmapParser.parse()", DeprecationWarning) + warnings.warn("data collected from finished events are deprecated." + "Use NmapParser.parse()", DeprecationWarning) return self.__summary @property From 1241fcd9e1d15454c64fd345f73544a5801fc0c5 Mon Sep 17 00:00:00 2001 From: Ronald Date: Mon, 24 Feb 2014 20:56:43 +0100 Subject: [PATCH 005/206] issue #10 fix --- libnmap/objects/host.py | 2 +- libnmap/parser.py | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index e3a244b..04cf3d5 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -348,7 +348,7 @@ def os_ports_used(self): """ rval = [] try: - rval = self._extras['ports_used'] + rval = self._extras['os']['ports_used'] except (KeyError, TypeError): pass return rval diff --git a/libnmap/parser.py b/libnmap/parser.py index 1fad7f9..9248e34 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -407,7 +407,11 @@ def __parse_os_fingerprint(cls, os_data): rdict['osmatch'] = os_match_probability rdict['osclass'] = os_class_probability rdict['ports_used'] = os_ports_used - rdict['osfingerprint'] = os_fp['fingerprint'] + if 'fingerprint' in os_fp: + rdict['osfingerprint'] = os_fp['fingerprint'] + else: + rdict['osfingerprint'] = '' + return rdict From 41e1ecf6c391c4cd1c639d6b85414cb0050e6309 Mon Sep 17 00:00:00 2001 From: Ronald Date: Tue, 25 Feb 2014 00:11:50 +0100 Subject: [PATCH 006/206] fix for issue9: further test with serialization required --- examples/json_serialize.py | 4 +-- libnmap/objects/host.py | 60 +++++++++++++++++++++++++++++++++----- libnmap/parser.py | 7 ++--- 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/examples/json_serialize.py b/examples/json_serialize.py index f329bc1..597dabc 100644 --- a/examples/json_serialize.py +++ b/examples/json_serialize.py @@ -6,8 +6,8 @@ # create a json object from an NmapReport instance nmap_report_json = json.dumps(nmap_report_obj, cls=ReportEncoder) -print nmap_report_json +#print nmap_report_json # create a NmapReport instance from a json object nmap_report_obj = json.loads(nmap_report_json, cls=ReportDecoder) -print nmap_report_obj +#print nmap_report_obj diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index 04cf3d5..285ce6a 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -51,8 +51,8 @@ def osclass(self, min_accuracy=90): if int(osclass_entry['accuracy']) >= min_accuracy: _relevantkeys = ['type', 'vendor', 'osfamily', 'osgen'] _ftstr = "|".join([vkey + ": " + osclass_entry[vkey] - for vkey in osclass_entry - if vkey in _relevantkeys]) + for vkey in osclass_entry + if vkey in _relevantkeys]) os_array.append(_ftstr) except (KeyError, TypeError): pass @@ -71,9 +71,9 @@ def __repr__(self): _fmtstr += "OS CLASS:\r\n" for _oscline in self.osclass(): _fmtstr += " {0}\r\n".format(_oscline) - elif len(self.fingerprint): + elif len(self.fingerprint()): _fmtstr += "OS FINGERPRINT:\r\n" - _fmtstr += " {0}".format(self.fingerprint) + _fmtstr += " {0}".format(self.fingerprint()) return _fmtstr """ @@ -98,7 +98,6 @@ def __init__(self, starttime='', endtime='', address=None, status=None, self._endtime = endtime self._hostnames = hostnames if hostnames is not None else [] self._status = status if status is not None else {} - self._address = address if address is not None else {} self._services = services if services is not None else [] self._extras = extras if extras is not None else {} self._osfingerprinted = False @@ -106,6 +105,19 @@ def __init__(self, starttime='', endtime='', address=None, status=None, self.os = self.NmapOSFingerprint(self._extras['os']) self._osfingerprinted = True + self._ipv4_addr = None + self._ipv6_addr = None + self._mac_addr = None + for addr in address: + if addr['addrtype'] == "ipv4": + self._ipv4_addr = addr['addr'] + elif addr['addrtype'] == 'ipv6': + self._ipv6_addr = addr['addr'] + elif addr['addrtype'] == 'mac': + self._mac_addr = addr['addr'] + + self._address = self._ipv4_addr or self._ipv6_addr or '' + def __eq__(self, other): """ Compare eq NmapHost based on : @@ -188,7 +200,7 @@ def address(self): :return: IP address as a string """ - return self._address['addr'] + return self._address @address.setter def address(self, addrdict): @@ -198,7 +210,41 @@ def address(self, addrdict): :param addrdict: valid dict is {'addr': '1.1.1.1', 'addrtype': 'ipv4'} """ - self._address = addrdict + if addrdict['addrtype'] == 'ipv4': + self._ipv4_addr = addrdict['addr'] + elif addrdict['addrtype'] == 'ipv6': + self._ipv6_addr = addrdict['addr'] + if addrdict['addrtype'] == 'mac': + self._mac_addr = addrdict['addr'] + + self._address = self._ipv4_addr or self._ipv6_addr or '' + + @property + def ipv4(self): + """ + Accessor for the IPv4 address of the scanned host + + :return: IPv4 address as a string + """ + return self._ipv4_addr or '' + + @property + def mac(self): + """ + Accessor for the MAC address of the scanned host + + :return: MAC address as a string + """ + return self._mac_addr or '' + + @property + def ipv6(self): + """ + Accessor for the IPv6 address of the scanned host + + :return: IPv6 address as a string + """ + return self._ipv6_addr or '' @property def status(self): diff --git a/libnmap/parser.py b/libnmap/parser.py index 9248e34..95c56a4 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -243,7 +243,7 @@ def _parse_xml_host(cls, scanhost_data): _hostnames = [] _services = [] _status = {} - _address = {} + _addresses = [] _host_extras = {} extra_tags = ['uptime', 'distance', 'tcpsequence', 'ipidsequence', 'tcptssequence', 'times'] @@ -257,7 +257,7 @@ def _parse_xml_host(cls, scanhost_data): elif xh.tag == 'status': _status = cls.__format_attributes(xh) elif xh.tag == 'address': - _address = cls.__format_attributes(xh) + _addresses.append(cls.__format_attributes(xh)) elif xh.tag == 'os': _os_extra = cls.__parse_os_fingerprint(xh) _host_extras.update({'os': _os_extra}) @@ -274,7 +274,7 @@ def _parse_xml_host(cls, scanhost_data): _etime = _host_header['endtime'] nhost = NmapHost(_stime, _etime, - _address, + _addresses, _status, _hostnames, _services, @@ -412,7 +412,6 @@ def __parse_os_fingerprint(cls, os_data): else: rdict['osfingerprint'] = '' - return rdict @classmethod From 3ee74b8cd7c0879b0ca33ba311facc16a2ab9e8b Mon Sep 17 00:00:00 2001 From: Ronald Date: Tue, 25 Feb 2014 00:39:39 +0100 Subject: [PATCH 007/206] fix for issue9 TODO: - review OS Fingerprint - provide some os fingerprint code examples - provide some nse scripts output code examples --- examples/json_serialize.py | 4 ++-- libnmap/objects/host.py | 12 +++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/examples/json_serialize.py b/examples/json_serialize.py index 597dabc..f329bc1 100644 --- a/examples/json_serialize.py +++ b/examples/json_serialize.py @@ -6,8 +6,8 @@ # create a json object from an NmapReport instance nmap_report_json = json.dumps(nmap_report_obj, cls=ReportEncoder) -#print nmap_report_json +print nmap_report_json # create a NmapReport instance from a json object nmap_report_obj = json.loads(nmap_report_json, cls=ReportDecoder) -#print nmap_report_obj +print nmap_report_obj diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index 285ce6a..5be54f4 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -51,8 +51,8 @@ def osclass(self, min_accuracy=90): if int(osclass_entry['accuracy']) >= min_accuracy: _relevantkeys = ['type', 'vendor', 'osfamily', 'osgen'] _ftstr = "|".join([vkey + ": " + osclass_entry[vkey] - for vkey in osclass_entry - if vkey in _relevantkeys]) + for vkey in osclass_entry + if vkey in _relevantkeys]) os_array.append(_ftstr) except (KeyError, TypeError): pass @@ -116,7 +116,8 @@ def __init__(self, starttime='', endtime='', address=None, status=None, elif addr['addrtype'] == 'mac': self._mac_addr = addr['addr'] - self._address = self._ipv4_addr or self._ipv6_addr or '' + self._main_address = self._ipv4_addr or self._ipv6_addr or '' + self._address = address #self._ipv4_addr or self._ipv6_addr or '' def __eq__(self, other): """ @@ -200,7 +201,7 @@ def address(self): :return: IP address as a string """ - return self._address + return self._main_address @address.setter def address(self, addrdict): @@ -217,7 +218,8 @@ def address(self, addrdict): if addrdict['addrtype'] == 'mac': self._mac_addr = addrdict['addr'] - self._address = self._ipv4_addr or self._ipv6_addr or '' + self._main_address = self._ipv4_addr or self._ipv6_addr or '' + self._address = addrdict @property def ipv4(self): From 00f55eba36a97984fc208a9885f4b880ad119940 Mon Sep 17 00:00:00 2001 From: Ronald Date: Tue, 25 Feb 2014 00:49:04 +0100 Subject: [PATCH 008/206] fixed pep8 error/typo --- libnmap/objects/host.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index 5be54f4..9d20a41 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -117,7 +117,7 @@ def __init__(self, starttime='', endtime='', address=None, status=None, self._mac_addr = addr['addr'] self._main_address = self._ipv4_addr or self._ipv6_addr or '' - self._address = address #self._ipv4_addr or self._ipv6_addr or '' + self._address = address def __eq__(self, other): """ From 0b92f980a3c151c581d50fe5877f600927bfcc7e Mon Sep 17 00:00:00 2001 From: Ronald Date: Tue, 25 Feb 2014 09:46:18 +0100 Subject: [PATCH 009/206] fixed issue in test files --- libnmap/test/test_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnmap/test/test_report.py b/libnmap/test/test_report.py index 7e940e7..d2f0f8f 100644 --- a/libnmap/test/test_report.py +++ b/libnmap/test/test_report.py @@ -167,7 +167,7 @@ def test_host_not_equal(self): host1 = np1.hosts.pop() host2 = np2.hosts.pop() - host1._address['addr'] = 'xxxxxx' + host1.address = {'addr': '1.3.3.7', 'addrtype': 'ipv4'} self.assertNotEqual(host1, host2) def test_host_equal(self): From 5d2c467a000a762ee36fd742fea57b4ac7c25281 Mon Sep 17 00:00:00 2001 From: nnam Date: Thu, 27 Feb 2014 20:41:23 -0800 Subject: [PATCH 010/206] fixed typo in targets Exception message --- libnmap/process.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnmap/process.py b/libnmap/process.py index cc0aee5..d717cef 100644 --- a/libnmap/process.py +++ b/libnmap/process.py @@ -67,7 +67,7 @@ def __init__(self, targets="127.0.0.1", self.__nmap_targets = targets else: raise Exception("Supplied target list should be either a " - "string of a list") + "string or a list") self._nmap_options = set(options.split()) if safe_mode and not self._nmap_options.isdisjoint(unsafe_opts): From 704117390ed808eda322aefb3b249918963502dc Mon Sep 17 00:00:00 2001 From: Ronald Date: Fri, 14 Mar 2014 20:01:24 +0100 Subject: [PATCH 011/206] fix for issue 14. TODO: additional samples for testing hostscript tag --- libnmap/objects/host.py | 15 +++++++++++++++ libnmap/parser.py | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index 9d20a41..e4be2af 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -472,6 +472,21 @@ def distance(self): pass return rval + + @property + def scripts_results(self): + """ + Scripts results specific to the scanned host + + :return: array of + + + + + + + + +
+ + + + + +cpe:/a:openbsd:openssh:5.9p1cpe:/o:linux:linux_kernel +cpe:/a:apache:http_server:2.2.22 + + + + + + + + + diff --git a/libnmap/test/files/os_scan5.xml b/libnmap/test/files/os_scan5.xml new file mode 100644 index 0000000..30b787a --- /dev/null +++ b/libnmap/test/files/os_scan5.xml @@ -0,0 +1,864 @@ + + + + + + + + +
+ + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libnmap/test/files/os_scan6.xml b/libnmap/test/files/os_scan6.xml new file mode 100644 index 0000000..d3c4a9a --- /dev/null +++ b/libnmap/test/files/os_scan6.xml @@ -0,0 +1,878 @@ + + + + + + + + + + + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + + + + +
+
+ + + + + + + + + + + + + + + + +cpe:/o:linux:linux_kernel:2.6.13 + + + + + + + + + + + +
+
+ + + + + + + + + + +cpe:/o:microsoft:windows_server_2008::beta3 + + +cpe:/o:microsoft:windows_7::-:professional + + +cpe:/o:microsoft:windows + + +cpe:/o:microsoft:windows_vista::-cpe:/o:microsoft:windows_vista::sp1 +cpe:/o:microsoft:windows_server_2008::sp1 +cpe:/o:microsoft:windows_7 + + +cpe:/o:microsoft:windows_vista::sp2 +cpe:/o:microsoft:windows_7::sp1 +cpe:/o:microsoft:windows_server_2008 + + + + + + + + + + + + + +
+ + + + + + + + + + + +cpe:/o:apple:mac_os_x:10.8 + + +cpe:/o:apple:mac_os_x:10.8 +cpe:/o:apple:iphone_os:5 +cpe:/o:apple:iphone_os:5 + + + + + + + + + + + + + From a3631cb4ed0087c0e66bd485419ff8e960ed932d Mon Sep 17 00:00:00 2001 From: Ronald B Date: Fri, 9 May 2014 13:48:52 +0200 Subject: [PATCH 037/206] added OSFPPortUsed and fixed bug in old xml parsing support --- libnmap/objects/host.py | 26 +++++++----------- libnmap/objects/os.py | 60 +++++++++++++++++++++++++++++++++++------ 2 files changed, 61 insertions(+), 25 deletions(-) diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index 3418765..d321efc 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -2,6 +2,7 @@ from libnmap.diff import NmapDiff from libnmap.objects.os import NmapOSFingerprint + class NmapHost(object): """ NmapHost is a class representing a host object of NmapReport @@ -28,6 +29,7 @@ def __init__(self, starttime='', endtime='', address=None, status=None, self._services = services if services is not None else [] self._extras = extras if extras is not None else {} self._osfingerprinted = False + self.os = None if 'os' in self._extras: self.os = NmapOSFingerprint(self._extras['os']) self._osfingerprinted = True @@ -263,16 +265,11 @@ def os_class_probabilities(self): Returns an array of possible OS class detected during the OS fingerprinting. - Example [{'accuracy': '96', 'osfamily': 'embedded', - 'type': 'WAP', 'vendor': 'Netgear'}, {...}] - - :return: dict describing the OS class detected and the accuracy + :return: Array of NmapOSClass objects """ rval = [] - try: - rval = self._extras['os']['osclass'] - except (KeyError, TypeError): - pass + if self.os is not None: + rval = self.os.osclasses return rval def os_match_probabilities(self): @@ -280,14 +277,11 @@ def os_match_probabilities(self): Returns an array of possible OS match detected during the OS fingerprinting - :return: dict describing the OS version detected and the accuracy + :return: array of NmapOSMatches objects """ - rval = [] - try: - rval = self._extras['os']['osmatch'] - except (KeyError, TypeError): - pass + if self.os is not None: + rval = self.os.osmatches return rval @property @@ -307,10 +301,8 @@ def os_fingerprint(self): :return: string """ rval = '' - try: + if self.os is not None: rval = self.os.fingerprints.join("\n") - except (KeyError, TypeError): - pass return rval def os_ports_used(self): diff --git a/libnmap/objects/os.py b/libnmap/objects/os.py index 751082d..32db210 100644 --- a/libnmap/objects/os.py +++ b/libnmap/objects/os.py @@ -23,6 +23,42 @@ def __repr__(self): return self._cpestring +class OSFPPortUsed(object): + """ + Port used class: this enables the user of NmapOSFingerprint class + to have a common and clear interface to access portused data which + were collected and used during os fingerprint scan + """ + def __init__(self, port_used_dict): + try: + self._state = port_used_dict['state'] + self._proto = port_used_dict['proto'] + self._portid = port_used_dict['portid'] + except KeyError: + raise Exception("Cannot create OSFPPortUsed: missing required key") + + @property + def state(self): + """ + Accessor for the portused state (closed, open,...) + """ + return self._state + + @property + def proto(self): + """ + Accessor for the portused protocol (tcp, udp,...) + """ + return self._proto + + @property + def portid(self): + """ + Accessor for the referenced port number used + """ + return self._portid + + class NmapOSMatch(object): """ NmapOSMatch is an internal class used for offering results @@ -65,17 +101,14 @@ def __init__(self, osmatch_dict): except KeyError: pass - def add_osclass(self, class_dict): + def add_osclass(self, osclass_obj): """ Add a NmapOSClass object to the OSMatch object. This method is useful to implement compatibility with older versions of NMAP by providing a common interface to access os fingerprint data. """ - if 'osclass' in class_dict: - self.__osclasses.append(class_dict) - else: - raise Exception("Cannot add: no osclass data in provided dict") + self._osclasses.append(osclass_obj) @property def osclasses(self): @@ -243,9 +276,11 @@ def __init__(self, osfp_data): if 'fingerprint' in _osfp: self.__fingerprints.append(_osfp['fingerprint']) if 'ports_used' in osfp_data: - self.__ports_used = osfp_data['ports_used'] + for _pused_dict in osfp_data['ports_used']: + _pused = OSFPPortUsed(_pused_dict) + self.__ports_used.append(_pused) - def get_osmatch(self, accuracy): + def get_osmatch(self, osclass_obj): """ This function enables NmapOSFingerprint to determine if an NmapOSClass object could be attached to an existing NmapOSMatch @@ -257,7 +292,7 @@ def get_osmatch(self, accuracy): """ rval = None for _osmatch in self.__osmatches: - if _osmatch.accuracy == accuracy: + if _osmatch.accuracy == osclass_obj.accuracy: rval = _osmatch break # sorry return rval @@ -296,6 +331,15 @@ def fingerprint(self): def fingerprints(self): return self.__fingerprints + @property + def ports_used(self): + """ + Return an array of OSFPPortUsed object with the ports used to + perform the os fingerprint. This dict might contain another dict + embedded containing the ports_reason values. + """ + return self.__ports_used + def osmatch(self, min_accuracy=90): warnings.warn("NmapOSFingerprint.osmatch is deprecated: " "use NmapOSFingerprint.osmatches", DeprecationWarning) From 77b91ede963d189817e2524ab424ffe5e6fd4ca7 Mon Sep 17 00:00:00 2001 From: Ronald Date: Sun, 11 May 2014 21:56:38 +0200 Subject: [PATCH 038/206] fix issue11, 26, 28: Changes in OS fingerprint data API - NmapHost now holds its OS fingerprint data in NmapHost.os (NmapOSFingerpring object) - fingerprint is now a property which means you have to call it without () NmapHost.os.fingerprint and should be called directly from NmapHost.os - NmapHost.os.fingerprints return an array of os fingerprints (strings) - NmapHost.os.fingerprint return a concatenated string of all fingerprints - Fingerprints data are now accessible via NmapHost.os.osmatches which returns a list of NmapOSMatch objects - NmapOSMatch objects might contain a list of NmapOSClass objects matching with it - NmapOSClass objects might contain a list of CPE object related to the os class (CPE class will be improved and API enriched) - TODO: finalize API doc and create all related unit tests --- CHANGES.txt | 2 +- libnmap/objects/host.py | 2 + libnmap/objects/os.py | 28 ++++++------ libnmap/parser.py | 1 - libnmap/test/test_fp.py | 92 ++++++++++++++++++++++++++++++++++++++- libnmap/test/test_osfp.py | 26 +---------- 6 files changed, 109 insertions(+), 42 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 99d3cc4..b11b470 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -16,7 +16,7 @@ v0.4.8, 05/05/2014 -- Changes in OS fingerprint data API - NmapOSClass objects might contain a list of CPE object related to the os class (CPE class will be improved and API enriched) - - TODO: finalize API and create all related unit tests + - TODO: finalize API doc and create all related unit tests v0.4.7, 03/05/2014 -- minor fix for issue25 - fixed exception when optional service tag is not present in tag diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index d321efc..084241a 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -33,6 +33,8 @@ def __init__(self, starttime='', endtime='', address=None, status=None, if 'os' in self._extras: self.os = NmapOSFingerprint(self._extras['os']) self._osfingerprinted = True + else: + self.os = NmapOSFingerprint({}) self._ipv4_addr = None self._ipv6_addr = None diff --git a/libnmap/objects/os.py b/libnmap/objects/os.py index 32db210..d634895 100644 --- a/libnmap/objects/os.py +++ b/libnmap/objects/os.py @@ -19,8 +19,8 @@ def cpestring(self): """ return self._cpestring - def __repr__(self): - return self._cpestring +# def __repr__(self): +# return self._cpestring class OSFPPortUsed(object): @@ -144,11 +144,11 @@ def accuracy(self): """ return int(self._accuracy) - def __repr__(self): - rval = "{0}: {1}".format(self.name, self.accuracy) - for _osclass in self._osclasses: - rval += "\r\n |__ os class: {0}".format(str(_osclass)) - return rval +# def __repr__(self): +# rval = "{0}: {1}".format(self.name, self.accuracy) +# for _osclass in self._osclasses: +# rval += "\r\n |__ os class: {0}".format(str(_osclass)) +# return rval class NmapOSClass(object): @@ -239,13 +239,13 @@ def type(self): """ return self._type - def __repr__(self): - rval = "{0}: {1}, {2}".format(self.type, self.vendor, self.osfamily) - if len(self.osgen): - rval += "({0})".format(self.osgen) - for _cpe in self._cpelist: - rval += "\r\n |__ {0}".format(str(_cpe)) - return rval +# def __repr__(self): +# rval = "{0}: {1}, {2}".format(self.type, self.vendor, self.osfamily) +# if len(self.osgen): +# rval += "({0})".format(self.osgen) +# for _cpe in self._cpelist: +# rval += "\r\n |__ {0}".format(str(_cpe)) +# return rval class NmapOSFingerprint(object): diff --git a/libnmap/parser.py b/libnmap/parser.py index 4b3e17c..a7d9712 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -513,7 +513,6 @@ def __parse_os_fingerprint(cls, os_data): elif xos.tag == 'osmatch': os_match_proba = cls.__parse_osmatch(xos) os_match_probability.append(os_match_proba) - print os_match_proba elif xos.tag == 'portused': os_portused = cls.__format_attributes(xos) os_ports_used.append(os_portused) diff --git a/libnmap/test/test_fp.py b/libnmap/test/test_fp.py index 18b292d..1167e01 100644 --- a/libnmap/test/test_fp.py +++ b/libnmap/test/test_fp.py @@ -14,6 +14,8 @@ def setUp(self): { 'file': "%s/%s" % (fdir, 'files/1_hosts_banner.xml'), 'os': 0}, { 'file': "%s/%s" % (fdir, 'files/1_hosts_down.xml'), 'os': 0}] self.flist = self.flist_full + self.flist_os = {'nv6': {'file': "%s/%s" % (fdir, 'files/full_sudo6.xml'), 'os': 0}, + 'nv5': { 'file': "%s/%s" % (fdir, 'files/os_scan5.xml'), 'os': 0}} def test_fp(self): for file_e in self.flist_full: @@ -26,7 +28,95 @@ def test_fp(self): else: raise Exception + def test_osclasses_new(self): + oclines = [[[{'type': 'general purpose', 'accuracy': 100, 'vendor': 'Apple', 'osfamily': 'Mac OS X', 'osgen': '10.8.X'}, + {'type': 'phone', 'accuracy': 100, 'vendor': 'Apple', 'osfamily': 'iOS', 'osgen': '5.X'}, + {'type': 'media device', 'accuracy': 100, 'vendor': 'Apple', 'osfamily': 'iOS', 'osgen': '5.X'}]], + [ + [{'type': 'general purpose', 'accuracy': 100, 'vendor': 'Microsoft', 'osfamily': 'Windows', 'osgen': '2008'}], + [{'type': 'general purpose', 'accuracy': 100, 'vendor': 'Microsoft', 'osfamily': 'Windows', 'osgen': '7'}], + [{'type': 'phone', 'accuracy': 100, 'vendor': 'Microsoft', 'osfamily': 'Windows', 'osgen': 'Phone'}], + [{'type': 'general purpose', 'accuracy': 100, 'vendor': 'Microsoft', 'osfamily': 'Windows', 'osgen': 'Vista'}, + {'type': 'general purpose', 'accuracy': 100, 'vendor': 'Microsoft', 'osfamily': 'Windows', 'osgen': '2008'}, + {'type': 'general purpose', 'accuracy': 100, 'vendor': 'Microsoft', 'osfamily': 'Windows', 'osgen': '7'}], + [{'type': 'general purpose', 'accuracy': 100, 'vendor': 'Microsoft', 'osfamily': 'Windows', 'osgen': 'Vista'}, + {'type': 'general purpose', 'accuracy': 100, 'vendor': 'Microsoft', 'osfamily': 'Windows', 'osgen': '7'}, + {'type': 'general purpose', 'accuracy': 100, 'vendor': 'Microsoft', 'osfamily': 'Windows', 'osgen': '2008'}]] + ] + rep = NmapParser.parse_fromfile(self.flist_os['nv6']['file']) + hlist = [] + hlist.append(rep.hosts.pop()) + hlist.append(rep.hosts.pop()) + i=0 + j=0 + k=0 + for h in hlist: + for om in h.os.osmatches: + for oc in om.osclasses: + tdict = {'type': oc.type, 'accuracy': oc.accuracy, 'vendor': oc.vendor, 'osfamily': oc.osfamily, 'osgen': oc.osgen} + self.assertEqual(oclines[i][j][k], tdict) + k+=1 + j+=1 + k=0 + j=0 + i+=1 + + def test_osmatches_new(self): + rep = NmapParser.parse_fromfile(self.flist_os['nv6']['file']) + hlist = [] + hlist.append(rep.hosts.pop()) + hlist.append(rep.hosts.pop()) + + baseline = [[{'line': 6014, 'accuracy': 100, 'name': 'Apple Mac OS X 10.8 - 10.8.1 (Mountain Lion) (Darwin 12.0.0 - 12.1.0) or iOS 5.0.1'}], + [{'line': 52037, 'accuracy': 100, 'name': 'Microsoft Windows Server 2008 Beta 3'}, + {'line': 52938, 'accuracy': 100, 'name': 'Microsoft Windows 7 Professional'}, + {'line': 54362, 'accuracy': 100, 'name': 'Microsoft Windows Phone 7.5'}, + {'line': 54897, 'accuracy': 100, 'name': 'Microsoft Windows Vista SP0 or SP1, Windows Server 2008 SP1, or Windows 7'}, + {'line': 55210, 'accuracy': 100, 'name': 'Microsoft Windows Vista SP2, Windows 7 SP1, or Windows Server 2008'}] + ] + i=0 + j=0 + for h in hlist: + for om in h.os.osmatches: + tdict = {'line': om.line, 'accuracy': om.accuracy, 'name': om.name} + self.assertEqual(baseline[i][j], tdict) + j+=1 + j=0 + i+=1 + + def test_osmatches_old(self): + rep = NmapParser.parse_fromfile(self.flist_os['nv5']['file']) + h1 = rep.hosts[4] + h1osmatches = [{'line': -1, 'accuracy': 95, 'name': 'general purpose:Linux:Linux'}, + {'line': -1, 'accuracy': 90, 'name': 'WAP:Gemtek:embedded'}, + {'line': -1, 'accuracy': 89, 'name': 'general purpose:Nokia:Linux'}, + {'line': -1, 'accuracy': 88, 'name': 'webcam:AXIS:Linux'}] + + j=0 + for om in h1.os.osmatches: + tdict = {'line': om.line, 'accuracy': om.accuracy, 'name': om.name} + self.assertEqual(h1osmatches[j], tdict) + j+=1 + + def test_fpv6(self): + fpval = "OS:SCAN(V=6.40-2%E=4%D=5/9%OT=88%CT=%CU=%PV=Y%DS=0%DC=L%G=N%TM=536BFF2F%P=x\nOS:86_64-apple-darwin10.8.0)SEQ(SP=F9%GCD=1%ISR=103%TI=RD%TS=A)OPS(O1=M3FD8\nOS:NW4NNT11SLL%O2=M3FD8NW4NNT11SLL%O3=M3FD8NW4NNT11%O4=M3FD8NW4NNT11SLL%O5=\nOS:M3FD8NW4NNT11SLL%O6=M3FD8NNT11SLL)WIN(W1=FFFF%W2=FFFF%W3=FFFF%W4=FFFF%W5\nOS:=FFFF%W6=FFFF)ECN(R=Y%DF=Y%TG=40%W=FFFF%O=M3FD8NW4SLL%CC=N%Q=)T1(R=Y%DF=\nOS:Y%TG=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%TG=40%W=0%S=A%A=\nOS:Z%F=R%O=%RD=0%Q=)U1(R=N)IE(R=N)\n" + fparray = ['OS:SCAN(V=6.40-2%E=4%D=5/9%OT=88%CT=%CU=%PV=Y%DS=0%DC=L%G=N%TM=536BFF2F%P=x\nOS:86_64-apple-darwin10.8.0)SEQ(SP=F9%GCD=1%ISR=103%TI=RD%TS=A)OPS(O1=M3FD8\nOS:NW4NNT11SLL%O2=M3FD8NW4NNT11SLL%O3=M3FD8NW4NNT11%O4=M3FD8NW4NNT11SLL%O5=\nOS:M3FD8NW4NNT11SLL%O6=M3FD8NNT11SLL)WIN(W1=FFFF%W2=FFFF%W3=FFFF%W4=FFFF%W5\nOS:=FFFF%W6=FFFF)ECN(R=Y%DF=Y%TG=40%W=FFFF%O=M3FD8NW4SLL%CC=N%Q=)T1(R=Y%DF=\nOS:Y%TG=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%TG=40%W=0%S=A%A=\nOS:Z%F=R%O=%RD=0%Q=)U1(R=N)IE(R=N)\n'] + rep = NmapParser.parse_fromfile(self.flist_os['nv6']['file']) + h1 = rep.hosts.pop() + self.assertEqual(h1.os.fingerprint, fpval) + self.assertEqual(h1.os.fingerprints, fparray) + + def test_fpv5(self): + fpval = 'OS:SCAN(V=5.21%D=5/8%OT=22%CT=1%CU=37884%PV=Y%DS=0%DC=L%G=Y%TM=536BFE32%P=x\nOS:86_64-unknown-linux-gnu)SEQ(SP=100%GCD=1%ISR=106%TI=Z%CI=Z%II=I%TS=8)SEQ\nOS:(SP=101%GCD=1%ISR=107%TI=Z%CI=Z%II=I%TS=8)OPS(O1=M400CST11NW3%O2=M400CST\nOS:11NW3%O3=M400CNNT11NW3%O4=M400CST11NW3%O5=M400CST11NW3%O6=M400CST11)WIN(\nOS:W1=8000%W2=8000%W3=8000%W4=8000%W5=8000%W6=8000)ECN(R=Y%DF=Y%T=40%W=8018\nOS:%O=M400CNNSNW3%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(\nOS:R=Y%DF=Y%T=40%W=8000%S=O%A=S+%F=AS%O=M400CST11NW3%RD=0%Q=)T4(R=Y%DF=Y%T=\nOS:40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0\nOS:%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z\nOS:%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G\nOS:%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)\n' + fparray = ['OS:SCAN(V=5.21%D=5/8%OT=22%CT=1%CU=37884%PV=Y%DS=0%DC=L%G=Y%TM=536BFE32%P=x\nOS:86_64-unknown-linux-gnu)SEQ(SP=100%GCD=1%ISR=106%TI=Z%CI=Z%II=I%TS=8)SEQ\nOS:(SP=101%GCD=1%ISR=107%TI=Z%CI=Z%II=I%TS=8)OPS(O1=M400CST11NW3%O2=M400CST\nOS:11NW3%O3=M400CNNT11NW3%O4=M400CST11NW3%O5=M400CST11NW3%O6=M400CST11)WIN(\nOS:W1=8000%W2=8000%W3=8000%W4=8000%W5=8000%W6=8000)ECN(R=Y%DF=Y%T=40%W=8018\nOS:%O=M400CNNSNW3%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(\nOS:R=Y%DF=Y%T=40%W=8000%S=O%A=S+%F=AS%O=M400CST11NW3%RD=0%Q=)T4(R=Y%DF=Y%T=\nOS:40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0\nOS:%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z\nOS:%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G\nOS:%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=S)\n'] + rep = NmapParser.parse_fromfile(self.flist_os['nv5']['file']) + h1 = rep.hosts[4] + self.assertEqual(h1.os.fingerprint, fpval) + self.assertEqual(h1.os.fingerprints, fparray) + + if __name__ == '__main__': - test_suite = ['test_fp'] + test_suite = ['test_fp', 'test_fpv6', 'test_osmatches_new', 'test_osclasses_new', + 'test_fpv5', 'test_osmatches_old'] suite = unittest.TestSuite(map(TestNmapFP, test_suite)) test_result = unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/libnmap/test/test_osfp.py b/libnmap/test/test_osfp.py index 802ddf9..f916213 100644 --- a/libnmap/test/test_osfp.py +++ b/libnmap/test/test_osfp.py @@ -3,34 +3,10 @@ import unittest from libnmap.parser import NmapParser -osmatch_v2 = """ - - - - - cpe:/o:microsoft:windows_7::- - cpe:/o:microsoft:windows_7::sp1 - - - -""" - -osmatch_buggy1 = """ - - - - cpe:/o:microsoft:windows_7::- - cpe:/o:microsoft:windows_7::sp1 - - - -""" - - class TestNmapService(unittest.TestCase): # def setUp(self): def test_osmatch(self): - myservice = NmapParser.parse(osmatch_v2) + myservice = NmapParser.parse_fromfile(osmatch_v2) myosmatch = myservice.os.osmatch[0] self.assetEqual(osmatch.accuracy, 100) sname = "Microsoft Windows 7 SP0 - SP1, Windows Server 2008 SP1, or Windows 8" From d7b57a95fdca629b72eaf266750c3826c63b15ab Mon Sep 17 00:00:00 2001 From: Ronald Date: Sun, 11 May 2014 22:04:02 +0200 Subject: [PATCH 039/206] fixed pep on test error and enabled __repr__ --- libnmap/objects/os.py | 28 ++++++++++++++-------------- libnmap/test/test_osfp.py | 23 ----------------------- 2 files changed, 14 insertions(+), 37 deletions(-) delete mode 100644 libnmap/test/test_osfp.py diff --git a/libnmap/objects/os.py b/libnmap/objects/os.py index d634895..32db210 100644 --- a/libnmap/objects/os.py +++ b/libnmap/objects/os.py @@ -19,8 +19,8 @@ def cpestring(self): """ return self._cpestring -# def __repr__(self): -# return self._cpestring + def __repr__(self): + return self._cpestring class OSFPPortUsed(object): @@ -144,11 +144,11 @@ def accuracy(self): """ return int(self._accuracy) -# def __repr__(self): -# rval = "{0}: {1}".format(self.name, self.accuracy) -# for _osclass in self._osclasses: -# rval += "\r\n |__ os class: {0}".format(str(_osclass)) -# return rval + def __repr__(self): + rval = "{0}: {1}".format(self.name, self.accuracy) + for _osclass in self._osclasses: + rval += "\r\n |__ os class: {0}".format(str(_osclass)) + return rval class NmapOSClass(object): @@ -239,13 +239,13 @@ def type(self): """ return self._type -# def __repr__(self): -# rval = "{0}: {1}, {2}".format(self.type, self.vendor, self.osfamily) -# if len(self.osgen): -# rval += "({0})".format(self.osgen) -# for _cpe in self._cpelist: -# rval += "\r\n |__ {0}".format(str(_cpe)) -# return rval + def __repr__(self): + rval = "{0}: {1}, {2}".format(self.type, self.vendor, self.osfamily) + if len(self.osgen): + rval += "({0})".format(self.osgen) + for _cpe in self._cpelist: + rval += "\r\n |__ {0}".format(str(_cpe)) + return rval class NmapOSFingerprint(object): diff --git a/libnmap/test/test_osfp.py b/libnmap/test/test_osfp.py deleted file mode 100644 index f916213..0000000 --- a/libnmap/test/test_osfp.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -import unittest -from libnmap.parser import NmapParser - -class TestNmapService(unittest.TestCase): -# def setUp(self): - def test_osmatch(self): - myservice = NmapParser.parse_fromfile(osmatch_v2) - myosmatch = myservice.os.osmatch[0] - self.assetEqual(osmatch.accuracy, 100) - sname = "Microsoft Windows 7 SP0 - SP1, Windows Server 2008 SP1, or Windows 8" - self.assetEqual(myosmatch.name, sname) - self.assetEqual(myosmatch.line, 53077) - osclass_dict = { - 'type': 'general purpose' - 'vendor': 'Microsoft', - 'osfamily': 'Windows', - 'osgen': '7', - 'accuracy': '100' - } - self.assetEqual(myosmatch.osclasses, [osclass_dict]) - self.assertRaises(NmapParserException, NmapParser.parse, osmatch_buggy1) From 9ac2885821bdea5dd075847c0605b39df09bd0fe Mon Sep 17 00:00:00 2001 From: Ronald B Date: Mon, 12 May 2014 16:17:15 +0200 Subject: [PATCH 040/206] fixed serialization issue --- libnmap/parser.py | 1 + libnmap/reportjson.py | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libnmap/parser.py b/libnmap/parser.py index a7d9712..4f0eca7 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -225,6 +225,7 @@ def parse_fromdict(cls, rdict): slist.append(NmapService(portid=s[cname]['_portid'], protocol=s[cname]['_protocol'], state=s[cname]['_state'], + owner=s[cname]['_owner'], service=s[cname]['_service'])) nh = NmapHost(starttime=h['__NmapHost__']['_starttime'], diff --git a/libnmap/reportjson.py b/libnmap/reportjson.py index 0dbbe4e..92f5b1e 100644 --- a/libnmap/reportjson.py +++ b/libnmap/reportjson.py @@ -1,13 +1,18 @@ #!/usr/bin/env python import json from libnmap.objects import NmapHost, NmapService, NmapReport +from libnmap.objects.os import * from libnmap.parser import NmapParser class ReportEncoder(json.JSONEncoder): def default(self, obj): otype = {'NmapHost': NmapHost, - 'NmapOSFingerprint': NmapHost.NmapOSFingerprint, + 'NmapOSFingerprint': NmapOSFingerprint, + 'NmapOSMatch': NmapOSMatch, + 'NmapOSClass': NmapOSClass, + 'CPE': CPE, + 'OSFPPortUsed': OSFPPortUsed, 'NmapService': NmapService, 'NmapReport': NmapReport} if isinstance(obj, tuple(otype.values())): From 95c5ea5d1b4b805164a1979321dba6399a44dd88 Mon Sep 17 00:00:00 2001 From: Ronald B Date: Mon, 12 May 2014 16:28:30 +0200 Subject: [PATCH 041/206] fixed serialization issue: pep8 issue --- libnmap/reportjson.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libnmap/reportjson.py b/libnmap/reportjson.py index 92f5b1e..c559983 100644 --- a/libnmap/reportjson.py +++ b/libnmap/reportjson.py @@ -1,7 +1,8 @@ #!/usr/bin/env python import json from libnmap.objects import NmapHost, NmapService, NmapReport -from libnmap.objects.os import * +from libnmap.objects.os import NmapOSFingerprint, NmapOSMatch, NmapOSClass +from libnmap.objects.os import CPE, OSFPPortUsed from libnmap.parser import NmapParser From a9d6c54c5152bfd58efaead1e2d6f614fb1e4dd4 Mon Sep 17 00:00:00 2001 From: Ronald Date: Mon, 12 May 2014 19:24:46 +0200 Subject: [PATCH 042/206] added missing test file --- libnmap/test/files/full_sudo6.xml | 1010 +++++++++++++++++++++++++++++ 1 file changed, 1010 insertions(+) create mode 100644 libnmap/test/files/full_sudo6.xml diff --git a/libnmap/test/files/full_sudo6.xml b/libnmap/test/files/full_sudo6.xml new file mode 100644 index 0000000..fd461c0 --- /dev/null +++ b/libnmap/test/files/full_sudo6.xml @@ -0,0 +1,1010 @@ + + + + + + + + + + + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + +cpe:/o:linux:linux_kernel:2.6.13 + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + +
+
+ + + + + +cpe:/o:microsoft:windows +cpe:/o:microsoft:windows + + + +cpe:/o:microsoft:windows_server_2008::beta3 + + +cpe:/o:microsoft:windows_7::-:professional + + +cpe:/o:microsoft:windows + + +cpe:/o:microsoft:windows_vista::-cpe:/o:microsoft:windows_vista::sp1 +cpe:/o:microsoft:windows_server_2008::sp1 +cpe:/o:microsoft:windows_7 + + +cpe:/o:microsoft:windows_vista::sp2 +cpe:/o:microsoft:windows_7::sp1 +cpe:/o:microsoft:windows_server_2008 + + + + + + + + + + + + + + + + +
+ + + + + +cpe:/a:heimdal:kerberos +cpe:/o:microsoft:windows +cpe:/o:apple:mac_os_x + + +cpe:/a:mysql:mysql:5.7.16 + + +cpe:/a:memcached:memcached:1.4.25 + + + + + + + + \ No newline at end of file diff --git a/libnmap/test/test_service.py b/libnmap/test/test_service.py index 0e4c29d..567b239 100644 --- a/libnmap/test/test_service.py +++ b/libnmap/test/test_service.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +import os import unittest from libnmap.diff import NmapDiffException @@ -170,6 +171,7 @@ class TestNmapService(unittest.TestCase): def setUp(self): + self.fdir = os.path.dirname(os.path.realpath(__file__)) self.s1 = NmapParser.parse(service1) self.s2 = NmapParser.parse(service2) self.s3 = NmapParser.parse(service3) @@ -255,6 +257,21 @@ def test_tunnel(self): servicetunnel = NmapParser.parse(port_tunnel) self.assertEqual(servicetunnel.tunnel, "ssl") + def test_bannerdict(self): + nmapreport = NmapParser.parse_fromfile( + "{0}/files/dionaea_scan.xml".format(self.fdir) + ) + dhttp = nmapreport.hosts[0].get_service(80) + dftp = nmapreport.hosts[0].get_service(21) + self.assertEqual(dhttp.banner_dict, {"product": "nginx"}) + self.assertEqual( + dftp.banner_dict, + { + "product": "Synology DiskStation NAS ftpd", + "devicetype": "storage-misc", + }, + ) + if __name__ == "__main__": test_suite = [ From 58b0d0a77afd2d1bca66477386cecb017fbcb99a Mon Sep 17 00:00:00 2001 From: Debra Jules Date: Fri, 17 Apr 2015 10:09:45 +0200 Subject: [PATCH 162/206] Adding the MAC adress to method host.get_dict() When host is down, mac_addr = None --- libnmap/objects/host.py | 2 ++ libnmap/test/test_host.py | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index a8e1ba6..fc957fd 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -510,8 +510,10 @@ def get_dict(self): "address": self.address, "status": self.status, "hostnames": " ".join(self._hostnames), + "mac_addr": self._mac_addr, } ) + return d def diff(self, other): diff --git a/libnmap/test/test_host.py b/libnmap/test/test_host.py index 67830ff..0d6394c 100644 --- a/libnmap/test/test_host.py +++ b/libnmap/test/test_host.py @@ -149,6 +149,10 @@ class TestNmapHost(unittest.TestCase): + def setUp(self): + self.fdir = os.path.dirname(os.path.realpath(__file__)) + self.dionaea_path = "{0}/files/dionaea_scan.xml" + def test_eq_host(self): h1 = NmapParser.parse(host1) h2 = NmapParser.parse(host2) @@ -218,6 +222,7 @@ def test_diff_host(self): "NmapService::tcp.3306", "address", "NmapService::tcp.25", + "mac_addr", ] ), ) @@ -236,6 +241,7 @@ def test_diff_host(self): "NmapService::tcp.22", "NmapService::tcp.111", "address", + "mac_addr", ] ), ) @@ -253,6 +259,7 @@ def test_diff_host(self): "NmapService::tcp.22", "NmapService::tcp.111", "address", + "mac_addr", ] ), ) From 204c8e3918966f11bcb12f2f7f16086ac8a0e3ea Mon Sep 17 00:00:00 2001 From: Ronald Date: Sun, 6 Dec 2020 21:38:00 +0100 Subject: [PATCH 163/206] feat: added support for mac address in NmapHost and cascaded attribute support in NmapDiff --- .gitignore | 1 + TODO | 7 ++++--- libnmap/objects/host.py | 1 + libnmap/test/files/1_host_ping.xml | 19 +++++++++++++++++++ .../files/diff_1_host_ping_mac_changed.xml | 19 +++++++++++++++++++ libnmap/test/test_host.py | 4 +++- libnmap/test/test_report.py | 18 ++++++++++++++++++ 7 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 libnmap/test/files/1_host_ping.xml create mode 100644 libnmap/test/files/diff_1_host_ping_mac_changed.xml diff --git a/.gitignore b/.gitignore index 1be8b40..3706542 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.py[cod] *.swp +.pylintrc *~ *.lock *.DS_Store diff --git a/TODO b/TODO index 386ee14..aacde4a 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,12 @@ 0.7.1: - clean-up blacked code and pylint it 0.7.1: - add unittest for defusedxml to fix billionlaugh and external entities security issues -0.7.1: - add CSV backend support 0.7.1: - Change License from CC-BY to Apache 2.0 -0.7.1: - Update readme file to reflect the xml fixed issue + how to install it +0.7.1: - Enabled defusedxml support as preferred option for parsing () 0.7.1: - add extra_requires for plugins deps and defusedxml 0.7.1: - Remove code duplication in sudo_run and sudo_run_background from process.py -0.7.1: - Merge PR79 from Shouren to fix empty nmap outputs +0.7.1: - Fix empty nmap outputs due to subprocess race condition (Merge PR79 from @Shouren) +0.7.1: - Added banner_dict support + unittest (Merge edited PR from @cfoulds) +0.7.2: - add CSV backend support 0.7.2: improve API for NSE scripts 0.7.2: add support for post,pre and host scripts 0.7.2: add a Contribution guideline page diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index fc957fd..9425e2e 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -113,6 +113,7 @@ def __hash__(self): return ( hash(self.status) ^ hash(self.address) + ^ hash(self._mac_addr) ^ hash(frozenset(self._services)) ^ hash(frozenset(" ".join(self._hostnames))) ) diff --git a/libnmap/test/files/1_host_ping.xml b/libnmap/test/files/1_host_ping.xml new file mode 100644 index 0000000..92fde18 --- /dev/null +++ b/libnmap/test/files/1_host_ping.xml @@ -0,0 +1,19 @@ + + + + + + + + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/libnmap/test/files/diff_1_host_ping_mac_changed.xml b/libnmap/test/files/diff_1_host_ping_mac_changed.xml new file mode 100644 index 0000000..91d0429 --- /dev/null +++ b/libnmap/test/files/diff_1_host_ping_mac_changed.xml @@ -0,0 +1,19 @@ + + + + + + + + +
+
+ + + + + + + + + \ No newline at end of file diff --git a/libnmap/test/test_host.py b/libnmap/test/test_host.py index 0d6394c..d192f32 100644 --- a/libnmap/test/test_host.py +++ b/libnmap/test/test_host.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +import os import unittest from libnmap.parser import NmapParser @@ -111,6 +112,7 @@ """ + host4 = """ @@ -151,7 +153,7 @@ class TestNmapHost(unittest.TestCase): def setUp(self): self.fdir = os.path.dirname(os.path.realpath(__file__)) - self.dionaea_path = "{0}/files/dionaea_scan.xml" + self.dionaea_path = "{0}/files/dionaea_scan.xml".format(self.fdir) def test_eq_host(self): h1 = NmapParser.parse(host1) diff --git a/libnmap/test/test_report.py b/libnmap/test/test_report.py index b777a5d..df231d6 100644 --- a/libnmap/test/test_report.py +++ b/libnmap/test/test_report.py @@ -249,10 +249,28 @@ def test_host_address_unchanged(self): "NmapService::tcp.3306", "address", "NmapService::tcp.25", + "mac_addr", ] ), ) + def test_diff_mac(self): + fdir = os.path.dirname(os.path.realpath(__file__)) + host_ping = "{0}/files/1_host_ping.xml".format(fdir) + host_ping_mac_changed = ( + "{0}/files/diff_1_host_ping_mac_changed.xml".format(fdir) + ) + + report_mac_original = NmapParser.parse_fromfile(host_ping) + report_mac_changed = NmapParser.parse_fromfile(host_ping_mac_changed) + + report_diff = report_mac_original.diff(report_mac_changed) + self.assertEqual(report_diff.changed(), set(["NmapHost::172.28.1.3"])) + host_original = report_mac_original.hosts[0] + host_mac_changed = report_mac_changed.hosts[0] + host_diff = host_original.diff(host_mac_changed) + self.assertEqual(host_diff.changed(), set(["mac_addr"])) + if __name__ == "__main__": test_suite = [ From 93c6f6a817624b482f658f284cd70a88e0f0888c Mon Sep 17 00:00:00 2001 From: Ronald Date: Thu, 10 Dec 2020 23:25:02 +0100 Subject: [PATCH 164/206] fix: fixed change log and added pre-commit hooks --- CHANGES.txt | 148 ------------------------ TODO | 28 ++--- libnmap/plugins/backendpluginFactory.py | 6 +- 3 files changed, 19 insertions(+), 163 deletions(-) delete mode 100644 CHANGES.txt diff --git a/CHANGES.txt b/CHANGES.txt deleted file mode 100644 index 55d6af7..0000000 --- a/CHANGES.txt +++ /dev/null @@ -1,148 +0,0 @@ -v0.7.1, 22/11/2020 -- code clean-up + fix for CVE-2019-1010017 -v0.7.0, 28/02/2016 -- A few bugfixes - - fixe of endless loop in Nmap.Process. Fix provided by @rcarrillo, many thanks! -v0.6.3, 18/08/2015 -- Merged pull requests for automatic pypi upload, thanks @bmx0r -v0.6.2, 03/01/2015 -- Fixed a few bugs - - bulk fix for issues 37, 41, 42, 43, 44, 46 - - added cpe_list method - - added elasticsearch example code -v0.6.1, 29/06/2014 -- Added full support for python 3.X - - python now supports python 2.6, 2.7, 3.3, 3.4 -v0.5.1, 26/05/2014 -- Added basic API for class CPE - - interface similar to python-cpe for more - advanced usage of CPE, I strongly recommend you - to use python-cpe. Nice code, good doc. -v0.5.0, 17/05/2014 -- Rewrite of NmapProcess - - removed Threads to read stdout/stderr - - replaced by Process - - use of semaphore to not consume CPU while looping - - added NmapTask class - - added NmapProcess.current_task - - added NmapProcess.tasks list - - fixed bug in NmapProcess.state - - note NmapProcess.is_alive() is not recommended -v0.4.9, 14/05/2014 -- Fix for issue 28 and added code samples -v0.4.8, 05/05/2014 -- Changes in OS fingerprint data API - - NmapHost now holds its OS fingerprint data in NmapHost.os - (NmapOSFingerpring object) - - fingerprint is now a property which means you have to call - it without () NmapHost.os.fingerprint and should be called - directly from NmapHost.os - - NmapHost.os.fingerprints return an array of os - fingerprints (strings) - - NmapHost.os.fingerprint return a concatenated string of - all fingerprints - - Fingerprints data are now accessible via - NmapHost.os.osmatches which returns a list of NmapOSMatch - objects - - NmapOSMatch objects might contain a list of NmapOSClass - objects matching with it - - NmapOSClass objects might contain a list of CPE object - related to the os class (CPE class will be improved and - API enriched) - - TODO: finalize API doc and create all related unit tests -v0.4.7, 03/05/2014 -- minor fix for issue25 - - fixed exception when optional service tag is not present - in tag - - added support for if present in : - accessible via NmapService.owner -v0.4.6, 06/04/2014 -- minor fix - - corrected missing incomplete parameter on parse_fromfile - and parse_fromstring - - added support to run scan in background with sudo support - added NmapProcess.sudo_run_background() - - fixed issue with run() blocking when an error triggered - during the scan -v0.4.5, 06/04/2014 -- minor fixes and botox injections - - Added "incomplete" argument in NmapReport.parse() - in order to enable parsing of incomplete or interrupted - nmap scans. Could be useful to use with a background - scan to parse incomplete data blocks from callback - function (thanks @Sibwara for the idea). - - Fixed bug when NmapReport.summary is empty - - added NmapReport.endtimestr - - remplaced ElementTree by cElementTree (performance) -v0.4.4, 04/04/2014 -- Bugs and features requests - - added support for tunnel attribute from tag - - added support for servicefp (service fingerprint) in - attributes from tag - - added support for reasons attributes from tag - - added support for extraports/extrareasons tags - - corrected bug in serialization: missing extra data - (pull request from DougRoyal) -v0.4.3, 14/03/2014 -- Lots of bug corrections - Warning small changes in API: - - fix issue#14: better scripts parsing - - API change for NmapService.scripts_results: - NmapService.scripts_results is now a property - NmapService.scripts_results return an array of scripts results - - - fix issue#9 address field not correcly parsed: - MAC Address would erase an ipv4 address type. - - added new properties in hosts object API: - NmapHost.ipv4, NmapHost.ipv6, NmapHost.mac - - NmapHost.address property returns the IPv4 or IPv6 or MAC - in that preference order. Use specific calls for determinists - results - - fix API issue#10: os_ports_used -v0.4.2, 26/12/2013 -- Bug corrected: issue 8 - There is no guarantee that "finished" or "runstats" will be - received by event parser of process.py. - Summary functions are now flagged as deprecated. To use data - from scan summary of numbers of hosts up, the user of the - lib will have to use NmapParser.parse() and the appropriate - accessors. -v0.4.1, 26/12/2013 -- Bug corrected: issue6 - Infinite loop while launching several nmap scans in background -v0.4.0, 28/10/2013 -- Bug corrected in missing data from nmap scan output - Added stop() to terminate nmap scan running in background -v0.3.1, 17/06/2013 -- Refactory of objects to isolate each nmap object in a - separate file -v0.3.0, 17/06/2013 -- Added fingerprint class - - added NmapOSFingerprint class to provide better API to - fingerprint data - - added unit tests for basic NmapHost API check - - added unit test for basic NmapOSFingerprint class -v0.2.9, 17/06/2013 -- Add S3 plugin, allow to store nmapreport object to aws - - S3. and compatible (via boto) -v0.2.8, 11/06/2013 -- Prepare packaging for pypi -v0.2.1, 17/05/2013 -- Code Docstring and added support for additional data - - added support for scripts in NmapService - - added support for hosts extra data in NmapHost (uptime, distance,...) - - added support for OS fingerprint data in NmapHost - - added python docstrings for all modules - - added sphinx documentation - - reviewed API for libnmap objects - - fixed errors with hash() in diff - - fixed errors/exceptions in NmapParser -v0.2.0, 18/04/2013 -- Added Serialization and Plugin support - - added serialization encoders and decoders for NmapReport - - added basic plugin capability to NmapReport - - added basic mongodb plugin to validate plugin setup -v0.1.5, 08/04/2013 -- Refactory of NmapDiff system - - rework of NmapHost and NmapService API - - added __hash__, id and get_dict() for common Nmap - Objects - - added NmapDiff class - - full rework of unittests - - NmapParser now supports parsing from file - - NmapParser is able to handle nmap XML portions - - added import in reports -v0.1.4, 05/04/2013 -- Bug Fixes and improvements - - unittest for diff on NmapHost - - unittest for diff on NmapService - - fixed: __eq__ in NmapService: protocol not honoured - - fixed: sudo_run hardened and added exception handling -v0.1.3, 04/04/2013 -- Full refactory of NmapParser with static method - - added support for diffing NmapHost and NmapService - - corrected en hardened code for NmapParser - - added NmapParserException class - - added NmapReport class - - added unittest for report api - - added unittest for parser -v0.1.2, 13/03/2013 -- Bug Fixes and improvement after refactory - - added scaninfo parsing - - corrected unused variables and wrong unittests - - parse() method reviewed to call "independent" XML bloc - parsers -v0.1.1, 12/03/2013 -- Complete refactory of code to isolate NMAP objects. -v0.1.0, 11/03/2013 -- First developement release packaged for Project Ninaval diff --git a/TODO b/TODO index aacde4a..927e686 100644 --- a/TODO +++ b/TODO @@ -1,20 +1,20 @@ -0.7.1: - clean-up blacked code and pylint it -0.7.1: - add unittest for defusedxml to fix billionlaugh and external entities security issues -0.7.1: - Change License from CC-BY to Apache 2.0 -0.7.1: - Enabled defusedxml support as preferred option for parsing () -0.7.1: - add extra_requires for plugins deps and defusedxml -0.7.1: - Remove code duplication in sudo_run and sudo_run_background from process.py -0.7.1: - Fix empty nmap outputs due to subprocess race condition (Merge PR79 from @Shouren) -0.7.1: - Added banner_dict support + unittest (Merge edited PR from @cfoulds) -0.7.2: - add CSV backend support +0.7.1: clean-up blacked code and pylint it +0.7.1: add unittest for defusedxml to fix billionlaugh and external entities security issues +0.7.1: Change License from CC-BY to Apache 2.0 +0.7.1: Enabled defusedxml support as preferred option for parsing () +0.7.1: add extra_requires for plugins deps and defusedxml +0.7.1: Remove code duplication in sudo_run and sudo_run_background from process.py +0.7.1: Fix empty nmap outputs due to subprocess race condition (Merge PR79 from @Shouren) +0.7.1: Added banner_dict support + unittest (Merge edited PR from @cfoulds) + +0.7.2: add CSV backend support 0.7.2: improve API for NSE scripts 0.7.2: add support for post,pre and host scripts 0.7.2: add a Contribution guideline page 0.7.2: add development environment config and setup 0.7.2: add pre-commit hooks to enforce black and isort 0.7.2: automate in github actions the git workflow + doc update + pypi update -0.7.3: create complete python testing environment based on docker-compose and some examples -- complete unit tests with coverall support -- Add new plugins to support import/export from mysql, couchdb, csv -- add unittest for udp scans, ping sweeping -- add support for 'resume' capability (see nmap --resume) + +0.7.3: Add support and tests for traceroute in nmap + +0.7.4: create complete python testing environment based on docker-compose and some examples diff --git a/libnmap/plugins/backendpluginFactory.py b/libnmap/plugins/backendpluginFactory.py index 3b06bfd..1690e17 100644 --- a/libnmap/plugins/backendpluginFactory.py +++ b/libnmap/plugins/backendpluginFactory.py @@ -29,5 +29,9 @@ def create(cls, plugin_name="mongodb", **kwargs): try: backendplugin = classobj(**kwargs) except Exception as error: - raise Exception("Cannot create Backend: {0}".format(error)) + raise Exception( + "Cannot create Backend {0}: {1}".format( + classname, error + ) + ) return backendplugin From e7ed29507d109b953a05da90dce0257f8794db85 Mon Sep 17 00:00:00 2001 From: Ronald Date: Wed, 16 Dec 2020 21:30:18 +0100 Subject: [PATCH 165/206] fix: changelog to keep-a-change-log specs and updated documentation --- .github/workflows/pypi_publish.yaml | 26 +++ .gitignore | 1 + .pre-commit-config.yaml | 23 ++ CHANGELOG.md | 319 ++++++++++++++++++++++++++++ CODEOWNERS | 1 + MANIFEST | 2 +- NOTICE | 4 + README.md | 22 +- TODO | 5 + docs/parser.rst | 46 +++- requirements-dev.txt | 7 + 11 files changed, 447 insertions(+), 9 deletions(-) create mode 100644 .github/workflows/pypi_publish.yaml create mode 100644 .pre-commit-config.yaml create mode 100644 CHANGELOG.md create mode 100644 CODEOWNERS create mode 100644 NOTICE create mode 100644 requirements-dev.txt diff --git a/.github/workflows/pypi_publish.yaml b/.github/workflows/pypi_publish.yaml new file mode 100644 index 0000000..2d88f00 --- /dev/null +++ b/.github/workflows/pypi_publish.yaml @@ -0,0 +1,26 @@ +name: Upload Python Package + +on: + release: + types: [created] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel twine + - name: Build and publish + env: + TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} + TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} + run: | + python setup.py sdist bdist_wheel + twine upload dist/* \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3706542..02d3b53 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ nosetests.xml __pycache__ .vscode/settings.json .noseids +_build diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..bf80633 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,23 @@ +exclude: ^(test/|.tox/|docs) +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + args: [--line-length=79] + files: ^libnmap +- repo: https://github.com/pre-commit/mirrors-isort + rev: v5.6.4 + hooks: + - id: isort + args: [--multi-line=3, --line-length=79, --profile=black] +- repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.4 + hooks: + - id: flake8 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7fed80b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,319 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). (or tries to...) + +## [v0.7.1] + +### Added + +- Added pre-commit hook support to enforce code style (black, isort) +- Added unittest for defusedxml to fix billionlaugh and external entities security issues +- Added extra_requires for plugins deps and defusedxml +- Added banner_dict support + unittest (Merge edited PR from @cfoulds) +- Added black, isort in tox environment +- Added more unit tests in several modules to improve code collaboration and automated tested +- Added GitHub action pipeline to run pytests, black and isort checks +- Added GitHub action pipeline to publish pypi package + +### Changed + +- Code linted and styled with black and isort +- Changed Licence from CC-BY to Apache 2.0, considering that CC is [not appropriate for code licensing](https://creativecommons.org/faq/#can-i-apply-a-creative-commons-license-to-software) +- Changelog now using [Keep-a-changelog](https://keepachangelog.com/en/1.0.0/) specs + +### Removed + +- Removed travis build in favor of GitHub Actions pipelines + +### Fixed + +- Fix empty nmap outputs due to subprocess race condition (Merge PR#79 from @Shouren) +- Add extra_requires for plugins deps and defusedxml +- Removed code duplication in sudo_run and sudo_run_background from process.py + +### Security + +- Fix for security issue on XXE (XML External Entities) - CVE-2019-1010017 + +## [v0.7.0] - 28/02/2016 + +### Fixed + +- Fix of endless loop in Nmap.Process. Fix provided by @rcarrillo, many thanks! + +## [v0.6.3] - 18/08/2015 + +### Added + +- Merged pull requests for automatic pypi upload, thanks @bmx0r + +## [v0.6.2] - 03/01/2015 + +### Added + +- Added cpe_list method +- Added elasticsearch example code + +### Fixed + +- Fixed issues: 37, 41, 42, 43, 44, 46 + +## [v0.6.1] - 29/06/2014 + +### Added + +- Added full support for python 3.X: python now supports python 2.6, 2.7, 3.3, 3.4 + +## [v0.5.1] - 26/05/2014 + +### Added + +- Basic API for class CPE interface similar to python-cpe for more advanced usage of CPE, I strongly recommend you to use python-cpe. + +## [v0.5.0] - 17/05/2014 + +### Added + +- Added NmapTask class +- Added NmapProcess.current_task +- Added NmapProcess.tasks list +- Use of semaphore to not consume CPU while looping + +### Fixed + +- Removed Threads to read stdout/stderr +- Fixed bug in NmapProcess.state + +## [v0.4.9] - 14/05/2014 + +### Added + +- Added [code samples](examples/) + +### Fixed + +- Fix for issue 28 + +## [v0.4.8] - 05/05/2014 + +### Changed + +- Changes in OS fingerprint data API +- NmapHost now holds its OS fingerprint data in NmapHost.os (NmapOSFingerpring object) +- fingerprint is now a property which means you have to call it without (); e.g.: NmapHost.os.fingerprint +- NmapHost.os.fingerprints return an array of os fingerprints (strings) +- NmapHost.os.fingerprint return a concatenated string of all fingerprints +- Fingerprints data are now accessible via NmapHost.os.osmatches which returns a list of NmapOSMatch objects +- NmapOSMatch objects might contain a list of NmapOSClass objects matching with it +- NmapOSClass objects might contain a list of CPE object related to the os class (CPE class will be improved and API enriched) + +## [v0.4.7] - 03/05/2014 + +### Added + +- added support for if present in : accessible via NmapService.owner + +### Fixed + +- Minor fix for issue25 +- Fixed exception when optional service tag is not present in tag + + +## [v0.4.6] - 06/04/2014 + +### Added + +- Added support to run scan in background with sudo support +- Added NmapProcess.sudo_run_background() + +### Fixed + +- Corrected missing incomplete parameter on parse_fromfile and parse_fromstring +- Fixed issue with run() blocking when an error triggered during the scan + +## [v0.4.5] - 06/04/2014 + +### Added + +- Added "incomplete" argument in NmapReport.parse() in order to enable parsing of incomplete or interrupted nmap scans. Could be useful to use with a background scan to parse incomplete data blocks from callback function (thanks @Sibwara for the idea). +- Added NmapReport.endtimestr +- Added and tested cElementTree support (performance) + +### Fixed + +- Fixed bug when NmapReport.summary is empty + +## [v0.4.4] - 04/04/2014 + +### Added +- Added support for tunnel attribute from tag +- Added support for servicefp (service fingerprint) in attributes from tag +- Added support for reasons attributes from tag +- Added support for extraports/extrareasons tags + +### Fixed + +- corrected bug in serialization: missing extra data (pull request from @DougRoyal) + +## [v0.4.3] - 14/03/2014 + +### Changed + +- API change for NmapService.scripts_results: + - NmapHost.address property returns the IPv4 or IPv6 or MAC in that preference order. Use specific calls for determinists results + - NmapService.scripts_results is now a property + - NmapService.scripts_results return an array of scripts results + +### Added + +- Added new properties in hosts object API: + - NmapHost.ipv4 + - NmapHost.ipv6 + - NmapHost.mac + +### Fixed + +- Fix issue#14: better scripts parsing +- Fix issue#9 address field not correcly parsed: MAC Address would erase an ipv4 address type. +- Fix API issue#10: os_ports_used + +## [v0.4.2] - 26/12/2013 + +### Fixed + +- Fixed #issue8: There is no guarantee that "finished" or "runstats" will be received by event parser of process.py. +- Summary functions are now flagged as deprecated. To use data from scan summary of numbers of hosts up, the user of the lib will have to use NmapParser.parse() and the appropriate accessors. + +## [v0.4.1] - 26/12/2013 + +### Fixed + +- Fixed issue#6: Infinite loop while launching several nmap scans in background + +## [v0.4.0] - 28/10/2013 + +### Added + +- Added stop() to terminate nmap scan running in background + +### Fixed + +- Bug corrected in missing data from nmap scan output + +## [v0.3.1] - 17/06/2013 + +### Changed + +- Refactory of objects to isolate each nmap object in a separate file + +## [v0.3.0] - 17/06/2013 + +### Added + +- Added fingerprint class +- Added NmapOSFingerprint class to provide better API to fingerprint data +- Added unit tests for basic NmapHost API check +- Added unit test for basic NmapOSFingerprint class + +## [v0.2.9] - 17/06/2013 + +### Added + +- Add S3 plugin, allow to store nmapreport object to aws S3 compatible object storage backend (via boto) + +## [v0.2.8] - 11/06/2013 + +### Added + +- Prepare packaging for pypi + +## [v0.2.1] - 17/05/2013 + +### Added +- Code Docstring and added support for additional data +- Added support for scripts in NmapService +- Added support for hosts extra data in NmapHost (uptime, distance,...) +- Added support for OS fingerprint data in NmapHost +- Added python docstrings for all modules +- Added sphinx documentation + +### Fixed + +- Reviewed API for libnmap objects +- Fixed errors with hash() in diff +- Fixed errors/exceptions in NmapParser + +## [v0.2.0] - 18/04/2013 + +### Added + +- Added Serialization and Plugin support +- Added serialization encoders and decoders for NmapReport +- Added basic plugin capability to NmapReport +- Added basic mongodb plugin to validate plugin setup + +## [v0.1.5] - 08/04/2013 + +### Changed + +Refactory of NmapDiff system +- Rework of NmapHost and NmapService API +- Added __hash__, id and get_dict() for common Nmap Objects +- Added NmapDiff class +- Full rework of unittests +- NmapParser now supports parsing from file +- NmapParser is able to handle nmap XML portions +- Added import in reports + +## [v0.1.4] - 05/04/2013 -- Bug Fixes and improvements + +### Added + +- Added unittest for diff on NmapHost +- Added unittest for diff on NmapService + +### Fixed + +- Fixed: __eq__ in NmapService: protocol not honoured +- Fixed: sudo_run hardened and added exception handling + +## [v0.1.3] - 04/04/2013 + +### Added + +- Full refactory of NmapParser with static method +- Added support for diffing NmapHost and NmapService +- Added NmapParserException class +- Added NmapReport class +- Added unittest for report api +- Added unittest for parser + +### Fixed + +- Corrected en hardened code for NmapParser + +## [v0.1.2] - 13/03/2013 + +### Added + +- Added scaninfo parsing + +### Fixed + +- Corrected unused variables and wrong unittests +- Parse() method reviewed to call "independent" XML bloc parsers + +## [v0.1.1] - 12/03/2013 + +### Added + +- Complete refactory of code to isolate NMAP objects. + +## [v0.1.0] - 11/03/2013 + +### Added + +- First developement release packaged for Project Ninaval diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..7b644ee --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @savon-noir \ No newline at end of file diff --git a/MANIFEST b/MANIFEST index 3137d35..6637bb5 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,7 +1,7 @@ # file GENERATED by distutils, do NOT edit -CHANGES.txt README.md TODO +requirements-dev.txt setup.py docs/diff.rst docs/index.rst diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..88e053c --- /dev/null +++ b/NOTICE @@ -0,0 +1,4 @@ +python-libnmap +Copyright 2020 Ronald Bister + +This product includes software developed by Ronald Bister \ No newline at end of file diff --git a/README.md b/README.md index 931cbee..1e6afe3 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ All the documentation is available on [read the docs](https://libnmap.readthedoc ## Dependencies -libnmap has by default no dependencies. +libnmap has by default no dependencies, except defusedxml if you need to import untrusted XML scans data. The only additional python modules you'll have to install depends if you wish to use libnmap to store reports on an exotic data store via libnmap's independents plugins. @@ -52,15 +52,27 @@ Below the list of optional dependencies: - [pymongo](https://github.com/mongodb/mongo-python-driver/) - [boto](https://github.com/boto/boto) +## Security + +If you are importing/parsing untrusted XML scan outputs with python-libnmap, install defusedxml library: + +```bash +ronald@brouette:~/dev$ pip install defusedxml +``` + +This will prevent you from being vulnerable to [XML External Entities attacks](https://owasp.org/www-community/vulnerabilities/XML_External_Entity_(XXE)_Processing). + +For more information, read the [official libnmap documentation](https://libnmap.readthedocs.io/en/latest/parser.html#security-note-for-libnmap-parser) + +This note relates to a cascaded CVE vulnerability from the python core library XML ElementTree. Nevertheless, python-libnmap has been assigned an [official CVE](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1010017) to track this issue. + +This CVE is addressed from v0.7.1. + ## Python Support The libnmap code is tested against the following python interpreters: -- Python 2.6 - Python 2.7 -- Python 3.3 -- Python 3.4 -- Python 3.5 - Python 3.6 - Python 3.7 - Python 3.8 diff --git a/TODO b/TODO index 927e686..4af529b 100644 --- a/TODO +++ b/TODO @@ -7,6 +7,11 @@ 0.7.1: Fix empty nmap outputs due to subprocess race condition (Merge PR79 from @Shouren) 0.7.1: Added banner_dict support + unittest (Merge edited PR from @cfoulds) +release: +- changelog date not respecting KACL specs +- check https://github.com/anton-yurchenko/git-release +- https://github.com/sean0x42/markdown-extract + 0.7.2: add CSV backend support 0.7.2: improve API for NSE scripts 0.7.2: add support for post,pre and host scripts diff --git a/docs/parser.rst b/docs/parser.rst index 99725eb..9673859 100644 --- a/docs/parser.rst +++ b/docs/parser.rst @@ -1,6 +1,42 @@ libnmap.parser ============== +Security note for libnmap.parser +-------------------------------- + +**TLDR:** if you are importing/parsing untrusted XML scan outputs with python-libnmap, install defusedxml library: + +.. code-block:: bash + + ronald@brouette:~/dev$ pip install defusedxml + +By default, python-libnmap's parser module does not enforces an extra XML parser module than the one provided in the python core distribution. + +In versions previous to 0.7.1, by default, the `ElementTree XML API was used `_. +This XML library is vulnerable to several `XML External Entities attacks `_ which may lead to: + +- Denial of Service attacks +- Remote and local files inclusions +- Remote code execution + +This implies, de facto, that parsing any untrusted XML file could result in any of the above. + +Fortunately, one of the python core developer is maintaining an alternative Python XML parsing library: `defusedxml `_ which addresses all the above vulnerabilities. + +Since the above vulnerabilities will only affect you if you are parsing untrusted XML scan outputs, by default, the defusedxml library is not enforced. +But if the defusedxml library is installed, it will be the preferred XML parser picked by python-libnmap. + +Consider the following lines from libnmap.parser module: + +.. literalinclude:: ../libnmap/parser.py + :linenos: + :lines: 3-10 + + +- Line 4 first tries to import defusedxml +- if it fails, it then tries to load cElementTree (known to be more performant) +- if it fails, it then defaults to XML ElementTree. + Purpose of libnmap.parser ------------------------- @@ -40,14 +76,18 @@ All of the above methods can receive as input: - a list of scanned services in XML (... tag) and will return a python array of NmapService objects - a scanned service in XML (... tag) and will return a NmapService object -Small example:: +Small example: + +.. code-block:: python from libnmap.parser import NmapParser nmap_report = NmapParser.parse_fromfile('libnmap/test/files/1_os_banner_scripts.xml') print "Nmap scan summary: {0}".format(nmap_report.summary) -Basic usage from a processed scan:: +Basic usage from a processed scan: + +.. code-block:: python from libnmap.process import NmapProcess from libnmap.parser import NmapParser @@ -67,4 +107,4 @@ NmapParser methods .. automodule:: libnmap.parser .. autoclass:: NmapParser - :members: + :members: \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..aaeebb6 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,7 @@ +black==20.8b1 +defusedxml==0.6.0 +isort==5.6.4 +pre-commit +pytest +pytest-cov +flake8 From 8438f4507969e28d1cea7ea472fe124ba101ef06 Mon Sep 17 00:00:00 2001 From: Ronald Date: Wed, 16 Dec 2020 22:01:42 +0100 Subject: [PATCH 166/206] fix: updated pypi publish workflow --- .github/workflows/pypi_publish.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pypi_publish.yaml b/.github/workflows/pypi_publish.yaml index 2d88f00..a141097 100644 --- a/.github/workflows/pypi_publish.yaml +++ b/.github/workflows/pypi_publish.yaml @@ -2,7 +2,7 @@ name: Upload Python Package on: release: - types: [created] + types: [published] jobs: deploy: @@ -23,4 +23,4 @@ jobs: TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | python setup.py sdist bdist_wheel - twine upload dist/* \ No newline at end of file + twine upload dist/* From 55588ba6b210e42a56de97235c2c04fc0d9bd99d Mon Sep 17 00:00:00 2001 From: Ronald Date: Wed, 16 Dec 2020 22:30:14 +0100 Subject: [PATCH 167/206] fix(github-acitons): fix pupi_publish workflow --- .github/workflows/pypi_publish.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pypi_publish.yaml b/.github/workflows/pypi_publish.yaml index a141097..583652b 100644 --- a/.github/workflows/pypi_publish.yaml +++ b/.github/workflows/pypi_publish.yaml @@ -22,5 +22,5 @@ jobs: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} run: | - python setup.py sdist bdist_wheel + python setup.py sdist twine upload dist/* From 579b6ceacebb2cbdd6f9e4aff27394fb1a2bd2b0 Mon Sep 17 00:00:00 2001 From: Ronald Date: Wed, 16 Dec 2020 23:14:08 +0100 Subject: [PATCH 168/206] fix: moved back from README.md to README.rst --- MANIFEST | 2 +- MANIFEST.in | 2 +- README.md | 114 ------------------------------------- README.rst | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 2 +- 5 files changed, 162 insertions(+), 117 deletions(-) delete mode 100644 README.md create mode 100644 README.rst diff --git a/MANIFEST b/MANIFEST index 6637bb5..c06a8aa 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1,5 +1,5 @@ # file GENERATED by distutils, do NOT edit -README.md +README.rst TODO requirements-dev.txt setup.py diff --git a/MANIFEST.in b/MANIFEST.in index 9180c88..bb7a46c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,3 +1,3 @@ include TODO -include *.rst *.txt README.md +include *.rst *.txt recursive-include docs *.rst diff --git a/README.md b/README.md deleted file mode 100644 index 1e6afe3..0000000 --- a/README.md +++ /dev/null @@ -1,114 +0,0 @@ -# python-libnmap - -## Code status - -![preflight-check](https://github.com/savon-noir/python-libnmap/workflows/Preflight%20Check/badge.svg) -[![Coverage Status](https://coveralls.io/repos/github/savon-noir/python-libnmap/badge.svg?branch=master)](https://coveralls.io/github/savon-noir/python-libnmap?branch=master) -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) - -## Use cases - -libnmap is a python library enabling python developers to manipulate nmap process and data. - -libnmap is what you were looking for if you need to implement the following: - -- automate or schedule nmap scans on a regular basis -- manipulate nmap scans results to do reporting -- compare and diff nmap scans to generate graphs -- batch process scan reports -- ... - -The above uses cases will be easy to implement with the help of the libnmap modules. - -## libnmap modules - -The lib currently offers the following modules: - -- **process**: enables you to launch nmap scans -- **parse**: enables you to parse nmap reports or scan results (only XML so far) from a file, a string,... -- **report**: enables you to manipulate a parsed scan result and de/serialize scan results in a json format -- **diff**: enables you to see what changed between two scans -- **common**: contains basic nmap objects like NmapHost and NmapService. It is to note that each object can be "diff()ed" with another similar object. -- **plugins**: enables you to support datastores for your scan results directly in the "NmapReport" object. from report module: - - mongodb: insert/get/getAll/delete - - sqlalchemy: insert/get/getAll/delete - - aws s3: insert/get/getAll/delete (not supported for python3 since boto is not supporting py3) - - csv: todo (easy to implement) - - elastic search: todo - -## Documentation - -All the documentation is available on [read the docs](https://libnmap.readthedocs.org). This documentation contains small code samples that you directly reuse. - -## Dependencies - -libnmap has by default no dependencies, except defusedxml if you need to import untrusted XML scans data. - -The only additional python modules you'll have to install depends if you wish to use libnmap to store reports on an exotic data store via libnmap's independents plugins. - -Below the list of optional dependencies: - -- [sqlalchemy](https://github.com/zzzeek/sqlalchemy) (+the driver ie:MySQL-python) -- [pymongo](https://github.com/mongodb/mongo-python-driver/) -- [boto](https://github.com/boto/boto) - -## Security - -If you are importing/parsing untrusted XML scan outputs with python-libnmap, install defusedxml library: - -```bash -ronald@brouette:~/dev$ pip install defusedxml -``` - -This will prevent you from being vulnerable to [XML External Entities attacks](https://owasp.org/www-community/vulnerabilities/XML_External_Entity_(XXE)_Processing). - -For more information, read the [official libnmap documentation](https://libnmap.readthedocs.io/en/latest/parser.html#security-note-for-libnmap-parser) - -This note relates to a cascaded CVE vulnerability from the python core library XML ElementTree. Nevertheless, python-libnmap has been assigned an [official CVE](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-1010017) to track this issue. - -This CVE is addressed from v0.7.1. - -## Python Support - -The libnmap code is tested against the following python interpreters: - -- Python 2.7 -- Python 3.6 -- Python 3.7 -- Python 3.8 - -## Install - -You can install libnmap via pip: - -```bash -ronald@brouette:~$ pip install python-libnmap -``` - -or via git and dist utils (à l'ancienne): - -```bash -ronald@brouette:~$ git clone https://github.com/savon-noir/python-libnmap.git -ronald@brouette:~$ cd python-libnmap -ronald@brouette:~$ python setup.py install -``` - -or via git and pip: - -```bash -ronald@brouette:~$ git clone https://github.com/savon-noir/python-libnmap.git -ronald@brouette:~$ cd python-libnmap -ronald@brouette:~$ pip install . -``` - -## Examples - -Some codes samples are available in the examples directory or in the [documentation](https://libnmap.readthedocs.org). - -Among other example, you notice an sample code pushing nmap scan reports in an ElasticSearch instance and allowing you to create fancy dashboards in Kibana like the screenshot below: - -![Kibanane](https://github.com/savon-noir/python-libnmap/blob/es/examples/kibanalibnmap.png) - -## Contributors - -Mike @bmx0r Boutillier for S3 and SQL-Alechemy plugins and for the constructive critics. Thanks! diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..f097b19 --- /dev/null +++ b/README.rst @@ -0,0 +1,159 @@ +python-libnmap +============== + +Code status +----------- + +|preflight-check| |Coverage Status| |License| + +Use cases +--------- + +libnmap is a python library enabling python developers to manipulate +nmap process and data. + +libnmap is what you were looking for if you need to implement the +following: + +- automate or schedule nmap scans on a regular basis +- manipulate nmap scans results to do reporting +- compare and diff nmap scans to generate graphs +- batch process scan reports +- … + +The above uses cases will be easy to implement with the help of the +libnmap modules. + +libnmap modules +--------------- + +The lib currently offers the following modules: + +- **process**: enables you to launch nmap scans +- **parse**: enables you to parse nmap reports or scan results (only + XML so far) from a file, a string,… +- **report**: enables you to manipulate a parsed scan result and + de/serialize scan results in a json format +- **diff**: enables you to see what changed between two scans +- **common**: contains basic nmap objects like NmapHost and + NmapService. It is to note that each object can be “diff()ed” with + another similar object. +- **plugins**: enables you to support datastores for your scan results + directly in the “NmapReport” object. from report module: + + - mongodb: insert/get/getAll/delete + - sqlalchemy: insert/get/getAll/delete + - aws s3: insert/get/getAll/delete (not supported for python3 since + boto is not supporting py3) + - csv: todo (easy to implement) + - elastic search: todo + +Documentation +------------- + +All the documentation is available on `read the +docs `__. This documentation contains +small code samples that you directly reuse. + +Dependencies +------------ + +libnmap has by default no dependencies, except defusedxml if you need to +import untrusted XML scans data. + +The only additional python modules you’ll have to install depends if you +wish to use libnmap to store reports on an exotic data store via +libnmap’s independents plugins. + +Below the list of optional dependencies: + +- `sqlalchemy `__ (+the driver + ie:MySQL-python) +- `pymongo `__ +- `boto `__ + +Security +-------- + +If you are importing/parsing untrusted XML scan outputs with +python-libnmap, install defusedxml library: + +.. code:: bash + + ronald@brouette:~/dev$ pip install defusedxml + +This will prevent you from being vulnerable to `XML External Entities +attacks `__. + +For more information, read the `official libnmap +documentation `__ + +This note relates to a cascaded CVE vulnerability from the python core +library XML ElementTree. Nevertheless, python-libnmap has been assigned +an `official +CVE `__ +to track this issue. + +This CVE is addressed from v0.7.1. + +Python Support +-------------- + +The libnmap code is tested against the following python interpreters: + +- Python 2.7 +- Python 3.6 +- Python 3.7 +- Python 3.8 + +Install +------- + +You can install libnmap via pip: + +.. code:: bash + + ronald@brouette:~$ pip install python-libnmap + +or via git and dist utils (à l’ancienne): + +.. code:: bash + + ronald@brouette:~$ git clone https://github.com/savon-noir/python-libnmap.git + ronald@brouette:~$ cd python-libnmap + ronald@brouette:~$ python setup.py install + +or via git and pip: + +.. code:: bash + + ronald@brouette:~$ git clone https://github.com/savon-noir/python-libnmap.git + ronald@brouette:~$ cd python-libnmap + ronald@brouette:~$ pip install . + +Examples +-------- + +Some codes samples are available in the examples directory or in the +`documentation `__. + +Among other example, you notice an sample code pushing nmap scan reports +in an ElasticSearch instance and allowing you to create fancy dashboards +in Kibana like the screenshot below: + +.. figure:: https://github.com/savon-noir/python-libnmap/blob/es/examples/kibanalibnmap.png + :alt: Kibanane + + Kibanane + +Contributors +------------ + +Mike @bmx0r Boutillier for S3 and SQL-Alechemy plugins and for the +constructive critics. Thanks! + +.. |preflight-check| image:: https://github.com/savon-noir/python-libnmap/workflows/Preflight%20Check/badge.svg +.. |Coverage Status| image:: https://coveralls.io/repos/github/savon-noir/python-libnmap/badge.svg?branch=master + :target: https://coveralls.io/github/savon-noir/python-libnmap?branch=master +.. |License| image:: https://img.shields.io/badge/License-Apache%202.0-blue.svg + :target: https://opensource.org/licenses/Apache-2.0 diff --git a/setup.py b/setup.py index b898211..d86f6e4 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from distutils.core import setup -with open("README.md") as rfile: +with open("README.rst") as rfile: long_description = rfile.read() setup( From d142c00b43fe4972daeb12f4aef639a0ef700ef3 Mon Sep 17 00:00:00 2001 From: Ronald Date: Wed, 16 Dec 2020 23:34:17 +0100 Subject: [PATCH 169/206] fix: bump up to v0.7.2 needed following pypi issue --- CHANGELOG.md | 2 +- README.rst | 4 +--- TODO | 41 ++++++++++++++++++++++++----------------- docs/conf.py | 2 +- docs/parser.rst | 4 ++-- libnmap/__init__.py | 2 +- setup.py | 2 +- 7 files changed, 31 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fed80b..824e7d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). (or tries to...) -## [v0.7.1] +## [v0.7.2] 2020-12-16 ### Added diff --git a/README.rst b/README.rst index f097b19..4951e71 100644 --- a/README.rst +++ b/README.rst @@ -94,7 +94,7 @@ an `official CVE `__ to track this issue. -This CVE is addressed from v0.7.1. +This CVE is addressed from v0.7.2. Python Support -------------- @@ -144,8 +144,6 @@ in Kibana like the screenshot below: .. figure:: https://github.com/savon-noir/python-libnmap/blob/es/examples/kibanalibnmap.png :alt: Kibanane - Kibanane - Contributors ------------ diff --git a/TODO b/TODO index 4af529b..6aa4c65 100644 --- a/TODO +++ b/TODO @@ -1,25 +1,32 @@ -0.7.1: clean-up blacked code and pylint it -0.7.1: add unittest for defusedxml to fix billionlaugh and external entities security issues -0.7.1: Change License from CC-BY to Apache 2.0 -0.7.1: Enabled defusedxml support as preferred option for parsing () -0.7.1: add extra_requires for plugins deps and defusedxml -0.7.1: Remove code duplication in sudo_run and sudo_run_background from process.py -0.7.1: Fix empty nmap outputs due to subprocess race condition (Merge PR79 from @Shouren) -0.7.1: Added banner_dict support + unittest (Merge edited PR from @cfoulds) +0.7.2: clean-up blacked code and pylint it +0.7.2: add unittest for defusedxml to fix billionlaugh and external entities security issues +0.7.2: Change License from CC-BY to Apache 2.0 +0.7.2: Enabled defusedxml support as preferred option for parsing () +0.7.2: add extra_requires for plugins deps and defusedxml +0.7.2: Remove code duplication in sudo_run and sudo_run_background from process.py +0.7.2: Fix empty nmap outputs due to subprocess race condition (Merge PR79 from @Shouren) +0.7.2: Added banner_dict support + unittest (Merge edited PR from @cfoulds) release: - changelog date not respecting KACL specs - check https://github.com/anton-yurchenko/git-release - https://github.com/sean0x42/markdown-extract -0.7.2: add CSV backend support -0.7.2: improve API for NSE scripts -0.7.2: add support for post,pre and host scripts -0.7.2: add a Contribution guideline page -0.7.2: add development environment config and setup -0.7.2: add pre-commit hooks to enforce black and isort -0.7.2: automate in github actions the git workflow + doc update + pypi update +Contribution file: +- specify where version needs to be set before adding tag to commit + - libnmap/__init__.py + - docs/conf.py + - setup.py + - CHANGELOG.md (set correct date) -0.7.3: Add support and tests for traceroute in nmap +0.7.3: add CSV backend support +0.7.3: improve API for NSE scripts +0.7.3: add support for post,pre and host scripts +0.7.3: add a Contribution guideline page +0.7.3: add development environment config and setup +0.7.3: add pre-commit hooks to enforce black and isort +0.7.3: automate in github actions the git workflow + doc update + pypi update -0.7.4: create complete python testing environment based on docker-compose and some examples +0.7.4: Add support and tests for traceroute in nmap + +0.7.5: create complete python testing environment based on docker-compose and some examples diff --git a/docs/conf.py b/docs/conf.py index 07e9dc8..6e0d4ee 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,7 +51,7 @@ # The short X.Y version. version = "0.7" # The full version, including alpha/beta/rc tags. -release = "0.7.1" +release = "0.7.2" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/parser.rst b/docs/parser.rst index 9673859..c0a1a70 100644 --- a/docs/parser.rst +++ b/docs/parser.rst @@ -12,7 +12,7 @@ Security note for libnmap.parser By default, python-libnmap's parser module does not enforces an extra XML parser module than the one provided in the python core distribution. -In versions previous to 0.7.1, by default, the `ElementTree XML API was used `_. +In versions previous to 0.7.2, by default, the `ElementTree XML API was used `_. This XML library is vulnerable to several `XML External Entities attacks `_ which may lead to: - Denial of Service attacks @@ -107,4 +107,4 @@ NmapParser methods .. automodule:: libnmap.parser .. autoclass:: NmapParser - :members: \ No newline at end of file + :members: diff --git a/libnmap/__init__.py b/libnmap/__init__.py index b3b5f89..411e762 100644 --- a/libnmap/__init__.py +++ b/libnmap/__init__.py @@ -5,4 +5,4 @@ __maintainer__ = "Ronald Bister" __email__ = "mini.pelle@gmail.com" __license__ = "Apache 2.0" -__version__ = "0.7.1" +__version__ = "0.7.2" diff --git a/setup.py b/setup.py index d86f6e4..4643b7f 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="python-libnmap", - version="0.7.1", + version="0.7.2", author="Ronald Bister", author_email="mini.pelle@gmail.com", packages=["libnmap", "libnmap.plugins", "libnmap.objects"], From 4e969dd43ef92f4a4c4243e70629595b88da4f0b Mon Sep 17 00:00:00 2001 From: Konstantin Weddige Date: Mon, 4 Jan 2021 18:10:51 +0100 Subject: [PATCH 170/206] Set encoding for README As the default encoding is platform dependent, python-libnmap can not be installed if the preferred encoding is not UTF-8. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4643b7f..9668371 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from distutils.core import setup -with open("README.rst") as rfile: +with open("README.rst", encoding="utf-8") as rfile: long_description = rfile.read() setup( From 8f442747a7a16969309d6f7653ad1b13a3a99bae Mon Sep 17 00:00:00 2001 From: Freddie Chessell <4047565+FreddieDev@users.noreply.github.com> Date: Mon, 13 Sep 2021 14:09:31 +0100 Subject: [PATCH 171/206] Fix Windows charmap exception for quote characters When installing on Windows with `pip install python-libnmap`, these characters cause the error: ``` UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 1117: character maps to ``` --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 4951e71..023726f 100644 --- a/README.rst +++ b/README.rst @@ -36,10 +36,10 @@ The lib currently offers the following modules: de/serialize scan results in a json format - **diff**: enables you to see what changed between two scans - **common**: contains basic nmap objects like NmapHost and - NmapService. It is to note that each object can be “diff()ed” with + NmapService. It is to note that each object can be "diff()ed" with another similar object. - **plugins**: enables you to support datastores for your scan results - directly in the “NmapReport” object. from report module: + directly in the "NmapReport" object. from report module: - mongodb: insert/get/getAll/delete - sqlalchemy: insert/get/getAll/delete From 28060b437e800d9bc9840cca8f171ce7a3fb34dd Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Sun, 5 Sep 2021 06:54:47 +1000 Subject: [PATCH 172/206] docs: Fix a few typos There are small typos in: - docs/diff.rst - docs/index.rst - docs/parser.rst - libnmap/objects/os.py - libnmap/objects/report.py - libnmap/plugins/backendplugin.py - libnmap/plugins/es.py - libnmap/plugins/mongodb.py - libnmap/plugins/sql.py - libnmap/process.py Fixes: - Should read `retrieve` rather than `retreive`. - Should read `instantiated` rather than `instanciated`. - Should read `privileges` rather than `priviledges`. - Should read `number` rather than `numer`. - Should read `what` rather than `waht`. - Should read `support` rather than `suport`. - Should read `receive` rather than `reeive`. --- docs/diff.rst | 2 +- docs/index.rst | 2 +- docs/parser.rst | 2 +- libnmap/objects/os.py | 2 +- libnmap/objects/report.py | 4 ++-- libnmap/plugins/backendplugin.py | 2 +- libnmap/plugins/es.py | 2 +- libnmap/plugins/mongodb.py | 2 +- libnmap/plugins/sql.py | 4 ++-- libnmap/process.py | 6 +++--- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/diff.rst b/docs/diff.rst index 658c3f9..e9950d9 100644 --- a/docs/diff.rst +++ b/docs/diff.rst @@ -17,7 +17,7 @@ Those methods return a python set() of keys which have been changed/added/remove object to another. The keys of each objects could be found in the implementation of the get_dict() methods of the compared objects. -The example below is a heavy version of going through all nested objects to see waht has changed after a diff:: +The example below is a heavy version of going through all nested objects to see what has changed after a diff:: #!/usr/bin/env python diff --git a/docs/index.rst b/docs/index.rst index 7e89213..7226bbf 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,7 +21,7 @@ libnmap is a python toolkit for manipulating nmap. It currently offers the follo - plugins: enables you to support datastores for your scan results directly in the "NmapReport" object from report module - mongodb: only plugin implemented so far, ultra basic, for POC purpose only - - sqlalchemy: Allow to store/retreive NmapReport to sqlite/mysql/... all engine supported by sqlalchemy + - sqlalchemy: Allow to store/retrieve NmapReport to sqlite/mysql/... all engine supported by sqlalchemy - rabbitMQ : todo - couchdb: todo - elastic search: todo diff --git a/docs/parser.rst b/docs/parser.rst index c0a1a70..09bc5d2 100644 --- a/docs/parser.rst +++ b/docs/parser.rst @@ -63,7 +63,7 @@ Using libnmap.parser module NmapParser parse the whole data and returns nmap objects usable via their documented API. -The NmapParser should never be instanciated and only the following methods should be called: +The NmapParser should never be instantiated and only the following methods should be called: - NmapParser.parse(string) - NmapParser.parse_fromfile(file_path) diff --git a/libnmap/objects/os.py b/libnmap/objects/os.py index 912c32e..30d3f3b 100644 --- a/libnmap/objects/os.py +++ b/libnmap/objects/os.py @@ -269,7 +269,7 @@ def __repr__(self): class NmapOSFingerprint(object): """ NmapOSFingerprint is a easier API for using os fingerprinting. - Data for OS fingerprint ( tag) is instanciated from + Data for OS fingerprint ( tag) is instantiated from a NmapOSFingerprint which is accessible in NmapHost via NmapHost.os """ diff --git a/libnmap/objects/report.py b/libnmap/objects/report.py index cd9a556..8c69eb4 100644 --- a/libnmap/objects/report.py +++ b/libnmap/objects/report.py @@ -254,7 +254,7 @@ def elapsed(self): @property def hosts_up(self): """ - Accessor returning the numer of host detected + Accessor returning the number of host detected as 'up' during the scan. :return: integer (0 >= or -1) @@ -270,7 +270,7 @@ def hosts_up(self): @property def hosts_down(self): """ - Accessor returning the numer of host detected + Accessor returning the number of host detected as 'down' during the scan. :return: integer (0 >= or -1) diff --git a/libnmap/plugins/backendplugin.py b/libnmap/plugins/backendplugin.py index cfbf393..0a7c9a5 100644 --- a/libnmap/plugins/backendplugin.py +++ b/libnmap/plugins/backendplugin.py @@ -30,7 +30,7 @@ def delete(self, id): def get(self, id): """ - retreive a NmapReport from the backend + retrieve a NmapReport from the backend :param id: str :return: NmapReport """ diff --git a/libnmap/plugins/es.py b/libnmap/plugins/es.py index 688a3d2..8db8623 100644 --- a/libnmap/plugins/es.py +++ b/libnmap/plugins/es.py @@ -48,7 +48,7 @@ def delete(self, id): def get(self, id): """ - retreive a NmapReport from the backend + retrieve a NmapReport from the backend :param id: str :return: NmapReport """ diff --git a/libnmap/plugins/mongodb.py b/libnmap/plugins/mongodb.py index 1e707f3..80bfe1a 100644 --- a/libnmap/plugins/mongodb.py +++ b/libnmap/plugins/mongodb.py @@ -15,7 +15,7 @@ class NmapMongodbPlugin(NmapBackendPlugin): Implementation is made using pymongo Object of this class must be create via the BackendPluginFactory.create(**url) where url is a named dict like - {'plugin_name': "mongodb"} this dict may reeive all the param + {'plugin_name': "mongodb"} this dict may receive all the param MongoClient() support """ diff --git a/libnmap/plugins/sql.py b/libnmap/plugins/sql.py index dc0ede4..95eb36f 100644 --- a/libnmap/plugins/sql.py +++ b/libnmap/plugins/sql.py @@ -77,7 +77,7 @@ def __init__(self, **kwargs): - create all the necessary obj to discuss with the DB - create all the mapping(ORM) - todo : suport the : sqlalchemy.engine_from_config + todo : support the : sqlalchemy.engine_from_config :param **kwargs: :raises: ValueError if no url is given, @@ -121,7 +121,7 @@ def insert(self, nmap_report): def get(self, report_id=None): """ - retreive a NmapReport from the backend + retrieve a NmapReport from the backend :param id: str diff --git a/libnmap/process.py b/libnmap/process.py index 639a7f1..704c344 100644 --- a/libnmap/process.py +++ b/libnmap/process.py @@ -22,7 +22,7 @@ class NmapTask(object): """ NmapTask is a internal class used by process. Each time nmap - starts a new task during the scan, a new class will be instanciated. + starts a new task during the scan, a new class will be instantiated. Classes examples are: "Ping Scan", "NSE script", "DNS Resolve",.. To each class an estimated time to complete is assigned and updated at least every second within the NmapProcess. @@ -215,7 +215,7 @@ def _ensure_user_exists(self, username=""): def sudo_run(self, run_as="root"): """ Public method enabling the library's user to run the scan with - priviledges via sudo. The sudo configuration should be set manually + privileges via sudo. The sudo configuration should be set manually on the local system otherwise sudo will prompt for a password. This method alters the command line by prefixing the sudo command to nmap and will then call self.run() @@ -245,7 +245,7 @@ def sudo_run(self, run_as="root"): def sudo_run_background(self, run_as="root"): """ Public method enabling the library's user to run in background a - nmap scan with priviledges via sudo. + nmap scan with privileges via sudo. The sudo configuration should be set manually on the local system otherwise sudo will prompt for a password. This method alters the command line by prefixing the sudo command to From c36fecde90017befeb4853396d0e2aac93c95b64 Mon Sep 17 00:00:00 2001 From: Facundo Tuesca Date: Mon, 29 Aug 2022 15:39:38 +0200 Subject: [PATCH 173/206] Fix CVE-2022-30284 by validating targets This change adds an extra validation to `NmapProcess` that does some basic checks on the `targets` parameter. It checks that no unexpected characters are present in each target, and that if dashes are present, they are not located at the start or end of the string (a valid FQDN can have dashes except in those positions). This last check fixes CVE-2022-30284, which allowed to inject extra command line options by using a target such as `127.0.0.1,--script,http-fetch....`. --- libnmap/process.py | 25 +++++++++++++++++++++++++ libnmap/test/test_process.py | 31 ++++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/libnmap/process.py b/libnmap/process.py index 704c344..d1aba76 100644 --- a/libnmap/process.py +++ b/libnmap/process.py @@ -4,6 +4,7 @@ import os import platform import shlex +import string import subprocess import warnings from threading import Thread @@ -129,6 +130,9 @@ def __init__( "Supplied target list should be either a string or a list" ) + for target in self.__nmap_targets: + self.__validate_target(target) + self._nmap_options = set(options.split()) if safe_mode and not self._nmap_options.isdisjoint(unsafe_opts): raise Exception( @@ -480,6 +484,27 @@ def __build_windows_cmdline(self): cmdline += self.__nmap_targets # already a list return cmdline + @staticmethod + def __validate_target(target): + # See https://nmap.org/book/man-target-specification.html for all the + # ways targets can be specified + allowed_characters = frozenset( + string.ascii_letters + string.digits + "-.:/% " + ) + if not set(target).issubset(allowed_characters): + raise Exception( + "Target '{}' contains invalid characters".format(target) + ) + # FQDN can contain dashes anywhere except at the beginning or end + # This check also fixes/prevents CVE-2022-30284, which depends on being + # able to pass options such as --script as a target + elif target.startswith("-") or target.endswith("-"): + raise Exception( + "Target '{}' cannot begin or end with a dash ('-')".format( + target + ) + ) + @property def command(self): """ diff --git a/libnmap/test/test_process.py b/libnmap/test/test_process.py index 4345493..b2f3297 100644 --- a/libnmap/test/test_process.py +++ b/libnmap/test/test_process.py @@ -19,19 +19,26 @@ def setUp(self): self._assertRaisesRegex = self.assertRaisesRegexp self.fdir = os.path.dirname(os.path.realpath(__file__)) - def test_check_targets(self): - invalid_target_tests = [{"a": "bba"}, 5] + def test_check_valid_targets(self): valid_target_tests = [ {"value": "127.0.0.1, 1.1.1.1, 2.20.202", "size": 3}, {"value": ["127.0.0.1", "1.1.1.1", "2.20.202.2"], "size": 3}, {"value": [" 127.0.0.1", " 1.1.1.1"], "size": 2}, {"value": " 127.0.0.1, 1.1.1.1 , a", "size": 3}, + {"value": ["192.168.10.0/24", "192.168.0-255.1-254"], "size": 2}, + {"value": ["fe80::a8bb:ccff:fedd:eeff%eth0"], "size": 1}, + {"value": ["my-domain.com", "my-num3r1c-domain.com"], "size": 2}, ] for vtarget in valid_target_tests: nmapobj = NmapProcess(targets=vtarget["value"], options="-sP") self.assertEqual(vtarget["size"], len(nmapobj.targets)) - for vtarget in invalid_target_tests: + def test_check_invalid_targets(self): + invalid_target_type_tests = [{"a": "bba"}, 5] + invalid_target_character_tests = ["1.1.1.1$", "invalid_domain.com"] + invalid_target_dash_tests = ["-invalid-target", "--option"] + + for vtarget in invalid_target_type_tests: self._assertRaisesRegex( Exception, "Supplied target list should be either a string or a list", @@ -40,6 +47,24 @@ def test_check_targets(self): options="-sP", ) + for vtarget in invalid_target_character_tests: + self._assertRaisesRegex( + Exception, + "contains invalid characters", + NmapProcess, + targets=vtarget, + options="-sP", + ) + + for vtarget in invalid_target_dash_tests: + self._assertRaisesRegex( + Exception, + "cannot begin or end with a dash", + NmapProcess, + targets=vtarget, + options="-sP", + ) + def test_nmap_options(self): invalid_options = ["--iflist"] From 866f2aa4db5bf222a6d13ef722b417aae4489397 Mon Sep 17 00:00:00 2001 From: savon-noir Date: Thu, 1 Sep 2022 21:32:30 +0200 Subject: [PATCH 174/206] lint: ran linters on code --- .github/workflows/preflight_check.yaml | 4 ++-- .pre-commit-config.yaml | 2 +- CHANGELOG.md | 6 +++--- CODEOWNERS | 2 +- LICENCE | 2 +- NOTICE | 2 +- TODO | 4 ++-- config/database.yml | 3 +-- libnmap/test/files/1_host_ping.xml | 2 +- libnmap/test/files/2_hosts.json | 2 +- libnmap/test/files/defused_et_included.xml | 2 +- libnmap/test/files/defused_et_local_includer.xml | 2 +- libnmap/test/files/diff_1_host_ping_mac_changed.xml | 2 +- libnmap/test/files/dionaea_scan.xml | 2 +- libnmap/test/files/test_osclass.xml | 2 +- requirements-dev.txt | 6 +++--- tox.ini | 4 ++-- 17 files changed, 24 insertions(+), 25 deletions(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index 3368218..d6c3729 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -6,7 +6,7 @@ on: - '**' pull_request: branches: - - main + - '**' jobs: lint: @@ -70,4 +70,4 @@ jobs: pip3 install --upgrade coveralls coveralls --finish env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bf80633..b95e79d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 20.8b1 + rev: 22.1.0 hooks: - id: black args: [--line-length=79] diff --git a/CHANGELOG.md b/CHANGELOG.md index 824e7d2..8d20aae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -142,7 +142,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added NmapReport.endtimestr - Added and tested cElementTree support (performance) -### Fixed +### Fixed - Fixed bug when NmapReport.summary is empty @@ -199,10 +199,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added stop() to terminate nmap scan running in background -### Fixed +### Fixed - Bug corrected in missing data from nmap scan output - + ## [v0.3.1] - 17/06/2013 ### Changed diff --git a/CODEOWNERS b/CODEOWNERS index 7b644ee..78c6fef 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @savon-noir \ No newline at end of file +* @savon-noir diff --git a/LICENCE b/LICENCE index 92138ca..067f271 100644 --- a/LICENCE +++ b/LICENCE @@ -10,4 +10,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/NOTICE b/NOTICE index 88e053c..615671d 100644 --- a/NOTICE +++ b/NOTICE @@ -1,4 +1,4 @@ python-libnmap Copyright 2020 Ronald Bister -This product includes software developed by Ronald Bister \ No newline at end of file +This product includes software developed by Ronald Bister diff --git a/TODO b/TODO index 6aa4c65..bf03075 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,4 @@ -0.7.2: clean-up blacked code and pylint it +0.7.2: clean-up blacked code and pylint it 0.7.2: add unittest for defusedxml to fix billionlaugh and external entities security issues 0.7.2: Change License from CC-BY to Apache 2.0 0.7.2: Enabled defusedxml support as preferred option for parsing () @@ -10,7 +10,7 @@ release: - changelog date not respecting KACL specs - check https://github.com/anton-yurchenko/git-release -- https://github.com/sean0x42/markdown-extract +- https://github.com/sean0x42/markdown-extract Contribution file: - specify where version needs to be set before adding tag to commit diff --git a/config/database.yml b/config/database.yml index 7a28ee5..5c7a0f7 100644 --- a/config/database.yml +++ b/config/database.yml @@ -4,7 +4,6 @@ sqlite: timeout: 500 mysql: adapter: mysql2 - database: poulet + database: poulet username: encoding: utf8 - diff --git a/libnmap/test/files/1_host_ping.xml b/libnmap/test/files/1_host_ping.xml index 92fde18..0a0b797 100644 --- a/libnmap/test/files/1_host_ping.xml +++ b/libnmap/test/files/1_host_ping.xml @@ -16,4 +16,4 @@ - \ No newline at end of file + diff --git a/libnmap/test/files/2_hosts.json b/libnmap/test/files/2_hosts.json index f7099ea..d6e1727 100644 --- a/libnmap/test/files/2_hosts.json +++ b/libnmap/test/files/2_hosts.json @@ -1 +1 @@ -{"__NmapReport__": {"_nmaprun": {"scanner": "nmap", "args": "nmap -sS -vv -oX 2_hosts.xml localhost scanme.nmap.org", "start": "1361737906", "startstr": "Sun Feb 24 21:31:46 2013", "version": "5.51", "xmloutputversion": "1.03"}, "_scaninfo": {"type": "syn", "protocol": "tcp", "numservices": "1000", "services": "1,3-4,6-7,9,13,17,19-26,30,32-33,37,42-43,49,53,70,79-85,88-90,99-100,106,109-111,113,119,125,135,139,143-144,146,161,163,179,199,211-212,222,254-256,259,264,280,301,306,311,340,366,389,406-407,416-417,425,427,443-445,458,464-465,481,497,500,512-515,524,541,543-545,548,554-555,563,587,593,616-617,625,631,636,646,648,666-668,683,687,691,700,705,711,714,720,722,726,749,765,777,783,787,800-801,808,843,873,880,888,898,900-903,911-912,981,987,990,992-993,995,999-1002,1007,1009-1011,1021-1100,1102,1104-1108,1110-1114,1117,1119,1121-1124,1126,1130-1132,1137-1138,1141,1145,1147-1149,1151-1152,1154,1163-1166,1169,1174-1175,1183,1185-1187,1192,1198-1199,1201,1213,1216-1218,1233-1234,1236,1244,1247-1248,1259,1271-1272,1277,1287,1296,1300-1301,1309-1311,1322,1328,1334,1352,1417,1433-1434,1443,1455,1461,1494,1500-1501,1503,1521,1524,1533,1556,1580,1583,1594,1600,1641,1658,1666,1687-1688,1700,1717-1721,1723,1755,1761,1782-1783,1801,1805,1812,1839-1840,1862-1864,1875,1900,1914,1935,1947,1971-1972,1974,1984,1998-2010,2013,2020-2022,2030,2033-2035,2038,2040-2043,2045-2049,2065,2068,2099-2100,2103,2105-2107,2111,2119,2121,2126,2135,2144,2160-2161,2170,2179,2190-2191,2196,2200,2222,2251,2260,2288,2301,2323,2366,2381-2383,2393-2394,2399,2401,2492,2500,2522,2525,2557,2601-2602,2604-2605,2607-2608,2638,2701-2702,2710,2717-2718,2725,2800,2809,2811,2869,2875,2909-2910,2920,2967-2968,2998,3000-3001,3003,3005-3007,3011,3013,3017,3030-3031,3052,3071,3077,3128,3168,3211,3221,3260-3261,3268-3269,3283,3300-3301,3306,3322-3325,3333,3351,3367,3369-3372,3389-3390,3404,3476,3493,3517,3527,3546,3551,3580,3659,3689-3690,3703,3737,3766,3784,3800-3801,3809,3814,3826-3828,3851,3869,3871,3878,3880,3889,3905,3914,3918,3920,3945,3971,3986,3995,3998,4000-4006,4045,4111,4125-4126,4129,4224,4242,4279,4321,4343,4443-4446,4449,4550,4567,4662,4848,4899-4900,4998,5000-5004,5009,5030,5033,5050-5051,5054,5060-5061,5080,5087,5100-5102,5120,5190,5200,5214,5221-5222,5225-5226,5269,5280,5298,5357,5405,5414,5431-5432,5440,5500,5510,5544,5550,5555,5560,5566,5631,5633,5666,5678-5679,5718,5730,5800-5802,5810-5811,5815,5822,5825,5850,5859,5862,5877,5900-5904,5906-5907,5910-5911,5915,5922,5925,5950,5952,5959-5963,5987-5989,5998-6007,6009,6025,6059,6100-6101,6106,6112,6123,6129,6156,6346,6389,6502,6510,6543,6547,6565-6567,6580,6646,6666-6669,6689,6692,6699,6779,6788-6789,6792,6839,6881,6901,6969,7000-7002,7004,7007,7019,7025,7070,7100,7103,7106,7200-7201,7402,7435,7443,7496,7512,7625,7627,7676,7741,7777-7778,7800,7911,7920-7921,7937-7938,7999-8002,8007-8011,8021-8022,8031,8042,8045,8080-8090,8093,8099-8100,8180-8181,8192-8194,8200,8222,8254,8290-8292,8300,8333,8383,8400,8402,8443,8500,8600,8649,8651-8652,8654,8701,8800,8873,8888,8899,8994,9000-9003,9009-9011,9040,9050,9071,9080-9081,9090-9091,9099-9103,9110-9111,9200,9207,9220,9290,9415,9418,9485,9500,9502-9503,9535,9575,9593-9595,9618,9666,9876-9878,9898,9900,9917,9929,9943-9944,9968,9998-10004,10009-10010,10012,10024-10025,10082,10180,10215,10243,10566,10616-10617,10621,10626,10628-10629,10778,11110-11111,11967,12000,12174,12265,12345,13456,13722,13782-13783,14000,14238,14441-14442,15000,15002-15004,15660,15742,16000-16001,16012,16016,16018,16080,16113,16992-16993,17877,17988,18040,18101,18988,19101,19283,19315,19350,19780,19801,19842,20000,20005,20031,20221-20222,20828,21571,22939,23502,24444,24800,25734-25735,26214,27000,27352-27353,27355-27356,27715,28201,30000,30718,30951,31038,31337,32768-32785,33354,33899,34571-34573,35500,38292,40193,40911,41511,42510,44176,44442-44443,44501,45100,48080,49152-49161,49163,49165,49167,49175-49176,49400,49999-50003,50006,50300,50389,50500,50636,50800,51103,51493,52673,52822,52848,52869,54045,54328,55055-55056,55555,55600,56737-56738,57294,57797,58080,60020,60443,61532,61900,62078,63331,64623,64680,65000,65129,65389"}, "_hosts": [{"__NmapHost__": {"_starttime": "1361737906", "_endtime": "1361737906", "_hostnames": ["localhost", "localhost"], "_status": {"state": "up", "reason": "localhost-response"}, "_services": [{"__NmapService__": {"_portid": 22, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "ssh", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 25, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "smtp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 111, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "rpcbind", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 631, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "ipp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 3306, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "mysql", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}], "_extras": {"extraports": {"state": {"state": "closed", "count": "995"}, "count": {"state": "closed", "count": "995"}, "reasons": [{"reason": "resets", "count": "995"}]}, "times": {"srtt": "7", "rttvar": "0", "to": "100000"}}, "_osfingerprinted": false, "os": {"__NmapOSFingerprint__": {"_NmapOSFingerprint__osmatches": [], "_NmapOSFingerprint__ports_used": [], "_NmapOSFingerprint__fingerprints": []}}, "_ipv4_addr": "127.0.0.1", "_ipv6_addr": null, "_mac_addr": null, "_vendor": null, "_main_address": "127.0.0.1", "_address": [{"addr": "127.0.0.1", "addrtype": "ipv4"}]}}, {"__NmapHost__": {"_starttime": "1361737906", "_endtime": "1361738040", "_hostnames": ["scanme.nmap.org", "scanme.nmap.org"], "_status": {"state": "up", "reason": "echo-reply"}, "_services": [{"__NmapService__": {"_portid": 22, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "53"}, "_service": {"name": "ssh", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "53", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 25, "_protocol": "tcp", "_state": {"state": "filtered", "reason": "admin-prohibited", "reason_ttl": "253", "reason_ip": "109.133.192.1"}, "_service": {"name": "smtp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "admin-prohibited", "_reason_ip": "109.133.192.1", "_reason_ttl": "253", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 80, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "51"}, "_service": {"name": "http", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "51", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 9929, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "53"}, "_service": {"name": "nping-echo", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "53", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}], "_extras": {"extraports": {"state": {"state": "closed", "count": "996"}, "count": {"state": "closed", "count": "996"}, "reasons": [{"reason": "resets", "count": "996"}]}, "times": {"srtt": "177425", "rttvar": "1981", "to": "185349"}}, "_osfingerprinted": false, "os": {"__NmapOSFingerprint__": {"_NmapOSFingerprint__osmatches": [], "_NmapOSFingerprint__ports_used": [], "_NmapOSFingerprint__fingerprints": []}}, "_ipv4_addr": "74.207.244.221", "_ipv6_addr": null, "_mac_addr": null, "_vendor": null, "_main_address": "74.207.244.221", "_address": [{"addr": "74.207.244.221", "addrtype": "ipv4"}]}}], "_runstats": {"finished": {"time": "1361738040", "timestr": "Sun Feb 24 21:34:00 2013", "elapsed": "134.36", "summary": "Nmap done at Sun Feb 24 21:34:00 2013; 2 IP addresses (2 hosts up) scanned in 134.36 seconds", "exit": "success"}, "hosts": {"up": "2", "down": "0", "total": "2"}}}} \ No newline at end of file +{"__NmapReport__": {"_nmaprun": {"scanner": "nmap", "args": "nmap -sS -vv -oX 2_hosts.xml localhost scanme.nmap.org", "start": "1361737906", "startstr": "Sun Feb 24 21:31:46 2013", "version": "5.51", "xmloutputversion": "1.03"}, "_scaninfo": {"type": "syn", "protocol": "tcp", "numservices": "1000", "services": "1,3-4,6-7,9,13,17,19-26,30,32-33,37,42-43,49,53,70,79-85,88-90,99-100,106,109-111,113,119,125,135,139,143-144,146,161,163,179,199,211-212,222,254-256,259,264,280,301,306,311,340,366,389,406-407,416-417,425,427,443-445,458,464-465,481,497,500,512-515,524,541,543-545,548,554-555,563,587,593,616-617,625,631,636,646,648,666-668,683,687,691,700,705,711,714,720,722,726,749,765,777,783,787,800-801,808,843,873,880,888,898,900-903,911-912,981,987,990,992-993,995,999-1002,1007,1009-1011,1021-1100,1102,1104-1108,1110-1114,1117,1119,1121-1124,1126,1130-1132,1137-1138,1141,1145,1147-1149,1151-1152,1154,1163-1166,1169,1174-1175,1183,1185-1187,1192,1198-1199,1201,1213,1216-1218,1233-1234,1236,1244,1247-1248,1259,1271-1272,1277,1287,1296,1300-1301,1309-1311,1322,1328,1334,1352,1417,1433-1434,1443,1455,1461,1494,1500-1501,1503,1521,1524,1533,1556,1580,1583,1594,1600,1641,1658,1666,1687-1688,1700,1717-1721,1723,1755,1761,1782-1783,1801,1805,1812,1839-1840,1862-1864,1875,1900,1914,1935,1947,1971-1972,1974,1984,1998-2010,2013,2020-2022,2030,2033-2035,2038,2040-2043,2045-2049,2065,2068,2099-2100,2103,2105-2107,2111,2119,2121,2126,2135,2144,2160-2161,2170,2179,2190-2191,2196,2200,2222,2251,2260,2288,2301,2323,2366,2381-2383,2393-2394,2399,2401,2492,2500,2522,2525,2557,2601-2602,2604-2605,2607-2608,2638,2701-2702,2710,2717-2718,2725,2800,2809,2811,2869,2875,2909-2910,2920,2967-2968,2998,3000-3001,3003,3005-3007,3011,3013,3017,3030-3031,3052,3071,3077,3128,3168,3211,3221,3260-3261,3268-3269,3283,3300-3301,3306,3322-3325,3333,3351,3367,3369-3372,3389-3390,3404,3476,3493,3517,3527,3546,3551,3580,3659,3689-3690,3703,3737,3766,3784,3800-3801,3809,3814,3826-3828,3851,3869,3871,3878,3880,3889,3905,3914,3918,3920,3945,3971,3986,3995,3998,4000-4006,4045,4111,4125-4126,4129,4224,4242,4279,4321,4343,4443-4446,4449,4550,4567,4662,4848,4899-4900,4998,5000-5004,5009,5030,5033,5050-5051,5054,5060-5061,5080,5087,5100-5102,5120,5190,5200,5214,5221-5222,5225-5226,5269,5280,5298,5357,5405,5414,5431-5432,5440,5500,5510,5544,5550,5555,5560,5566,5631,5633,5666,5678-5679,5718,5730,5800-5802,5810-5811,5815,5822,5825,5850,5859,5862,5877,5900-5904,5906-5907,5910-5911,5915,5922,5925,5950,5952,5959-5963,5987-5989,5998-6007,6009,6025,6059,6100-6101,6106,6112,6123,6129,6156,6346,6389,6502,6510,6543,6547,6565-6567,6580,6646,6666-6669,6689,6692,6699,6779,6788-6789,6792,6839,6881,6901,6969,7000-7002,7004,7007,7019,7025,7070,7100,7103,7106,7200-7201,7402,7435,7443,7496,7512,7625,7627,7676,7741,7777-7778,7800,7911,7920-7921,7937-7938,7999-8002,8007-8011,8021-8022,8031,8042,8045,8080-8090,8093,8099-8100,8180-8181,8192-8194,8200,8222,8254,8290-8292,8300,8333,8383,8400,8402,8443,8500,8600,8649,8651-8652,8654,8701,8800,8873,8888,8899,8994,9000-9003,9009-9011,9040,9050,9071,9080-9081,9090-9091,9099-9103,9110-9111,9200,9207,9220,9290,9415,9418,9485,9500,9502-9503,9535,9575,9593-9595,9618,9666,9876-9878,9898,9900,9917,9929,9943-9944,9968,9998-10004,10009-10010,10012,10024-10025,10082,10180,10215,10243,10566,10616-10617,10621,10626,10628-10629,10778,11110-11111,11967,12000,12174,12265,12345,13456,13722,13782-13783,14000,14238,14441-14442,15000,15002-15004,15660,15742,16000-16001,16012,16016,16018,16080,16113,16992-16993,17877,17988,18040,18101,18988,19101,19283,19315,19350,19780,19801,19842,20000,20005,20031,20221-20222,20828,21571,22939,23502,24444,24800,25734-25735,26214,27000,27352-27353,27355-27356,27715,28201,30000,30718,30951,31038,31337,32768-32785,33354,33899,34571-34573,35500,38292,40193,40911,41511,42510,44176,44442-44443,44501,45100,48080,49152-49161,49163,49165,49167,49175-49176,49400,49999-50003,50006,50300,50389,50500,50636,50800,51103,51493,52673,52822,52848,52869,54045,54328,55055-55056,55555,55600,56737-56738,57294,57797,58080,60020,60443,61532,61900,62078,63331,64623,64680,65000,65129,65389"}, "_hosts": [{"__NmapHost__": {"_starttime": "1361737906", "_endtime": "1361737906", "_hostnames": ["localhost", "localhost"], "_status": {"state": "up", "reason": "localhost-response"}, "_services": [{"__NmapService__": {"_portid": 22, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "ssh", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 25, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "smtp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 111, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "rpcbind", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 631, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "ipp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 3306, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "mysql", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}], "_extras": {"extraports": {"state": {"state": "closed", "count": "995"}, "count": {"state": "closed", "count": "995"}, "reasons": [{"reason": "resets", "count": "995"}]}, "times": {"srtt": "7", "rttvar": "0", "to": "100000"}}, "_osfingerprinted": false, "os": {"__NmapOSFingerprint__": {"_NmapOSFingerprint__osmatches": [], "_NmapOSFingerprint__ports_used": [], "_NmapOSFingerprint__fingerprints": []}}, "_ipv4_addr": "127.0.0.1", "_ipv6_addr": null, "_mac_addr": null, "_vendor": null, "_main_address": "127.0.0.1", "_address": [{"addr": "127.0.0.1", "addrtype": "ipv4"}]}}, {"__NmapHost__": {"_starttime": "1361737906", "_endtime": "1361738040", "_hostnames": ["scanme.nmap.org", "scanme.nmap.org"], "_status": {"state": "up", "reason": "echo-reply"}, "_services": [{"__NmapService__": {"_portid": 22, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "53"}, "_service": {"name": "ssh", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "53", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 25, "_protocol": "tcp", "_state": {"state": "filtered", "reason": "admin-prohibited", "reason_ttl": "253", "reason_ip": "109.133.192.1"}, "_service": {"name": "smtp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "admin-prohibited", "_reason_ip": "109.133.192.1", "_reason_ttl": "253", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 80, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "51"}, "_service": {"name": "http", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "51", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 9929, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "53"}, "_service": {"name": "nping-echo", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "53", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}], "_extras": {"extraports": {"state": {"state": "closed", "count": "996"}, "count": {"state": "closed", "count": "996"}, "reasons": [{"reason": "resets", "count": "996"}]}, "times": {"srtt": "177425", "rttvar": "1981", "to": "185349"}}, "_osfingerprinted": false, "os": {"__NmapOSFingerprint__": {"_NmapOSFingerprint__osmatches": [], "_NmapOSFingerprint__ports_used": [], "_NmapOSFingerprint__fingerprints": []}}, "_ipv4_addr": "74.207.244.221", "_ipv6_addr": null, "_mac_addr": null, "_vendor": null, "_main_address": "74.207.244.221", "_address": [{"addr": "74.207.244.221", "addrtype": "ipv4"}]}}], "_runstats": {"finished": {"time": "1361738040", "timestr": "Sun Feb 24 21:34:00 2013", "elapsed": "134.36", "summary": "Nmap done at Sun Feb 24 21:34:00 2013; 2 IP addresses (2 hosts up) scanned in 134.36 seconds", "exit": "success"}, "hosts": {"up": "2", "down": "0", "total": "2"}}}} diff --git a/libnmap/test/files/defused_et_included.xml b/libnmap/test/files/defused_et_included.xml index 82e04b6..59472a0 100644 --- a/libnmap/test/files/defused_et_included.xml +++ b/libnmap/test/files/defused_et_included.xml @@ -3,4 +3,4 @@ text texttail - \ No newline at end of file + diff --git a/libnmap/test/files/defused_et_local_includer.xml b/libnmap/test/files/defused_et_local_includer.xml index d063ae6..6e90be8 100644 --- a/libnmap/test/files/defused_et_local_includer.xml +++ b/libnmap/test/files/defused_et_local_includer.xml @@ -2,4 +2,4 @@ - \ No newline at end of file + diff --git a/libnmap/test/files/diff_1_host_ping_mac_changed.xml b/libnmap/test/files/diff_1_host_ping_mac_changed.xml index 91d0429..be92c66 100644 --- a/libnmap/test/files/diff_1_host_ping_mac_changed.xml +++ b/libnmap/test/files/diff_1_host_ping_mac_changed.xml @@ -16,4 +16,4 @@ - \ No newline at end of file + diff --git a/libnmap/test/files/dionaea_scan.xml b/libnmap/test/files/dionaea_scan.xml index f891430..14e3b26 100644 --- a/libnmap/test/files/dionaea_scan.xml +++ b/libnmap/test/files/dionaea_scan.xml @@ -342,4 +342,4 @@ - \ No newline at end of file + diff --git a/libnmap/test/files/test_osclass.xml b/libnmap/test/files/test_osclass.xml index bcfd9c6..68850bb 100644 --- a/libnmap/test/files/test_osclass.xml +++ b/libnmap/test/files/test_osclass.xml @@ -43,4 +43,4 @@ - \ No newline at end of file + diff --git a/requirements-dev.txt b/requirements-dev.txt index aaeebb6..2fbfb2f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,6 @@ -black==20.8b1 -defusedxml==0.6.0 -isort==5.6.4 +black==20.8b1 +defusedxml==0.6.0 +isort==5.6.4 pre-commit pytest pytest-cov diff --git a/tox.ini b/tox.ini index 45ba18a..f86d9d3 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ envlist = py27, py32, py38, flake8, pycodestyle, formatting, defusedxml, coveral [testenv] deps=pytest pytest-cov -commands=pytest --cov --cov-report term-missing --ignore=libnmap/test/test_backend_plugin_factory.py --ignore=libnmap/test/test_defusedxml.py +commands=pytest --cov --cov-report term-missing --ignore=libnmap/test/test_backend_plugin_factory.py --ignore=libnmap/test/test_defusedxml.py [testenv:defusedxml] deps=pytest @@ -29,7 +29,7 @@ deps = pycodestyle commands = pycodestyle --exclude test,docs,examples,.tox . - + [testenv:formatting] deps = #black==20.8b1 From 2388af3637b58b849c832ef71ff8e931b1ba292c Mon Sep 17 00:00:00 2001 From: savon-noir Date: Thu, 1 Sep 2022 22:05:06 +0200 Subject: [PATCH 175/206] fix(lint): try to fix linting error from psf/black --- .github/workflows/preflight_check.yaml | 1 + .pre-commit-config.yaml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index d6c3729..e0e3022 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -25,6 +25,7 @@ jobs: python -m pip install --upgrade pip pip install black isort flake8 - name: Format checker with psf/black + uses: psf/black@22.8.0 run: black --check -l 79 . - name: Format checker with isort run: isort --check-only -m 3 -l 79 --profile=black . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b95e79d..cb54803 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 22.1.0 + rev: 22.8.0 hooks: - id: black args: [--line-length=79] From 1279ccae209fd30bdd3ac156e0922fafd4aaca4b Mon Sep 17 00:00:00 2001 From: savon-noir Date: Thu, 1 Sep 2022 22:15:36 +0200 Subject: [PATCH 176/206] fix(lint): try to fix linting error from psf/black --- .github/workflows/preflight_check.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index e0e3022..1c55e3a 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -25,8 +25,10 @@ jobs: python -m pip install --upgrade pip pip install black isort flake8 - name: Format checker with psf/black - uses: psf/black@22.8.0 - run: black --check -l 79 . + uses: psf/black@stable + with: + options: "--check -l 79" + version: "28.8.0" - name: Format checker with isort run: isort --check-only -m 3 -l 79 --profile=black . - name: Lint with flake8 From f34abce35c642423934e6992401dda0fae24d9d5 Mon Sep 17 00:00:00 2001 From: savon-noir Date: Thu, 1 Sep 2022 22:18:24 +0200 Subject: [PATCH 177/206] fix(lint): try to fix linting error from psf/black --- .github/workflows/preflight_check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index 1c55e3a..0273eb0 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -28,7 +28,7 @@ jobs: uses: psf/black@stable with: options: "--check -l 79" - version: "28.8.0" + version: "22.8.0" - name: Format checker with isort run: isort --check-only -m 3 -l 79 --profile=black . - name: Lint with flake8 From 20ef8ff06e9da71c70ed8816090a9cc157ffb604 Mon Sep 17 00:00:00 2001 From: savon-noir Date: Thu, 1 Sep 2022 22:22:49 +0200 Subject: [PATCH 178/206] fix(lint): try to fix linting error from psf/black --- .github/workflows/preflight_check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index 0273eb0..975ac41 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -27,7 +27,7 @@ jobs: - name: Format checker with psf/black uses: psf/black@stable with: - options: "--check -l 79" + options: "--check -l 79 --exclude docs/" version: "22.8.0" - name: Format checker with isort run: isort --check-only -m 3 -l 79 --profile=black . From 41a995a264ef694007e991b40a74b574310b1b80 Mon Sep 17 00:00:00 2001 From: savon-noir Date: Thu, 1 Sep 2022 22:27:41 +0200 Subject: [PATCH 179/206] fix(lint): try to fix linting error from coverall --- .github/workflows/preflight_check.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index 975ac41..ea25bfd 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -61,7 +61,7 @@ jobs: COVERALLS_FLAG_NAME: ${{ matrix.python-version }} COVERALLS_PARALLEL: true run: | - coveralls + coveralls --service=gihtub coveralls: name: Finish Coveralls needs: test From 6fe33e7460f4e0feab5102eda46b37d766b2f539 Mon Sep 17 00:00:00 2001 From: savon-noir Date: Thu, 1 Sep 2022 22:34:24 +0200 Subject: [PATCH 180/206] fix(lint): try to fix linting error from coveralls --- .github/workflows/preflight_check.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index ea25bfd..f1643f9 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -61,7 +61,7 @@ jobs: COVERALLS_FLAG_NAME: ${{ matrix.python-version }} COVERALLS_PARALLEL: true run: | - coveralls --service=gihtub + coveralls --service=github coveralls: name: Finish Coveralls needs: test @@ -71,6 +71,6 @@ jobs: - name: Finished run: | pip3 install --upgrade coveralls - coveralls --finish + coveralls --finish --service=github env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 3a4957124901b36bd37cd0c6334d9dd0db2c5f16 Mon Sep 17 00:00:00 2001 From: savon-noir Date: Thu, 1 Sep 2022 23:10:05 +0200 Subject: [PATCH 181/206] fix: updated changelog --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d20aae..d3fd9ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). (or tries to...) +## [v0.7.3] 2022-09-01 + +### Fixed + +- Linting and coveralls issues + +### Security + +- Fix for security issue on arguments injections - [CVE-2022-30284](https://nvd.nist.gov/vuln/detail/CVE-2022-30284) + ## [v0.7.2] 2020-12-16 ### Added From 37092bd825eeccaf3081b15b25f23294a94cf1ac Mon Sep 17 00:00:00 2001 From: savon-noir Date: Thu, 1 Sep 2022 23:32:34 +0200 Subject: [PATCH 182/206] fix: fixed version and documentation for v0.7.3 --- libnmap/objects/report.py | 5 +++++ libnmap/process.py | 24 +++++++++++++++++++----- setup.py | 2 +- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/libnmap/objects/report.py b/libnmap/objects/report.py index 8c69eb4..3a18ed6 100644 --- a/libnmap/objects/report.py +++ b/libnmap/objects/report.py @@ -411,6 +411,11 @@ def __ne__(self, other): return rval def __repr__(self): + """ + Returns a string-based representation of the report + + :return: string + """ return "{0}: started at {1} hosts up {2}/{3}".format( self.__class__.__name__, self.started, diff --git a/libnmap/process.py b/libnmap/process.py index d1aba76..d53854f 100644 --- a/libnmap/process.py +++ b/libnmap/process.py @@ -486,8 +486,24 @@ def __build_windows_cmdline(self): @staticmethod def __validate_target(target): - # See https://nmap.org/book/man-target-specification.html for all the - # ways targets can be specified + """ + Check if a provided target is valid. This function was created + in order to address CVE-2022-30284 + + See https://nmap.org/book/man-target-specification.html for all the + ways targets can be specified + + This function verifies the following: + + - matches the user specified target against a list of allowed chars + - check if dashes are used at the start or at the end of target + + FQDN can contain dashes anywhere except at the beginning or end + This check also fixes/prevents CVE-2022-30284, which depends on being + able to pass options such as --script as a target + + :return: False if target contains forbidden characters + """ allowed_characters = frozenset( string.ascii_letters + string.digits + "-.:/% " ) @@ -495,15 +511,13 @@ def __validate_target(target): raise Exception( "Target '{}' contains invalid characters".format(target) ) - # FQDN can contain dashes anywhere except at the beginning or end - # This check also fixes/prevents CVE-2022-30284, which depends on being - # able to pass options such as --script as a target elif target.startswith("-") or target.endswith("-"): raise Exception( "Target '{}' cannot begin or end with a dash ('-')".format( target ) ) + return True @property def command(self): diff --git a/setup.py b/setup.py index 9668371..d6427c6 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ setup( name="python-libnmap", - version="0.7.2", + version="0.7.3", author="Ronald Bister", author_email="mini.pelle@gmail.com", packages=["libnmap", "libnmap.plugins", "libnmap.objects"], From 16d23c07754d06b1c51e8c4f9db60666b3f93a4c Mon Sep 17 00:00:00 2001 From: dimeko Date: Thu, 4 Apr 2024 13:22:53 +0300 Subject: [PATCH 183/206] Adds trace key --- libnmap/parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/libnmap/parser.py b/libnmap/parser.py index 21f6a12..1d40e07 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -305,6 +305,7 @@ def _parse_xml_host(cls, scanhost_data): "tcpsequence", "ipidsequence", "tcptssequence", + "trace" "times", ] for xh in xelement: From d21dc98019153768642b7ad9bc2c5bfae1753bcc Mon Sep 17 00:00:00 2001 From: dimeko Date: Thu, 4 Apr 2024 13:26:35 +0300 Subject: [PATCH 184/206] Small change --- libnmap/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnmap/parser.py b/libnmap/parser.py index 1d40e07..b99e6ae 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -305,7 +305,7 @@ def _parse_xml_host(cls, scanhost_data): "tcpsequence", "ipidsequence", "tcptssequence", - "trace" + "trace", "times", ] for xh in xelement: From f17139cde1d3c134303d480d5343e2658544eae7 Mon Sep 17 00:00:00 2001 From: dimeko Date: Thu, 4 Apr 2024 14:29:59 +0300 Subject: [PATCH 185/206] Adds trace parsing --- libnmap/parser.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/libnmap/parser.py b/libnmap/parser.py index b99e6ae..7907b5b 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -327,6 +327,9 @@ def _parse_xml_host(cls, scanhost_data): elif xh.tag == "hostscript": _host_scripts = cls.__parse_host_scripts(xh) _host_extras.update({"hostscript": _host_scripts}) + elif xh.tag == "trace": + _trace = cls.__parse_trace(xh) + _host_extras.update({"trace": _trace}) elif xh.tag in extra_tags: _host_extras[xh.tag] = cls.__format_attributes(xh) # else: @@ -681,6 +684,30 @@ def __parse_runstats(cls, scanrunstats_data): return rdict + @classmethod + def __parse_trace(cls, scantrace_data): + """ + Private method parsing a portion of a nmap scan result. + Receives a XML tag. + + :param scantrace_data: XML tag from a nmap scan + :type scantrace_data: xml.ElementTree.Element or a string + + :return: python dict representing the XML trace tag + """ + + xelement = cls.__format_element(scantrace_data) + + rdict = {} + for xmltag in xelement: + if xmltag.tag in ["port", "proto", "hop"]: + rdict[xmltag.tag] = cls.__format_attributes(xmltag) + else: + exmsg = "Unexcepted node in : {0}".format(xmltag.tag) + raise NmapParserException(exmsg) + + return rdict + @staticmethod def __format_element(elt_data): """ From 53072527065fa193c4417f91e08efc4da4b274f1 Mon Sep 17 00:00:00 2001 From: dimeko Date: Thu, 4 Apr 2024 15:26:26 +0300 Subject: [PATCH 186/206] Adds more fields to tarce --- libnmap/parser.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/libnmap/parser.py b/libnmap/parser.py index 7907b5b..dd86884 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -675,6 +675,7 @@ def __parse_runstats(cls, scanrunstats_data): xelement = cls.__format_element(scanrunstats_data) rdict = {} + rdict["port"] for xmltag in xelement: if xmltag.tag in ["finished", "hosts"]: rdict[xmltag.tag] = cls.__format_attributes(xmltag) @@ -697,11 +698,15 @@ def __parse_trace(cls, scantrace_data): """ xelement = cls.__format_element(scantrace_data) + _trace_attrs = cls.__format_attributes(xelement) rdict = {} + rdict["port"] = _trace_attrs["port"] + rdict["proto"] = _trace_attrs["proto"] + rdict["hops"] = [] for xmltag in xelement: - if xmltag.tag in ["port", "proto", "hop"]: - rdict[xmltag.tag] = cls.__format_attributes(xmltag) + if xmltag.tag in ["hop"]: + rdict["hops"].append(cls.__format_attributes(xmltag)) else: exmsg = "Unexcepted node in : {0}".format(xmltag.tag) raise NmapParserException(exmsg) From 3a4fa75da4688f5beef06a6bc6f59f58c5d1537b Mon Sep 17 00:00:00 2001 From: dimeko Date: Thu, 4 Apr 2024 15:36:44 +0300 Subject: [PATCH 187/206] WIP: fix --- libnmap/parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/libnmap/parser.py b/libnmap/parser.py index dd86884..718dac6 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -675,7 +675,6 @@ def __parse_runstats(cls, scanrunstats_data): xelement = cls.__format_element(scanrunstats_data) rdict = {} - rdict["port"] for xmltag in xelement: if xmltag.tag in ["finished", "hosts"]: rdict[xmltag.tag] = cls.__format_attributes(xmltag) From a4b355b7f5b10ebbc015e0748fedcfb1f6af743b Mon Sep 17 00:00:00 2001 From: dimeko Date: Wed, 10 Apr 2024 21:46:20 +0300 Subject: [PATCH 188/206] Adds conditions --- libnmap/parser.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/libnmap/parser.py b/libnmap/parser.py index 718dac6..4bb9123 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -700,8 +700,12 @@ def __parse_trace(cls, scantrace_data): _trace_attrs = cls.__format_attributes(xelement) rdict = {} - rdict["port"] = _trace_attrs["port"] - rdict["proto"] = _trace_attrs["proto"] + if "proto" not in _trace_attrs: + rdict["port"] = _trace_attrs["port"] + + if "port" not in _trace_attrs: + rdict["port"] = _trace_attrs["proto"] + rdict["hops"] = [] for xmltag in xelement: if xmltag.tag in ["hop"]: From b9a42aa8bd5e446bb06460808f91c54e617efcdb Mon Sep 17 00:00:00 2001 From: dimeko Date: Tue, 16 Apr 2024 15:32:12 +0300 Subject: [PATCH 189/206] Fixes proto and port retrieval --- libnmap/parser.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libnmap/parser.py b/libnmap/parser.py index 4bb9123..52d78b7 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -700,11 +700,12 @@ def __parse_trace(cls, scantrace_data): _trace_attrs = cls.__format_attributes(xelement) rdict = {} - if "proto" not in _trace_attrs: - rdict["port"] = _trace_attrs["port"] - if "port" not in _trace_attrs: - rdict["port"] = _trace_attrs["proto"] + if "proto" in _trace_attrs: + rdict["proto"] = _trace_attrs["proto"] + + if "port" in _trace_attrs: + rdict["port"] = _trace_attrs["port"] rdict["hops"] = [] for xmltag in xelement: From d6f484fc8a4bb766eec18013c01feea1ec93c9e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:50:20 +0000 Subject: [PATCH 190/206] Bump black from 20.8b1 to 24.3.0 Bumps [black](https://github.com/psf/black) from 20.8b1 to 24.3.0. - [Release notes](https://github.com/psf/black/releases) - [Changelog](https://github.com/psf/black/blob/main/CHANGES.md) - [Commits](https://github.com/psf/black/commits/24.3.0) --- updated-dependencies: - dependency-name: black dependency-type: direct:development ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 2fbfb2f..56c9a07 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ -black==20.8b1 +black==24.3.0 defusedxml==0.6.0 isort==5.6.4 pre-commit From ed0619e9bb9648ce99d52c447c5dde8b6f5449d1 Mon Sep 17 00:00:00 2001 From: Alexandre ZANNI <16578570+noraj@users.noreply.github.com> Date: Fri, 7 Jul 2023 14:33:53 +0200 Subject: [PATCH 191/206] fix python 2 compat fix #138 --- setup.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index d6427c6..a2ca71f 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,13 @@ # -*- coding: utf-8 -*- from distutils.core import setup +import sys -with open("README.rst", encoding="utf-8") as rfile: - long_description = rfile.read() +if sys.version_info >= (3,0): + with open("README.rst", encoding="utf-8") as rfile: + long_description = rfile.read() +else: # encoding not compatible with python2 + with open("README.rst") as rfile: + long_description = rfile.read() setup( name="python-libnmap", From 6c0ee1ab3c5962cc124f8c4c7d8f51f0591c989f Mon Sep 17 00:00:00 2001 From: Martin Schobert Date: Tue, 12 Dec 2023 17:02:37 +0100 Subject: [PATCH 192/206] Update parser.py documentation with a warning/clarification. --- libnmap/parser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libnmap/parser.py b/libnmap/parser.py index 52d78b7..67ff616 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -34,7 +34,9 @@ def parse(cls, nmap_data=None, data_type="XML", incomplete=False): :param incomplete: enable you to parse interrupted nmap scans \ and/or incomplete nmap xml blocks by adding a at \ - the end of the scan. + the end of the scan. Be aware that this flag does not work for \ + already valid XML files, because adding an XML tag will \ + invalidate the XML. :type incomplete: boolean As of today, only XML parsing is supported. From e11ff0649bbfa7358fc7ee86c2bf8da841b604c3 Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Sun, 19 Jan 2025 22:22:25 +0100 Subject: [PATCH 193/206] fix: added versions for python envs --- .github/workflows/preflight_check.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index f1643f9..284998e 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8] + python-version: [3.6.15, 3.7.17, 3.8.18] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [2.7, 3.5, 3.6, 3.7, 3.8] + python-version: [3.5.10, 3.6.15, 3.7.17, 3.8.18] steps: - uses: actions/checkout@v2 - name: Setup Python ${{ matrix.python-version }} From 61fb48a6d530b9fcf1afe5e9a601f2ae463f064b Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Sun, 19 Jan 2025 22:33:12 +0100 Subject: [PATCH 194/206] fix: updated lib version for checks --- .github/workflows/preflight_check.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index 284998e..1487df0 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -15,9 +15,9 @@ jobs: matrix: python-version: [3.6.15, 3.7.17, 3.8.18] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -39,9 +39,9 @@ jobs: matrix: python-version: [3.5.10, 3.6.15, 3.7.17, 3.8.18] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Setup Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Setup Environment From 8bca24a76d0df82ead202c9b274fcd05106335cc Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Sun, 19 Jan 2025 22:53:19 +0100 Subject: [PATCH 195/206] fix: fixed pyblack codefmt --- .github/workflows/preflight_check.yaml | 2 +- libnmap/objects/os.py | 1 - libnmap/process.py | 2 -- setup.py | 4 ++-- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index 1487df0..a7f9314 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -28,7 +28,7 @@ jobs: uses: psf/black@stable with: options: "--check -l 79 --exclude docs/" - version: "22.8.0" + version: "24.10.0" - name: Format checker with isort run: isort --check-only -m 3 -l 79 --profile=black . - name: Lint with flake8 diff --git a/libnmap/objects/os.py b/libnmap/objects/os.py index 30d3f3b..2d2e109 100644 --- a/libnmap/objects/os.py +++ b/libnmap/objects/os.py @@ -89,7 +89,6 @@ def __init__(self, osmatch_dict): pass def add_osclass(self, osclass_obj): - """ Add a NmapOSClass object to the OSMatch object. This method is useful to implement compatibility with older versions of NMAP diff --git a/libnmap/process.py b/libnmap/process.py index d53854f..a2825cc 100644 --- a/libnmap/process.py +++ b/libnmap/process.py @@ -20,7 +20,6 @@ class NmapTask(object): - """ NmapTask is a internal class used by process. Each time nmap starts a new task during the scan, a new class will be instantiated. @@ -46,7 +45,6 @@ def __init__(self, name, starttime=0, extrainfo=""): class NmapProcess(Thread): - """ NmapProcess is a class which wraps around the nmap executable. diff --git a/setup.py b/setup.py index a2ca71f..5fa816c 100644 --- a/setup.py +++ b/setup.py @@ -2,10 +2,10 @@ from distutils.core import setup import sys -if sys.version_info >= (3,0): +if sys.version_info >= (3, 0): with open("README.rst", encoding="utf-8") as rfile: long_description = rfile.read() -else: # encoding not compatible with python2 +else: # encoding not compatible with python2 with open("README.rst") as rfile: long_description = rfile.read() From 23528164cff23f7ec5083c22f23907fdaf5142cd Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Sun, 19 Jan 2025 22:54:28 +0100 Subject: [PATCH 196/206] fix: isort in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 5fa816c..eb5c0f5 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from distutils.core import setup import sys +from distutils.core import setup if sys.version_info >= (3, 0): with open("README.rst", encoding="utf-8") as rfile: From 2e8ed2dd0617846b6c2191d63e91531929d91703 Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Sun, 19 Jan 2025 22:59:12 +0100 Subject: [PATCH 197/206] fix: requirements file --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 56c9a07..cb81983 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ -black==24.3.0 +black==24.10.0 defusedxml==0.6.0 isort==5.6.4 pre-commit From ff63931bd3941719dfb7c5a39bb2819d32682524 Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Sun, 19 Jan 2025 23:53:01 +0100 Subject: [PATCH 198/206] fix: gh actions fix bllint issue --- .github/workflows/preflight_check.yaml | 12 ++++++------ requirements-dev.txt | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index a7f9314..5cf7b87 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -10,10 +10,10 @@ on: jobs: lint: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: - python-version: [3.6.15, 3.7.17, 3.8.18] + python-version: ["3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} @@ -28,16 +28,16 @@ jobs: uses: psf/black@stable with: options: "--check -l 79 --exclude docs/" - version: "24.10.0" + version: "22.8.0" - name: Format checker with isort run: isort --check-only -m 3 -l 79 --profile=black . - name: Lint with flake8 run: flake8 --exclude test,docs,examples . test: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: - python-version: [3.5.10, 3.6.15, 3.7.17, 3.8.18] + python-version: ["3.7", "3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v4 - name: Setup Python ${{ matrix.python-version }} @@ -65,7 +65,7 @@ jobs: coveralls: name: Finish Coveralls needs: test - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 container: python:3-slim steps: - name: Finished diff --git a/requirements-dev.txt b/requirements-dev.txt index cb81983..d63813a 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,4 +1,4 @@ -black==24.10.0 +black==22.8.0 defusedxml==0.6.0 isort==5.6.4 pre-commit From 6c69c191b4ed7b3161120cd2d3ec14980289524c Mon Sep 17 00:00:00 2001 From: Ronald Date: Mon, 6 Apr 2015 23:35:04 +0200 Subject: [PATCH 199/206] temporary fix for issue40 --- libnmap/objects/__init__.py | 4 ++-- libnmap/objects/host.py | 10 ++++---- libnmap/objects/service.py | 48 +++++++++++++++++++++++++++++++++++++ libnmap/parser.py | 20 ++++++++-------- 4 files changed, 66 insertions(+), 16 deletions(-) diff --git a/libnmap/objects/__init__.py b/libnmap/objects/__init__.py index ddf3699..9cafae9 100644 --- a/libnmap/objects/__init__.py +++ b/libnmap/objects/__init__.py @@ -2,6 +2,6 @@ from libnmap.objects.host import NmapHost from libnmap.objects.report import NmapReport -from libnmap.objects.service import NmapService +from libnmap.objects.service import NmapService, NmapExtraPort -__all__ = ["NmapReport", "NmapHost", "NmapService"] +__all__ = ['NmapReport', 'NmapHost', 'NmapService', 'NmapExtraPort'] diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index 9425e2e..99be06f 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -38,6 +38,7 @@ def __init__( self._status = status if status is not None else {} self._services = services if services is not None else [] self._extras = extras if extras is not None else {} + self._extraports = self._extras.get('extraports', None) self._osfingerprinted = False self.os = None if "os" in self._extras: @@ -469,12 +470,13 @@ def extraports_state(self): :return: dict with keys 'state' and 'count' or None """ - _xtrports = self._extras.get("extraports", None) + rval = None + _xports = self._extras.get('extraports', None) - if _xtrports is None: - return None + if _xports is not None: + rval = {'state': _xtrports['state'], 'count': _xtrports['count']} - return {"state": _xtrports["state"], "count": _xtrports["count"]} + return rval @property def extraports_reasons(self): diff --git a/libnmap/objects/service.py b/libnmap/objects/service.py index e254a66..a21b1af 100644 --- a/libnmap/objects/service.py +++ b/libnmap/objects/service.py @@ -366,3 +366,51 @@ def diff(self, other): :return: NmapDiff object """ return NmapDiff(self, other) + + +class NmapExtraPort(object): + """ + NmapExtraPort is an object which documents unlisted ports/services + which are possibly closed, filtered, ignored,... + """ + def __init__(self, xdict): + """ + Constructor + :param xdict: python dict containing the following structure: + { + 'state': , + 'count': , + 'reasons': [{'reason': , 'count' }] + } + """ + self._count = xdict.get('count', 0) + self._state = xdict.get('state', 'unknown') + self._reasons = xdict.get('reasons', []) + + @property + def count(self): + """ + Accessor for the number of extraports + + :return: int + """ + return int(self._count) + + @property + def state(self): + """ + Accessor for the state of extraports listed + + :return: string + """ + return self._state + + @property + def reason(self): + """ + Return the first reason available for the extraport listed. + + :return: dict, empty if no extraports reason available + """ + + return self.reasons[0] if len(self.reasons) else {} diff --git a/libnmap/parser.py b/libnmap/parser.py index 67ff616..1c5be59 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -8,10 +8,8 @@ import xml.etree.cElementTree as ET except ImportError: import xml.etree.ElementTree as ET - from xml.etree.ElementTree import iselement as et_iselement - -from libnmap.objects import NmapHost, NmapReport, NmapService +from libnmap.objects import NmapHost, NmapService, NmapReport, NmapExtraPort class NmapParser(object): @@ -385,17 +383,18 @@ def _parse_xml_ports(cls, scanports_data): xelement = cls.__format_element(scanports_data) - rdict = {"ports": [], "extraports": None} + rdict = {'ports': [], 'extraports': []} for xservice in xelement: if xservice.tag == "port": nport = cls._parse_xml_port(xservice) rdict["ports"].append(nport) elif xservice.tag == "extraports": extraports = cls.__parse_extraports(xservice) - rdict["extraports"] = extraports - # else: - # print "struct port unknown attr: %s value: %s" % - # (h.tag, h.get(h.tag)) + rdict['extraports'].append(extraports) + # DEBUG REMOVE ME + else: + print "struct port unknown attr: %s value: %s" % + (h.tag, h.get(h.tag)) return rdict @classmethod @@ -485,8 +484,9 @@ def __parse_extraports(cls, extraports_data): for xelt in xelement: if xelt.tag == "extrareasons": extrareasons_dict = cls.__format_attributes(xelt) - rdict["reasons"].append(extrareasons_dict) - return rdict + rdict['reasons'].append(extrareasons_dict) + robj = NmapExtraPort(rdict) + return robj @classmethod def __parse_script_table(cls, script_table): From 3b4cde5e76a86ceab894b0aa09517c6d0e9adb67 Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Mon, 27 Jan 2025 22:53:34 +0100 Subject: [PATCH 200/206] fix(extraports): added support for extra ports, fixes #40 --- libnmap/objects/host.py | 30 +-- libnmap/objects/service.py | 8 +- libnmap/parser.py | 10 +- libnmap/test/files/extra_ports.xml | 350 +++++++++++++++++++++++++++++ libnmap/test/test_extraports.py | 36 +++ 5 files changed, 396 insertions(+), 38 deletions(-) create mode 100644 libnmap/test/files/extra_ports.xml create mode 100644 libnmap/test/test_extraports.py diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index 99be06f..1058a8f 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -463,35 +463,13 @@ def id(self): return self.address @property - def extraports_state(self): + def extraports(self): """ - dictionnary containing state and amount of extra ports scanned - for which a common state, usually, closed was discovered. + Returns a list of NmapExtraPort objects - :return: dict with keys 'state' and 'count' or None + :return: list of NmapExtraPort objects """ - rval = None - _xports = self._extras.get('extraports', None) - - if _xports is not None: - rval = {'state': _xtrports['state'], 'count': _xtrports['count']} - - return rval - - @property - def extraports_reasons(self): - """ - dictionnary containing reasons why extra ports scanned - for which a common state, usually, closed was discovered. - - :return: array of dict containing keys 'state' and 'count' or None - """ - r = self._extras.get("extraports", {}) - - if r is None: - return None - - return r.get("reasons", None) + return self._extraports def get_dict(self): """ diff --git a/libnmap/objects/service.py b/libnmap/objects/service.py index a21b1af..ee5ea62 100644 --- a/libnmap/objects/service.py +++ b/libnmap/objects/service.py @@ -388,7 +388,7 @@ def __init__(self, xdict): self._reasons = xdict.get('reasons', []) @property - def count(self): + def extra_count(self): """ Accessor for the number of extraports @@ -397,7 +397,7 @@ def count(self): return int(self._count) @property - def state(self): + def extra_state(self): """ Accessor for the state of extraports listed @@ -406,11 +406,11 @@ def state(self): return self._state @property - def reason(self): + def extra_reasons(self): """ Return the first reason available for the extraport listed. :return: dict, empty if no extraports reason available """ - return self.reasons[0] if len(self.reasons) else {} + return self._reasons diff --git a/libnmap/parser.py b/libnmap/parser.py index 1c5be59..b714731 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -391,10 +391,6 @@ def _parse_xml_ports(cls, scanports_data): elif xservice.tag == "extraports": extraports = cls.__parse_extraports(xservice) rdict['extraports'].append(extraports) - # DEBUG REMOVE ME - else: - print "struct port unknown attr: %s value: %s" % - (h.tag, h.get(h.tag)) return rdict @classmethod @@ -477,10 +473,8 @@ def __parse_extraports(cls, extraports_data): xelement = cls.__format_element(extraports_data) extraports_dict = cls.__format_attributes(xelement) - if "state" in extraports_dict: - rdict["state"] = extraports_dict - if "count" in extraports_dict: - rdict["count"] = extraports_dict + rdict["state"] = extraports_dict.get("state", None) + rdict["count"] = extraports_dict.get("count", None) for xelt in xelement: if xelt.tag == "extrareasons": extrareasons_dict = cls.__format_attributes(xelt) diff --git a/libnmap/test/files/extra_ports.xml b/libnmap/test/files/extra_ports.xml new file mode 100644 index 0000000..26b650a --- /dev/null +++ b/libnmap/test/files/extra_ports.xml @@ -0,0 +1,350 @@ + + + + + + + + + + + + +cpe:/a:mysql:mysql:5.7.16 + + +cpe:/a:memcached:memcached:1.4.25 + + + + + + + + diff --git a/libnmap/test/test_extraports.py b/libnmap/test/test_extraports.py new file mode 100644 index 0000000..08552f1 --- /dev/null +++ b/libnmap/test/test_extraports.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import unittest + +from libnmap.parser import NmapParser, NmapParserException + + +class TestExtraPorts(unittest.TestCase): + def setUp(self): + fdir = os.path.dirname(os.path.realpath(__file__)) + _extrareason = [{'reason': 'filtered', 'count': '3'}, {'reason': 'resets', 'count': '7'}] + self.flist = [ + {"path": "%s/%s" % (fdir, "files/extra_ports.xml"), "extrareason": _extrareason} + ] + + def test_extraports(self): + for fentry in self.flist: + rep1 = NmapParser.parse_fromfile(fentry["path"]) + ep_list = rep1.hosts[0].extraports + self.assertEqual(len(ep_list), 2) + self.assertEqual(ep_list[0].extra_count, 65509) + self.assertEqual(ep_list[0].extra_state, 'closed') + self.assertEqual(len(ep_list[0].extra_reasons), 1) + self.assertEqual(ep_list[1].extra_count, 10) + self.assertEqual(len(ep_list[1].extra_reasons), 2) + self.assertEqual(ep_list[1].extra_reasons, fentry["extrareason"]) + + +if __name__ == "__main__": + test_suite = [ + "test_extraports", + ] + suite = unittest.TestSuite(map(TestExtraPorts, test_suite)) + test_result = unittest.TextTestRunner(verbosity=2).run(suite) From 24d6013c304973d5dcd29ff48ccecc7a2bf717e0 Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Mon, 27 Jan 2025 23:07:06 +0100 Subject: [PATCH 201/206] fix: fixed pylint issues --- .pre-commit-config.yaml | 1 + libnmap/objects/__init__.py | 4 ++-- libnmap/objects/host.py | 2 +- libnmap/objects/service.py | 37 +++++++++++++++++---------------- libnmap/parser.py | 10 +++++---- libnmap/test/test_extraports.py | 12 ++++++++--- 6 files changed, 38 insertions(+), 28 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cb54803..3e900c4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,3 +21,4 @@ repos: rev: 3.8.4 hooks: - id: flake8 + exclude: ^libnmap/(test/|docs/|examples/) diff --git a/libnmap/objects/__init__.py b/libnmap/objects/__init__.py index 9cafae9..889e1d3 100644 --- a/libnmap/objects/__init__.py +++ b/libnmap/objects/__init__.py @@ -2,6 +2,6 @@ from libnmap.objects.host import NmapHost from libnmap.objects.report import NmapReport -from libnmap.objects.service import NmapService, NmapExtraPort +from libnmap.objects.service import NmapExtraPort, NmapService -__all__ = ['NmapReport', 'NmapHost', 'NmapService', 'NmapExtraPort'] +__all__ = ["NmapReport", "NmapHost", "NmapService", "NmapExtraPort"] diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index 1058a8f..95734ea 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -38,7 +38,7 @@ def __init__( self._status = status if status is not None else {} self._services = services if services is not None else [] self._extras = extras if extras is not None else {} - self._extraports = self._extras.get('extraports', None) + self._extraports = self._extras.get("extraports", None) self._osfingerprinted = False self.os = None if "os" in self._extras: diff --git a/libnmap/objects/service.py b/libnmap/objects/service.py index ee5ea62..30c66f7 100644 --- a/libnmap/objects/service.py +++ b/libnmap/objects/service.py @@ -370,47 +370,48 @@ def diff(self, other): class NmapExtraPort(object): """ - NmapExtraPort is an object which documents unlisted ports/services - which are possibly closed, filtered, ignored,... + NmapExtraPort is an object which documents unlisted ports/services + which are possibly closed, filtered, ignored,... """ + def __init__(self, xdict): """ - Constructor - :param xdict: python dict containing the following structure: - { - 'state': , - 'count': , - 'reasons': [{'reason': , 'count' }] - } + Constructor + :param xdict: python dict containing the following structure: + { + 'state': , + 'count': , + 'reasons': [{'reason': , 'count' }] + } """ - self._count = xdict.get('count', 0) - self._state = xdict.get('state', 'unknown') - self._reasons = xdict.get('reasons', []) + self._count = xdict.get("count", 0) + self._state = xdict.get("state", "unknown") + self._reasons = xdict.get("reasons", []) @property def extra_count(self): """ - Accessor for the number of extraports + Accessor for the number of extraports - :return: int + :return: int """ return int(self._count) @property def extra_state(self): """ - Accessor for the state of extraports listed + Accessor for the state of extraports listed - :return: string + :return: string """ return self._state @property def extra_reasons(self): """ - Return the first reason available for the extraport listed. + Return the first reason available for the extraport listed. - :return: dict, empty if no extraports reason available + :return: dict, empty if no extraports reason available """ return self._reasons diff --git a/libnmap/parser.py b/libnmap/parser.py index b714731..9b12e12 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -8,8 +8,10 @@ import xml.etree.cElementTree as ET except ImportError: import xml.etree.ElementTree as ET + from xml.etree.ElementTree import iselement as et_iselement -from libnmap.objects import NmapHost, NmapService, NmapReport, NmapExtraPort + +from libnmap.objects import NmapExtraPort, NmapHost, NmapReport, NmapService class NmapParser(object): @@ -383,14 +385,14 @@ def _parse_xml_ports(cls, scanports_data): xelement = cls.__format_element(scanports_data) - rdict = {'ports': [], 'extraports': []} + rdict = {"ports": [], "extraports": []} for xservice in xelement: if xservice.tag == "port": nport = cls._parse_xml_port(xservice) rdict["ports"].append(nport) elif xservice.tag == "extraports": extraports = cls.__parse_extraports(xservice) - rdict['extraports'].append(extraports) + rdict["extraports"].append(extraports) return rdict @classmethod @@ -478,7 +480,7 @@ def __parse_extraports(cls, extraports_data): for xelt in xelement: if xelt.tag == "extrareasons": extrareasons_dict = cls.__format_attributes(xelt) - rdict['reasons'].append(extrareasons_dict) + rdict["reasons"].append(extrareasons_dict) robj = NmapExtraPort(rdict) return robj diff --git a/libnmap/test/test_extraports.py b/libnmap/test/test_extraports.py index 08552f1..c40308b 100644 --- a/libnmap/test/test_extraports.py +++ b/libnmap/test/test_extraports.py @@ -10,9 +10,15 @@ class TestExtraPorts(unittest.TestCase): def setUp(self): fdir = os.path.dirname(os.path.realpath(__file__)) - _extrareason = [{'reason': 'filtered', 'count': '3'}, {'reason': 'resets', 'count': '7'}] + _extrareason = [ + {"reason": "filtered", "count": "3"}, + {"reason": "resets", "count": "7"}, + ] self.flist = [ - {"path": "%s/%s" % (fdir, "files/extra_ports.xml"), "extrareason": _extrareason} + { + "path": "%s/%s" % (fdir, "files/extra_ports.xml"), + "extrareason": _extrareason, + } ] def test_extraports(self): @@ -21,7 +27,7 @@ def test_extraports(self): ep_list = rep1.hosts[0].extraports self.assertEqual(len(ep_list), 2) self.assertEqual(ep_list[0].extra_count, 65509) - self.assertEqual(ep_list[0].extra_state, 'closed') + self.assertEqual(ep_list[0].extra_state, "closed") self.assertEqual(len(ep_list[0].extra_reasons), 1) self.assertEqual(ep_list[1].extra_count, 10) self.assertEqual(len(ep_list[1].extra_reasons), 2) From 1185c32a53e6afbae0b0152121321495f35647e9 Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Mon, 27 Jan 2025 23:11:12 +0100 Subject: [PATCH 202/206] fix: upgraded pyblack for cve CVE-2024-21503 --- .github/workflows/preflight_check.yaml | 4 ++-- .pre-commit-config.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/preflight_check.yaml b/.github/workflows/preflight_check.yaml index 5cf7b87..c9f52e3 100644 --- a/.github/workflows/preflight_check.yaml +++ b/.github/workflows/preflight_check.yaml @@ -28,7 +28,7 @@ jobs: uses: psf/black@stable with: options: "--check -l 79 --exclude docs/" - version: "22.8.0" + version: "24.3.0" - name: Format checker with isort run: isort --check-only -m 3 -l 79 --profile=black . - name: Lint with flake8 @@ -37,7 +37,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - python-version: ["3.7", "3.8", "3.9", "3.10"] + python-version: ["3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v4 - name: Setup Python ${{ matrix.python-version }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3e900c4..339924e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: end-of-file-fixer - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 22.8.0 + rev: 24.3.0 hooks: - id: black args: [--line-length=79] From 00193b3c42563b3b447e86300777485618b80458 Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Tue, 28 Jan 2025 00:44:55 +0100 Subject: [PATCH 203/206] fix: simplified the extraports structs --- .pre-commit-config.yaml | 54 +++++++++++++++++++-------------- libnmap/objects/__init__.py | 4 +-- libnmap/objects/host.py | 5 +-- libnmap/objects/service.py | 49 ------------------------------ libnmap/parser.py | 11 +++---- libnmap/test/files/2_hosts.json | 2 +- libnmap/test/test_extraports.py | 18 ++++++----- libnmap/test/test_host.py | 19 ------------ 8 files changed, 53 insertions(+), 109 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 339924e..ab2e020 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,24 +1,34 @@ exclude: ^(test/|.tox/|docs) repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v2.3.0 - hooks: - - id: check-yaml - - id: end-of-file-fixer - - id: trailing-whitespace -- repo: https://github.com/psf/black - rev: 24.3.0 - hooks: - - id: black - args: [--line-length=79] - files: ^libnmap -- repo: https://github.com/pre-commit/mirrors-isort - rev: v5.6.4 - hooks: - - id: isort - args: [--multi-line=3, --line-length=79, --profile=black] -- repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 - hooks: - - id: flake8 - exclude: ^libnmap/(test/|docs/|examples/) +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v2.3.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/psf/black + rev: 24.3.0 + hooks: + - id: black + args: [--line-length=79] + files: ^libnmap +- repo: https://github.com/pre-commit/mirrors-isort + rev: v5.6.4 + hooks: + - id: isort + args: [--multi-line=3, --line-length=79, --profile=black] +- repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.4 + hooks: + - id: flake8 + exclude: ^libnmap/(test/|docs/|examples/) +- repo: local + hooks: + - id: pytest-check + name: pytest-check + stages: [pre-commit] + types: [python] + entry: pytest --cov=libnmap/ --ignore=libnmap/test/test_backend_plugin_factory.py + language: system + pass_filenames: false + always_run: true diff --git a/libnmap/objects/__init__.py b/libnmap/objects/__init__.py index 889e1d3..ddf3699 100644 --- a/libnmap/objects/__init__.py +++ b/libnmap/objects/__init__.py @@ -2,6 +2,6 @@ from libnmap.objects.host import NmapHost from libnmap.objects.report import NmapReport -from libnmap.objects.service import NmapExtraPort, NmapService +from libnmap.objects.service import NmapService -__all__ = ["NmapReport", "NmapHost", "NmapService", "NmapExtraPort"] +__all__ = ["NmapReport", "NmapHost", "NmapService"] diff --git a/libnmap/objects/host.py b/libnmap/objects/host.py index 95734ea..dba1653 100644 --- a/libnmap/objects/host.py +++ b/libnmap/objects/host.py @@ -465,9 +465,10 @@ def id(self): @property def extraports(self): """ - Returns a list of NmapExtraPort objects + Returns a list of extraport dict with + with struct { count: "123", state: "filtered, extrareasons: [{}] } - :return: list of NmapExtraPort objects + :return: list of extraport dict """ return self._extraports diff --git a/libnmap/objects/service.py b/libnmap/objects/service.py index 30c66f7..e254a66 100644 --- a/libnmap/objects/service.py +++ b/libnmap/objects/service.py @@ -366,52 +366,3 @@ def diff(self, other): :return: NmapDiff object """ return NmapDiff(self, other) - - -class NmapExtraPort(object): - """ - NmapExtraPort is an object which documents unlisted ports/services - which are possibly closed, filtered, ignored,... - """ - - def __init__(self, xdict): - """ - Constructor - :param xdict: python dict containing the following structure: - { - 'state': , - 'count': , - 'reasons': [{'reason': , 'count' }] - } - """ - self._count = xdict.get("count", 0) - self._state = xdict.get("state", "unknown") - self._reasons = xdict.get("reasons", []) - - @property - def extra_count(self): - """ - Accessor for the number of extraports - - :return: int - """ - return int(self._count) - - @property - def extra_state(self): - """ - Accessor for the state of extraports listed - - :return: string - """ - return self._state - - @property - def extra_reasons(self): - """ - Return the first reason available for the extraport listed. - - :return: dict, empty if no extraports reason available - """ - - return self._reasons diff --git a/libnmap/parser.py b/libnmap/parser.py index 9b12e12..e657d1c 100644 --- a/libnmap/parser.py +++ b/libnmap/parser.py @@ -11,7 +11,7 @@ from xml.etree.ElementTree import iselement as et_iselement -from libnmap.objects import NmapExtraPort, NmapHost, NmapReport, NmapService +from libnmap.objects import NmapHost, NmapReport, NmapService class NmapParser(object): @@ -469,9 +469,9 @@ def __parse_extraports(cls, extraports_data): :param extraports_data: XML data for extraports :type extraports_data: xml.ElementTree.Element or a string - :return: python dict with following keys: state, count, reason + :return: python dict with following keys: state, count, reasons """ - rdict = {"state": "", "count": "", "reasons": []} + rdict = {"state": "", "count": "", "extrareasons": []} xelement = cls.__format_element(extraports_data) extraports_dict = cls.__format_attributes(xelement) @@ -480,9 +480,8 @@ def __parse_extraports(cls, extraports_data): for xelt in xelement: if xelt.tag == "extrareasons": extrareasons_dict = cls.__format_attributes(xelt) - rdict["reasons"].append(extrareasons_dict) - robj = NmapExtraPort(rdict) - return robj + rdict["extrareasons"].append(extrareasons_dict) + return rdict @classmethod def __parse_script_table(cls, script_table): diff --git a/libnmap/test/files/2_hosts.json b/libnmap/test/files/2_hosts.json index d6e1727..985f293 100644 --- a/libnmap/test/files/2_hosts.json +++ b/libnmap/test/files/2_hosts.json @@ -1 +1 @@ -{"__NmapReport__": {"_nmaprun": {"scanner": "nmap", "args": "nmap -sS -vv -oX 2_hosts.xml localhost scanme.nmap.org", "start": "1361737906", "startstr": "Sun Feb 24 21:31:46 2013", "version": "5.51", "xmloutputversion": "1.03"}, "_scaninfo": {"type": "syn", "protocol": "tcp", "numservices": "1000", "services": "1,3-4,6-7,9,13,17,19-26,30,32-33,37,42-43,49,53,70,79-85,88-90,99-100,106,109-111,113,119,125,135,139,143-144,146,161,163,179,199,211-212,222,254-256,259,264,280,301,306,311,340,366,389,406-407,416-417,425,427,443-445,458,464-465,481,497,500,512-515,524,541,543-545,548,554-555,563,587,593,616-617,625,631,636,646,648,666-668,683,687,691,700,705,711,714,720,722,726,749,765,777,783,787,800-801,808,843,873,880,888,898,900-903,911-912,981,987,990,992-993,995,999-1002,1007,1009-1011,1021-1100,1102,1104-1108,1110-1114,1117,1119,1121-1124,1126,1130-1132,1137-1138,1141,1145,1147-1149,1151-1152,1154,1163-1166,1169,1174-1175,1183,1185-1187,1192,1198-1199,1201,1213,1216-1218,1233-1234,1236,1244,1247-1248,1259,1271-1272,1277,1287,1296,1300-1301,1309-1311,1322,1328,1334,1352,1417,1433-1434,1443,1455,1461,1494,1500-1501,1503,1521,1524,1533,1556,1580,1583,1594,1600,1641,1658,1666,1687-1688,1700,1717-1721,1723,1755,1761,1782-1783,1801,1805,1812,1839-1840,1862-1864,1875,1900,1914,1935,1947,1971-1972,1974,1984,1998-2010,2013,2020-2022,2030,2033-2035,2038,2040-2043,2045-2049,2065,2068,2099-2100,2103,2105-2107,2111,2119,2121,2126,2135,2144,2160-2161,2170,2179,2190-2191,2196,2200,2222,2251,2260,2288,2301,2323,2366,2381-2383,2393-2394,2399,2401,2492,2500,2522,2525,2557,2601-2602,2604-2605,2607-2608,2638,2701-2702,2710,2717-2718,2725,2800,2809,2811,2869,2875,2909-2910,2920,2967-2968,2998,3000-3001,3003,3005-3007,3011,3013,3017,3030-3031,3052,3071,3077,3128,3168,3211,3221,3260-3261,3268-3269,3283,3300-3301,3306,3322-3325,3333,3351,3367,3369-3372,3389-3390,3404,3476,3493,3517,3527,3546,3551,3580,3659,3689-3690,3703,3737,3766,3784,3800-3801,3809,3814,3826-3828,3851,3869,3871,3878,3880,3889,3905,3914,3918,3920,3945,3971,3986,3995,3998,4000-4006,4045,4111,4125-4126,4129,4224,4242,4279,4321,4343,4443-4446,4449,4550,4567,4662,4848,4899-4900,4998,5000-5004,5009,5030,5033,5050-5051,5054,5060-5061,5080,5087,5100-5102,5120,5190,5200,5214,5221-5222,5225-5226,5269,5280,5298,5357,5405,5414,5431-5432,5440,5500,5510,5544,5550,5555,5560,5566,5631,5633,5666,5678-5679,5718,5730,5800-5802,5810-5811,5815,5822,5825,5850,5859,5862,5877,5900-5904,5906-5907,5910-5911,5915,5922,5925,5950,5952,5959-5963,5987-5989,5998-6007,6009,6025,6059,6100-6101,6106,6112,6123,6129,6156,6346,6389,6502,6510,6543,6547,6565-6567,6580,6646,6666-6669,6689,6692,6699,6779,6788-6789,6792,6839,6881,6901,6969,7000-7002,7004,7007,7019,7025,7070,7100,7103,7106,7200-7201,7402,7435,7443,7496,7512,7625,7627,7676,7741,7777-7778,7800,7911,7920-7921,7937-7938,7999-8002,8007-8011,8021-8022,8031,8042,8045,8080-8090,8093,8099-8100,8180-8181,8192-8194,8200,8222,8254,8290-8292,8300,8333,8383,8400,8402,8443,8500,8600,8649,8651-8652,8654,8701,8800,8873,8888,8899,8994,9000-9003,9009-9011,9040,9050,9071,9080-9081,9090-9091,9099-9103,9110-9111,9200,9207,9220,9290,9415,9418,9485,9500,9502-9503,9535,9575,9593-9595,9618,9666,9876-9878,9898,9900,9917,9929,9943-9944,9968,9998-10004,10009-10010,10012,10024-10025,10082,10180,10215,10243,10566,10616-10617,10621,10626,10628-10629,10778,11110-11111,11967,12000,12174,12265,12345,13456,13722,13782-13783,14000,14238,14441-14442,15000,15002-15004,15660,15742,16000-16001,16012,16016,16018,16080,16113,16992-16993,17877,17988,18040,18101,18988,19101,19283,19315,19350,19780,19801,19842,20000,20005,20031,20221-20222,20828,21571,22939,23502,24444,24800,25734-25735,26214,27000,27352-27353,27355-27356,27715,28201,30000,30718,30951,31038,31337,32768-32785,33354,33899,34571-34573,35500,38292,40193,40911,41511,42510,44176,44442-44443,44501,45100,48080,49152-49161,49163,49165,49167,49175-49176,49400,49999-50003,50006,50300,50389,50500,50636,50800,51103,51493,52673,52822,52848,52869,54045,54328,55055-55056,55555,55600,56737-56738,57294,57797,58080,60020,60443,61532,61900,62078,63331,64623,64680,65000,65129,65389"}, "_hosts": [{"__NmapHost__": {"_starttime": "1361737906", "_endtime": "1361737906", "_hostnames": ["localhost", "localhost"], "_status": {"state": "up", "reason": "localhost-response"}, "_services": [{"__NmapService__": {"_portid": 22, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "ssh", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 25, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "smtp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 111, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "rpcbind", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 631, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "ipp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 3306, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "mysql", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}], "_extras": {"extraports": {"state": {"state": "closed", "count": "995"}, "count": {"state": "closed", "count": "995"}, "reasons": [{"reason": "resets", "count": "995"}]}, "times": {"srtt": "7", "rttvar": "0", "to": "100000"}}, "_osfingerprinted": false, "os": {"__NmapOSFingerprint__": {"_NmapOSFingerprint__osmatches": [], "_NmapOSFingerprint__ports_used": [], "_NmapOSFingerprint__fingerprints": []}}, "_ipv4_addr": "127.0.0.1", "_ipv6_addr": null, "_mac_addr": null, "_vendor": null, "_main_address": "127.0.0.1", "_address": [{"addr": "127.0.0.1", "addrtype": "ipv4"}]}}, {"__NmapHost__": {"_starttime": "1361737906", "_endtime": "1361738040", "_hostnames": ["scanme.nmap.org", "scanme.nmap.org"], "_status": {"state": "up", "reason": "echo-reply"}, "_services": [{"__NmapService__": {"_portid": 22, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "53"}, "_service": {"name": "ssh", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "53", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 25, "_protocol": "tcp", "_state": {"state": "filtered", "reason": "admin-prohibited", "reason_ttl": "253", "reason_ip": "109.133.192.1"}, "_service": {"name": "smtp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "admin-prohibited", "_reason_ip": "109.133.192.1", "_reason_ttl": "253", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 80, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "51"}, "_service": {"name": "http", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "51", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 9929, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "53"}, "_service": {"name": "nping-echo", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "53", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}], "_extras": {"extraports": {"state": {"state": "closed", "count": "996"}, "count": {"state": "closed", "count": "996"}, "reasons": [{"reason": "resets", "count": "996"}]}, "times": {"srtt": "177425", "rttvar": "1981", "to": "185349"}}, "_osfingerprinted": false, "os": {"__NmapOSFingerprint__": {"_NmapOSFingerprint__osmatches": [], "_NmapOSFingerprint__ports_used": [], "_NmapOSFingerprint__fingerprints": []}}, "_ipv4_addr": "74.207.244.221", "_ipv6_addr": null, "_mac_addr": null, "_vendor": null, "_main_address": "74.207.244.221", "_address": [{"addr": "74.207.244.221", "addrtype": "ipv4"}]}}], "_runstats": {"finished": {"time": "1361738040", "timestr": "Sun Feb 24 21:34:00 2013", "elapsed": "134.36", "summary": "Nmap done at Sun Feb 24 21:34:00 2013; 2 IP addresses (2 hosts up) scanned in 134.36 seconds", "exit": "success"}, "hosts": {"up": "2", "down": "0", "total": "2"}}}} +{"__NmapReport__": {"_nmaprun": {"scanner": "nmap", "args": "nmap -sS -vv -oX 2_hosts.xml localhost scanme.nmap.org", "start": "1361737906", "startstr": "Sun Feb 24 21:31:46 2013", "version": "5.51", "xmloutputversion": "1.03"}, "_scaninfo": {"type": "syn", "protocol": "tcp", "numservices": "1000", "services": "1,3-4,6-7,9,13,17,19-26,30,32-33,37,42-43,49,53,70,79-85,88-90,99-100,106,109-111,113,119,125,135,139,143-144,146,161,163,179,199,211-212,222,254-256,259,264,280,301,306,311,340,366,389,406-407,416-417,425,427,443-445,458,464-465,481,497,500,512-515,524,541,543-545,548,554-555,563,587,593,616-617,625,631,636,646,648,666-668,683,687,691,700,705,711,714,720,722,726,749,765,777,783,787,800-801,808,843,873,880,888,898,900-903,911-912,981,987,990,992-993,995,999-1002,1007,1009-1011,1021-1100,1102,1104-1108,1110-1114,1117,1119,1121-1124,1126,1130-1132,1137-1138,1141,1145,1147-1149,1151-1152,1154,1163-1166,1169,1174-1175,1183,1185-1187,1192,1198-1199,1201,1213,1216-1218,1233-1234,1236,1244,1247-1248,1259,1271-1272,1277,1287,1296,1300-1301,1309-1311,1322,1328,1334,1352,1417,1433-1434,1443,1455,1461,1494,1500-1501,1503,1521,1524,1533,1556,1580,1583,1594,1600,1641,1658,1666,1687-1688,1700,1717-1721,1723,1755,1761,1782-1783,1801,1805,1812,1839-1840,1862-1864,1875,1900,1914,1935,1947,1971-1972,1974,1984,1998-2010,2013,2020-2022,2030,2033-2035,2038,2040-2043,2045-2049,2065,2068,2099-2100,2103,2105-2107,2111,2119,2121,2126,2135,2144,2160-2161,2170,2179,2190-2191,2196,2200,2222,2251,2260,2288,2301,2323,2366,2381-2383,2393-2394,2399,2401,2492,2500,2522,2525,2557,2601-2602,2604-2605,2607-2608,2638,2701-2702,2710,2717-2718,2725,2800,2809,2811,2869,2875,2909-2910,2920,2967-2968,2998,3000-3001,3003,3005-3007,3011,3013,3017,3030-3031,3052,3071,3077,3128,3168,3211,3221,3260-3261,3268-3269,3283,3300-3301,3306,3322-3325,3333,3351,3367,3369-3372,3389-3390,3404,3476,3493,3517,3527,3546,3551,3580,3659,3689-3690,3703,3737,3766,3784,3800-3801,3809,3814,3826-3828,3851,3869,3871,3878,3880,3889,3905,3914,3918,3920,3945,3971,3986,3995,3998,4000-4006,4045,4111,4125-4126,4129,4224,4242,4279,4321,4343,4443-4446,4449,4550,4567,4662,4848,4899-4900,4998,5000-5004,5009,5030,5033,5050-5051,5054,5060-5061,5080,5087,5100-5102,5120,5190,5200,5214,5221-5222,5225-5226,5269,5280,5298,5357,5405,5414,5431-5432,5440,5500,5510,5544,5550,5555,5560,5566,5631,5633,5666,5678-5679,5718,5730,5800-5802,5810-5811,5815,5822,5825,5850,5859,5862,5877,5900-5904,5906-5907,5910-5911,5915,5922,5925,5950,5952,5959-5963,5987-5989,5998-6007,6009,6025,6059,6100-6101,6106,6112,6123,6129,6156,6346,6389,6502,6510,6543,6547,6565-6567,6580,6646,6666-6669,6689,6692,6699,6779,6788-6789,6792,6839,6881,6901,6969,7000-7002,7004,7007,7019,7025,7070,7100,7103,7106,7200-7201,7402,7435,7443,7496,7512,7625,7627,7676,7741,7777-7778,7800,7911,7920-7921,7937-7938,7999-8002,8007-8011,8021-8022,8031,8042,8045,8080-8090,8093,8099-8100,8180-8181,8192-8194,8200,8222,8254,8290-8292,8300,8333,8383,8400,8402,8443,8500,8600,8649,8651-8652,8654,8701,8800,8873,8888,8899,8994,9000-9003,9009-9011,9040,9050,9071,9080-9081,9090-9091,9099-9103,9110-9111,9200,9207,9220,9290,9415,9418,9485,9500,9502-9503,9535,9575,9593-9595,9618,9666,9876-9878,9898,9900,9917,9929,9943-9944,9968,9998-10004,10009-10010,10012,10024-10025,10082,10180,10215,10243,10566,10616-10617,10621,10626,10628-10629,10778,11110-11111,11967,12000,12174,12265,12345,13456,13722,13782-13783,14000,14238,14441-14442,15000,15002-15004,15660,15742,16000-16001,16012,16016,16018,16080,16113,16992-16993,17877,17988,18040,18101,18988,19101,19283,19315,19350,19780,19801,19842,20000,20005,20031,20221-20222,20828,21571,22939,23502,24444,24800,25734-25735,26214,27000,27352-27353,27355-27356,27715,28201,30000,30718,30951,31038,31337,32768-32785,33354,33899,34571-34573,35500,38292,40193,40911,41511,42510,44176,44442-44443,44501,45100,48080,49152-49161,49163,49165,49167,49175-49176,49400,49999-50003,50006,50300,50389,50500,50636,50800,51103,51493,52673,52822,52848,52869,54045,54328,55055-55056,55555,55600,56737-56738,57294,57797,58080,60020,60443,61532,61900,62078,63331,64623,64680,65000,65129,65389"}, "_hosts": [{"__NmapHost__": {"_starttime": "1361737906", "_endtime": "1361737906", "_hostnames": ["localhost", "localhost"], "_status": {"state": "up", "reason": "localhost-response"}, "_services": [{"__NmapService__": {"_portid": 22, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "ssh", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 25, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "smtp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 111, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "rpcbind", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 631, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "ipp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 3306, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "64"}, "_service": {"name": "mysql", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "64", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}], "_extras": {"extraports": [{"state": "closed", "count": "995", "extrareasons": [{"reason": "resets", "count": "995"}]}], "times": {"srtt": "7", "rttvar": "0", "to": "100000"}}, "_extraports": [{"state": "closed", "count": "995", "extrareasons": [{"reason": "resets", "count": "995"}]}], "_osfingerprinted": false, "os": {"__NmapOSFingerprint__": {"_NmapOSFingerprint__osmatches": [], "_NmapOSFingerprint__ports_used": [], "_NmapOSFingerprint__fingerprints": []}}, "_ipv4_addr": "127.0.0.1", "_ipv6_addr": null, "_mac_addr": null, "_vendor": null, "_main_address": "127.0.0.1", "_address": [{"addr": "127.0.0.1", "addrtype": "ipv4"}]}}, {"__NmapHost__": {"_starttime": "1361737906", "_endtime": "1361738040", "_hostnames": ["scanme.nmap.org", "scanme.nmap.org"], "_status": {"state": "up", "reason": "echo-reply"}, "_services": [{"__NmapService__": {"_portid": 22, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "53"}, "_service": {"name": "ssh", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "53", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 25, "_protocol": "tcp", "_state": {"state": "filtered", "reason": "admin-prohibited", "reason_ttl": "253", "reason_ip": "109.133.192.1"}, "_service": {"name": "smtp", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "admin-prohibited", "_reason_ip": "109.133.192.1", "_reason_ttl": "253", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 80, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "51"}, "_service": {"name": "http", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "51", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}, {"__NmapService__": {"_portid": 9929, "_protocol": "tcp", "_state": {"state": "open", "reason": "syn-ack", "reason_ttl": "53"}, "_service": {"name": "nping-echo", "method": "table", "conf": "3", "cpelist": []}, "_cpelist": [], "_owner": "", "_reason": "syn-ack", "_reason_ip": "", "_reason_ttl": "53", "_servicefp": "", "_tunnel": "", "_service_extras": {"scripts": []}}}], "_extras": {"extraports": [{"state": "closed", "count": "996", "extrareasons": [{"reason": "resets", "count": "996"}]}], "times": {"srtt": "177425", "rttvar": "1981", "to": "185349"}}, "_extraports": [{"state": "closed", "count": "996", "extrareasons": [{"reason": "resets", "count": "996"}]}], "_osfingerprinted": false, "os": {"__NmapOSFingerprint__": {"_NmapOSFingerprint__osmatches": [], "_NmapOSFingerprint__ports_used": [], "_NmapOSFingerprint__fingerprints": []}}, "_ipv4_addr": "74.207.244.221", "_ipv6_addr": null, "_mac_addr": null, "_vendor": null, "_main_address": "74.207.244.221", "_address": [{"addr": "74.207.244.221", "addrtype": "ipv4"}]}}], "_runstats": {"finished": {"time": "1361738040", "timestr": "Sun Feb 24 21:34:00 2013", "elapsed": "134.36", "summary": "Nmap done at Sun Feb 24 21:34:00 2013; 2 IP addresses (2 hosts up) scanned in 134.36 seconds", "exit": "success"}, "hosts": {"up": "2", "down": "0", "total": "2"}}}} diff --git a/libnmap/test/test_extraports.py b/libnmap/test/test_extraports.py index c40308b..c52a17a 100644 --- a/libnmap/test/test_extraports.py +++ b/libnmap/test/test_extraports.py @@ -10,14 +10,14 @@ class TestExtraPorts(unittest.TestCase): def setUp(self): fdir = os.path.dirname(os.path.realpath(__file__)) - _extrareason = [ + _extrareasons = [ {"reason": "filtered", "count": "3"}, {"reason": "resets", "count": "7"}, ] self.flist = [ { "path": "%s/%s" % (fdir, "files/extra_ports.xml"), - "extrareason": _extrareason, + "extrareasons": _extrareasons, } ] @@ -26,12 +26,14 @@ def test_extraports(self): rep1 = NmapParser.parse_fromfile(fentry["path"]) ep_list = rep1.hosts[0].extraports self.assertEqual(len(ep_list), 2) - self.assertEqual(ep_list[0].extra_count, 65509) - self.assertEqual(ep_list[0].extra_state, "closed") - self.assertEqual(len(ep_list[0].extra_reasons), 1) - self.assertEqual(ep_list[1].extra_count, 10) - self.assertEqual(len(ep_list[1].extra_reasons), 2) - self.assertEqual(ep_list[1].extra_reasons, fentry["extrareason"]) + self.assertEqual(ep_list[0]["count"], "65509") + self.assertEqual(ep_list[0]["state"], "closed") + self.assertEqual(len(ep_list[0]["extrareasons"]), 1) + self.assertEqual(ep_list[1]["count"], "10") + self.assertEqual(len(ep_list[1]["extrareasons"]), 2) + self.assertEqual( + ep_list[1]["extrareasons"], fentry["extrareasons"] + ) if __name__ == "__main__": diff --git a/libnmap/test/test_host.py b/libnmap/test/test_host.py index d192f32..0dff34d 100644 --- a/libnmap/test/test_host.py +++ b/libnmap/test/test_host.py @@ -181,25 +181,6 @@ def test_host_api(self): self.assertEqual(len(h2.get_open_ports()), 3) self.assertEqual(h2.get_service(22, "tcp").state, "open") - def test_extra_ports(self): - h1 = NmapParser.parse(host1) - h2 = NmapParser.parse(host2) - - self.assertEqual( - h1.extraports_state["state"], - {"count": "995", "state": "WILLY_WONCKA"}, - ) - self.assertEqual( - h1.extraports_reasons, [{"reason": "conn-refused", "count": "995"}] - ) - - self.assertEqual( - h2.extraports_state["state"], {"count": "995", "state": "closed"} - ) - self.assertEqual( - h2.extraports_reasons, [{"reason": "conn-refused", "count": "995"}] - ) - def test_diff_host(self): h1 = NmapParser.parse(host1) h2 = NmapParser.parse(host2) From 86478126924f3b002f814814546c7b2ab69250db Mon Sep 17 00:00:00 2001 From: Aquila Macedo Date: Sat, 16 Dec 2023 23:28:11 -0300 Subject: [PATCH 204/206] fix: replace deprecated distutils with setuptools Update setup.py from 'distutils' to 'setuptools' due to 'distutils' being deprecated in Python 3.12 Details: https://peps.python.org/pep-0632/ --- requirements-dev.txt | 1 + setup.py | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index d63813a..e8abeb1 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -5,3 +5,4 @@ pre-commit pytest pytest-cov flake8 +setuptools diff --git a/setup.py b/setup.py index eb5c0f5..6280910 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,10 @@ # -*- coding: utf-8 -*- import sys -from distutils.core import setup +try: + from setuptools import setup +except ImportError: + from distutils.core import setup + if sys.version_info >= (3, 0): with open("README.rst", encoding="utf-8") as rfile: From 01944cadab6eb65eae27e261ef86324ba7fa12ba Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Wed, 29 Jan 2025 22:59:21 +0100 Subject: [PATCH 205/206] fix: added hybrid support for setuptools --- README.rst | 9 +++++---- requirements-dev.txt | 7 +++---- setup.py | 5 +++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index 023726f..d4ab112 100644 --- a/README.rst +++ b/README.rst @@ -115,21 +115,22 @@ You can install libnmap via pip: ronald@brouette:~$ pip install python-libnmap -or via git and dist utils (à l’ancienne): +or via git and pip: .. code:: bash ronald@brouette:~$ git clone https://github.com/savon-noir/python-libnmap.git ronald@brouette:~$ cd python-libnmap - ronald@brouette:~$ python setup.py install + ronald@brouette:~$ pip install . -or via git and pip: +or via git and dist utils (à l’ancienne/deprecated): .. code:: bash ronald@brouette:~$ git clone https://github.com/savon-noir/python-libnmap.git ronald@brouette:~$ cd python-libnmap - ronald@brouette:~$ pip install . + ronald@brouette:~$ python setup.py install + Examples -------- diff --git a/requirements-dev.txt b/requirements-dev.txt index e8abeb1..cb383eb 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,8 +1,7 @@ -black==22.8.0 -defusedxml==0.6.0 -isort==5.6.4 +black==24.3.0 +defusedxml==0.7.1 +isort==6.0.0 pre-commit pytest pytest-cov flake8 -setuptools diff --git a/setup.py b/setup.py index 6280910..bd35ab1 100644 --- a/setup.py +++ b/setup.py @@ -5,11 +5,10 @@ except ImportError: from distutils.core import setup - if sys.version_info >= (3, 0): with open("README.rst", encoding="utf-8") as rfile: long_description = rfile.read() -else: # encoding not compatible with python2 +else: # if encoding not compatible with python2 with open("README.rst") as rfile: long_description = rfile.read() @@ -42,6 +41,8 @@ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Topic :: System :: Networking", ], ) From f9887fc9add90e2e0b3c59671ec5ecd9bee8fcd3 Mon Sep 17 00:00:00 2001 From: Ronald Bister Date: Wed, 29 Jan 2025 23:07:53 +0100 Subject: [PATCH 206/206] fix: linting --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index bd35ab1..1ff64c0 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- import sys + try: from setuptools import setup except ImportError: