Refactor: Improved Typing and Type Safety, Logging and Privilege Handling
Summary
This PR introduces refactoring with focus on typing, documentation improvements, and betterprivilege handling. The changes maintain full backward compatibility - all existing APIs work exactly as before, with no breaking changes to existing logic or functionality.
Key Features
Enhanced Privilege Handling
The @user_is_root decorator has been re-implemented with a smarter approach that doesn't rely on os.getuid() == 0. This is important because users can have sudo access (which is required by nmap for some ops) without being root with UID=0.
The decorator now:
-
Detects Windows admin privileges using
ctypes.windll.shell32.IsUserAnAdmin() - Analyzes nmap error messages to identify privilege-related failures using keywords like "root privileges", "administrator", "permission denied"
-
Raises
NmapPrivilegeErrorwith clear messaging when privileges are insufficient
For Unix/Linux systems requiring elevated privileges:
import nmap3
# Step 1: Run the entire script with sudo
# $ sudo python3 your_script.py
# Step 2: Manually configure nmap with sudo prefix
nmap = nmap3.Nmap()
nmap.nmaptool = f"sudo {nmap.nmaptool}" # !Required for elevated operations!
results = nmap.nmap_os_detection("target.com") # This should now work with elevated privileges
# Alternatively, external privilege escalation tools can be used
# You can integrate with packages like 'elevate' for automated elevation
For Windows systems:
# Method 1: Run PowerShell/Command Prompt as Administrator, then:
import nmap3
nmap = nmap3.Nmap()
results = nmap.nmap_os_detection("target.com") # Will work in elevated shell
# Method 2: Use PyUAC library for programmatic elevation
# pip install pyuac
import pyuac
if not pyuac.isUserAdmin():
print("Re-running as admin!")
pyuac.runAsAdmin()
else:
# Your nmap code here - now running with admin privileges
nmap = nmap3.Nmap()
results = nmap.nmap_os_detection("target.com")
print("Running with admin privileges")
Enhanced Type Safety
- Comprehensive type annotations throughout the codebase using modern Python typing
-
Version-aware typing with
ParamSpecsupport for Python 3.10+ andtyping_extensionsfallback - Improved decorator preservation maintaining original function signatures and type information
New Exception Hierarchy
Added specialized exception classes with a clean inheritance hierarchy for better error handling:
-
NmapError- Base exception class for all nmap3-related errors -
NmapPrivilegeError- Raised when nmap operations require root/administrator privileges -
NmapTimeoutError- Raised when nmap commands time out -
NmapExecutionError- Raised when nmap command execution fails
Simplified Error Handling: All exceptions inherit from NmapError, allowing users to catch all nmap3-related exceptions with a single catch block:
from nmap3.exceptions import NmapError
try:
results = nmap.nmap_syn_scan("192.168.1.1")
except NmapError as e:
# Catches all nmap3 exceptions: privilege, timeout, execution errors, etc.
print(f"Nmap operation failed: {e}")
Comprehensive Logging System
Proper logging has been implemented throughout the application using Python's standard logging module. Each module now includes dedicated loggers for better debugging and monitoring capabilities.
Logged Operations Include:
- Process Management: Command execution, process termination, and cleanup operations
- Error Conditions: Failed operations, timeouts, and security violations
Benefits:
- Enhanced debugging capabilities for troubleshooting command execution issues
Improved Documentation
- Improved docstrings with consistent parameter documentation and usage examples
- Type-aware examples that match actual implementation patterns
Code Simplification & Maintenance
- Removed redundant control flow logic while maintaining functionality
- Streamlined method implementations for better maintainability
- Consistent error handling patterns across sync and async variants
-
Commented out redundant
require_root()method that was superseded by the improved privilege detection approach - Enhanced subprocess cleanup with proper process termination and resource management
Technical Improvements
Typing Enhancements
Added appropriate type hints for all public methods and classes. Validated typing with mypy for 3.6+ compatibility.
Decorator Improvements
The @requires_root_privilege (alias user_is_root) decorator has been improved to not rely on os.getuid() == 0. This is crucial because users can have sudo access (which nmap requires) without being root with UID=0. The decorator now properly preserves function signatures and switches from returning an error dictionary to raising a NmapPrivilegeError when privilege requirements aren't met, which is more idiomatic in Python.
Why not os.getuid() == 0?
Many users have sudo access to run nmap with elevated privileges without actually being the root user (UID=0). The old approach would incorrectly reject these users. The new implementation:
-
On Windows: Uses
ctypes.windll.shell32.IsUserAnAdmin()to detect administrator privileges - On Unix/Linux: Attempts to run the command and analyzes error messages for privilege-related keywords
- Smart error detection: Looks for phrases like "root privileges", "administrator", "permission denied" in nmap output
This approach allows the library to work correctly whether users run their scripts with sudo or configure nmap.nmaptool = f"sudo {nmap.nmaptool}" manually.
@user_is_root
async def nmap_syn_scan(self, target: str, args: Optional[str] = None) -> Dict[str, Any]:
...
Cross-Platform Compatibility
-
Unix systems: Improved privilege detection (that doesn't rely on
os.getuid() == 0) -
Windows systems: UAC detection using
ctypes.windll.shell32.IsUserAnAdmin() -
Unified error handling: Clean exception hierarchy with
NmapErrorbase class allows catching all package exceptions with a single catch block
Backward Compatibility & Non-Breaking Changes
This refactoring as tried as much as possible to not introduce any breaking changes. All existing APIs and functionality remain unchanged:
# Existing code continues to work unchanged
nmap = NmapScanTechniques()
results = nmap.nmap_tcp_scan("192.168.1.1")
# All existing scan methods work exactly as before
results = nmap.nmap_syn_scan("192.168.1.1")
results = nmap.nmap_udp_scan("192.168.1.1")
results = nmap.nmap_ping_scan("192.168.1.1")
Only Minor Redundancy Removal: The require_root() method was commented out as it became redundant. This method was not widely used and users can now handle privilege elevation manually:
# Old approach (commented out but still visible in code):
# nmap.require_root(True) # This was redundant
# New approach (manual privilege elevation):
nmap = nmap3.Nmap()
nmap.nmaptool = f"sudo {nmap.nmaptool}" # or f"doas {nmap.nmaptool}" # For BSD systems
results = nmap.nmap_syn_scan("192.168.1.1") # Now with elevated privileges
Users can also use external privilege escalation libraries for more advanced scenarios.
Files Changed
-
nmap3/nmap3.py- Enhanced type annotations and documentation (commented out redundantrequire_root()method) -
nmap3/utils.py- Version-aware typing utilities and improved privilege detection -
nmap3/nmapparser.py- Type annotations for parser functions -
nmap3/exceptions.py- New exception hierarchy:NmapErrorbase class with specialized subclasses (NmapPrivilegeError,NmapTimeoutError,NmapExecutionError)
Support & Compatibility
No Breaking Changes: The refactoring maintains all existing functionality while adding new capabilities. Enhanced subprocess cleanup includes proper error handling, timeout management, and cleanup procedures.
Legacy Support: Existing codebases will continue to work without any modifications. The only change is that the redundant require_root() method is commented out (but still visible in the source for reference), as users can now manually handle privilege elevation when needed.