From 74547350e744d389ae0b38e93e04185fc81d1023 Mon Sep 17 00:00:00 2001 From: Nmmapper <57083309+nmmapper@users.noreply.github.com> Date: Thu, 7 Mar 2024 18:18:21 +0300 Subject: [PATCH 1/6] Update python-publish.yml --- .github/workflows/python-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 6c65810..9f0c7cb 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -33,7 +33,7 @@ jobs: - name: Build package run: python -m build - name: Publish package - uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 + uses: "nmmapper/python3-nmap@main" with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} From 01780be2e6fefd93c6349005909d8f3e46fb3a6e Mon Sep 17 00:00:00 2001 From: Nmmapper <57083309+nmmapper@users.noreply.github.com> Date: Thu, 14 Mar 2024 14:18:09 +0300 Subject: [PATCH 2/6] Update python-publish.yml --- .github/workflows/python-publish.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 9f0c7cb..103c569 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -33,7 +33,6 @@ jobs: - name: Build package run: python -m build - name: Publish package - uses: "nmmapper/python3-nmap@main" with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} From 3de136bc48ea8a00ae3b5074728185c67414f685 Mon Sep 17 00:00:00 2001 From: Nmmapper <57083309+nmmapper@users.noreply.github.com> Date: Thu, 14 Mar 2024 14:22:27 +0300 Subject: [PATCH 3/6] Update python-publish.yml --- .github/workflows/python-publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 103c569..6c65810 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -33,6 +33,7 @@ jobs: - name: Build package run: python -m build - name: Publish package + uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 with: user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} From 349a02928247423d68b7dd5305c89e4e1ba017ce Mon Sep 17 00:00:00 2001 From: Aliaksandr Date: Thu, 28 Nov 2024 23:40:27 +0300 Subject: [PATCH 4/6] check path duringing class init, some editions --- nmap3/nmap3.py | 95 +++++++++++++++++++++++++++----------------------- nmap3/utils.py | 17 ++++++--- 2 files changed, 64 insertions(+), 48 deletions(-) diff --git a/nmap3/nmap3.py b/nmap3/nmap3.py index 303a7cf..db06765 100644 --- a/nmap3/nmap3.py +++ b/nmap3/nmap3.py @@ -19,19 +19,16 @@ # # -import os import shlex import subprocess import sys -import simplejson as json import argparse import asyncio from xml.etree import ElementTree as ET from xml.etree.ElementTree import ParseError from nmap3.nmapparser import NmapCommandParser from nmap3.utils import get_nmap_path, user_is_root -from nmap3.exceptions import NmapNotInstalledError, NmapXMLParserError, NmapExecutionError -import xml +from nmap3.exceptions import NmapXMLParserError, NmapExecutionError import re __author__ = 'Wangolo Joel (inquiry@nmapper.com)' @@ -46,14 +43,14 @@ class Nmap(object): by calling nmap3.Nmap() """ - def __init__(self, path=None): + def __init__(self, path:str=''): """ Module initialization :param path: Path where nmap is installed on a user system. On linux system it's typically on /usr/bin/nmap. """ - self.nmaptool = path if path else get_nmap_path() + self.nmaptool = get_nmap_path(path) # check path, search or raise error self.default_args = "{nmap} {outarg} - " self.maxport = 65535 self.target = "" @@ -114,7 +111,7 @@ def nmap_version(self): # Unique method for repetitive tasks - Use of 'target' variable instead of 'host' or 'subnet' - no need to make difference between 2 strings that are used for the same purpose def scan_command(self, target, arg, args=None, timeout=None): - self.target == target + self.target = target command_args = "{target} {default}".format(target=target, default=arg) scancommand = self.default_command() + command_args @@ -216,7 +213,7 @@ def nmap_detect_firewall(self, target, arg="-sA", args=None): # requires root nmap -oX - nmmapper.com -sA @ TODO """ - xml_root = self.scan_command(target=target, arg=arg, args=args) + return self.scan_command(target=target, arg=arg, args=args) # TODO @user_is_root @@ -257,22 +254,26 @@ def run_command(self, cmd, timeout=None): @param: cmd--> the command we want run eg /usr/bin/nmap -oX - nmmapper.com --top-ports 10 @param: timeout--> command subprocess timeout in seconds. """ - if (os.path.exists(self.nmaptool)): - sub_proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - try: - output, errs = sub_proc.communicate(timeout=timeout) - except Exception as e: - sub_proc.kill() - raise (e) - else: - if 0 != sub_proc.returncode: - raise NmapExecutionError('Error during command: "' + ' '.join(cmd) + '"\n\n' + errs.decode('utf8')) - - # Response is bytes so decode the output and return - return output.decode('utf8').strip() + sub_proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + try: + output, errs = sub_proc.communicate(timeout=timeout) + except Exception as e: + sub_proc.kill() + raise (e) else: - raise NmapNotInstalledError() + if 0 != sub_proc.returncode: + raise NmapExecutionError( + 'Error during command: "' + ' '.join(cmd) + '"\n\n' \ + + errs.decode('utf8') + ) + # Response is bytes so decode the output and return + return output.decode('utf8').strip() + def get_xml_et(self, command_output): """ @ return xml ET @@ -309,7 +310,7 @@ class NmapScanTechniques(Nmap): 7) IP Scan (-sO) """ - def __init__(self, path=None): + def __init__(self, path:str=''): super(NmapScanTechniques, self).__init__(path=path) self.sync_scan = "-sS" @@ -350,7 +351,9 @@ def tpl(i): output = self.run_command(scan_shlex, timeout=timeout) xml_root = self.get_xml_et(output) - return xml_root + return xml_root + raise Exception("Something went wrong") + @user_is_root def nmap_fin_scan(self, target, args=None): @@ -442,7 +445,7 @@ class NmapHostDiscovery(Nmap): 4) Disable DNS resolution (-n) """ - def __init__(self, path=None): + def __init__(self, path:str=''): super(NmapHostDiscovery, self).__init__(path=path) self.port_scan_only = "-Pn" @@ -476,7 +479,8 @@ def tpl(i): output = self.run_command(scan_shlex, timeout=timeout) xml_root = self.get_xml_et(output) - return xml_root + return xml_root + raise Exception("Something went wrong") def nmap_portscan_only(self, target, args=None): """ @@ -523,30 +527,34 @@ def nmap_disable_dns(self, target, args=None): return results class NmapAsync(Nmap): - def __init__(self, path=None): + def __init__(self, path:str=''): super(NmapAsync, self).__init__(path=path) self.stdout = asyncio.subprocess.PIPE self.stderr = asyncio.subprocess.PIPE async def run_command(self, cmd, timeout=None): - if (os.path.exists(self.nmaptool)): - process = await asyncio.create_subprocess_shell(cmd,stdout=self.stdout,stderr=self.stderr) + process = await asyncio.create_subprocess_shell( + cmd, + stdout=self.stdout, + stderr=self.stderr + ) - try: - data, stderr = await process.communicate() - except Exception as e: - raise (e) - else: - if 0 != process.returncode: - raise NmapExecutionError('Error during command: "' + ' '.join(cmd) + '"\n\n' + stderr.decode('utf8')) - - # Response is bytes so decode the output and return - return data.decode('utf8').strip() + try: + data, stderr = await process.communicate() + except Exception as e: + raise (e) else: - raise NmapNotInstalledError() + if 0 != process.returncode: + raise NmapExecutionError( + 'Error during command: "' + ' '.join(cmd) + '"\n\n' + \ + stderr.decode('utf8') + ) + + # Response is bytes so decode the output and return + return data.decode('utf8').strip() async def scan_command(self, target, arg, args=None, timeout=None): - self.target == target + self.target = target command_args = "{target} {default}".format(target=target, default=arg) scancommand = self.default_command() + command_args @@ -611,7 +619,7 @@ async def nmap_list_scan(self, target, arg="-sL", args=None): # requires root return results class NmapScanTechniquesAsync(NmapAsync,NmapScanTechniques): - def __init__(self, path=None): + def __init__(self, path:str=''): super(NmapScanTechniquesAsync, self).__init__(path=path) self.udp_scan = "-sU" @@ -640,7 +648,8 @@ def tpl(i): output = await self.run_command(scan_type_command, timeout=timeout) xml_root = self.get_xml_et(output) - return xml_root + return xml_root + raise Exception("Something went wrong") async def nmap_udp_scan(self, target, args=None): if (args): diff --git a/nmap3/utils.py b/nmap3/utils.py index d7a6ac4..4323caa 100644 --- a/nmap3/utils.py +++ b/nmap3/utils.py @@ -21,20 +21,26 @@ import shlex import subprocess import sys -import re import os import ctypes import functools +from nmap3.exceptions import NmapNotInstalledError + __author__ = 'Wangolo Joel (inquiry@nmapper.com)' __version__ = '1.6.0' __last_modification__ = 'Sep/15/2024' -def get_nmap_path(): +def get_nmap_path(path:str='') -> str: """ + Accepts path, validate it. If not valide, search nmap path Returns the location path where nmap is installed by calling which nmap + If not found raises NmapNotInstalledError """ + if (os.path.exists(path)): + return path + os_type = sys.platform if os_type == 'win32': cmd = "where nmap" @@ -44,10 +50,11 @@ def get_nmap_path(): sub_proc = subprocess.Popen(args, stdout=subprocess.PIPE) try: - output, errs = sub_proc.communicate(timeout=15) + output, _ = sub_proc.communicate(timeout=15) except Exception as e: print(e) sub_proc.kill() + raise NmapNotInstalledError() else: if os_type == 'win32': return output.decode('utf8').strip().replace("\\", "/") @@ -62,7 +69,7 @@ def get_nmap_version(): sub_proc = subprocess.Popen(args, stdout=subprocess.PIPE) try: - output, errs = sub_proc.communicate(timeout=15) + output, _ = sub_proc.communicate(timeout=15) except Exception as e: print(e) sub_proc.kill() @@ -73,7 +80,7 @@ def user_is_root(func): def wrapper(*args, **kwargs): try: is_root_or_admin = (os.getuid() == 0) - except AttributeError as e: + except AttributeError: is_root_or_admin = ctypes.windll.shell32.IsUserAnAdmin() != 0 if(is_root_or_admin): From 2561051d4612885c017d070de8625ad38a6a0cf1 Mon Sep 17 00:00:00 2001 From: Aliaksandr Date: Fri, 29 Nov 2024 00:15:06 +0300 Subject: [PATCH 5/6] add to NmapNotInstallError wrong provided path in desc --- nmap3/exceptions.py | 13 +++++-------- nmap3/utils.py | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/nmap3/exceptions.py b/nmap3/exceptions.py index 9cc5f7b..c37be2b 100644 --- a/nmap3/exceptions.py +++ b/nmap3/exceptions.py @@ -17,11 +17,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # -# -import shlex -import subprocess -import sys -import re __author__ = 'Wangolo Joel (inquiry@nmapper.com)' __version__ = '1.6.0' @@ -30,9 +25,11 @@ class NmapNotInstalledError(Exception): """Exception raised when nmap is not installed""" - def __init__(self, message="Nmap is either not installed or we couldn't locate nmap path Please ensure nmap is installed"): - self.message = message - super().__init__(message) + def __init__(self, path=""): + self.message = f"Nmap is either not installed or we couldn't locate \ +nmap path. Please ensure nmap is installed and provide right path string. \n\ +Provided: *{path if path else 'Not provided'}*" + super().__init__(self.message) class NmapXMLParserError(Exception): """Exception raised when we can't parse the output""" diff --git a/nmap3/utils.py b/nmap3/utils.py index 4323caa..2c360d1 100644 --- a/nmap3/utils.py +++ b/nmap3/utils.py @@ -54,7 +54,7 @@ def get_nmap_path(path:str='') -> str: except Exception as e: print(e) sub_proc.kill() - raise NmapNotInstalledError() + raise NmapNotInstalledError(path=path) else: if os_type == 'win32': return output.decode('utf8').strip().replace("\\", "/") From 13186f7f6503da402f60b2ed4b5b09772c8be825 Mon Sep 17 00:00:00 2001 From: Aliaksandr Date: Fri, 29 Nov 2024 00:58:45 +0300 Subject: [PATCH 6/6] fix: nmap path checks --- nmap3/utils.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/nmap3/utils.py b/nmap3/utils.py index 2c360d1..daa16bb 100644 --- a/nmap3/utils.py +++ b/nmap3/utils.py @@ -38,7 +38,7 @@ def get_nmap_path(path:str='') -> str: by calling which nmap If not found raises NmapNotInstalledError """ - if (os.path.exists(path)): + if path and (os.path.exists(path)): return path os_type = sys.platform @@ -49,17 +49,15 @@ def get_nmap_path(path:str='') -> str: args = shlex.split(cmd) sub_proc = subprocess.Popen(args, stdout=subprocess.PIPE) - try: - output, _ = sub_proc.communicate(timeout=15) - except Exception as e: + output, e = sub_proc.communicate(timeout=15) + if e: print(e) - sub_proc.kill() + + if not output: raise NmapNotInstalledError(path=path) - else: - if os_type == 'win32': - return output.decode('utf8').strip().replace("\\", "/") - else: - return output.decode('utf8').strip() + if os_type == 'win32': + return output.decode('utf8').strip().replace("\\", "/") + return output.decode('utf8').strip() def get_nmap_version(): nmap = get_nmap_path()