Compare commits

...

11 Commits

Author SHA1 Message Date
codeshazard 676dc1c4d7
Merge add1e61e8a into 65bf88e68f 2025-11-24 16:43:08 +00:00
Sam Stepanyan add1e61e8a
Merge branch 'master' into windows-support-signed 2025-11-24 16:43:05 +00:00
codeshazard 03c5c89356
Merge branch 'master' into windows-support-signed 2025-11-08 11:47:25 +05:30
Shaswat e119fca7f2
Regenerate poetry.lock for Windows compatibility 2025-11-08 11:08:17 +05:30
Shaswat 5b47d16fba
Trigger Poetry lock regeneration 2025-11-08 10:28:17 +05:30
Shaswat baa7530252
Minor issue resolved 2025-10-16 22:35:14 +05:30
Shaswat 342e3c5e6a
Refactor: Move is_running_with_privileges to common utils 2025-10-16 18:31:15 +05:30
Shaswat 92c3627f33
Fix pre-commit formatting issues 2025-10-15 11:06:32 +05:30
Shaswat a8c49e7e81
Add Windows compatibility support
- Make uvloop optional using platform markers (Windows unsupported)
- Add win32 to allowed platforms in check_dependencies()
- Replace os.geteuid() with cross-platform privilege checking
- Add explicit UTF-8 encoding for file operations
- Fix hardcoded path separators using pathlib in:
  - load_graphs()
  - load_modules()
  - load_languages()
  - get_result_content()

Resolves #[933]
2025-10-15 11:06:15 +05:30
Shaswat 0b966cb2a7
Add Windows compatibility support
- Make uvloop optional using platform markers (Windows unsupported)
- Add win32 to allowed platforms in check_dependencies()
- Replace os.geteuid() with cross-platform privilege checking
- Add explicit UTF-8 encoding for file operations
- Fix hardcoded path separators using pathlib in:
  - load_graphs()
  - load_modules()
  - load_languages()
  - get_result_content()

This enables Nettacker to run on Windows systems.
2025-10-15 11:03:20 +05:30
Shaswat 67264422bc
Make uvloop optional for Windows compatibility
uvloop does not support Windows. This change makes it conditional
using platform markers, only installing on non-Windows platforms.
2025-10-15 10:59:55 +05:30
7 changed files with 37 additions and 16 deletions

View File

@ -5,6 +5,7 @@ import os
import random
import string
import time
from pathlib import Path
from threading import Thread
from types import SimpleNamespace
@ -392,7 +393,7 @@ def get_result_content():
return Response(
file_content,
mimetype=mime_types().get(os.path.splitext(filename)[1], "text/plain"),
headers={"Content-Disposition": "attachment;filename=" + filename.split("/")[-1]},
headers={"Content-Disposition": "attachment;filename=" + Path(filename).name},
)

View File

@ -27,7 +27,7 @@ from nettacker.core.messages import messages as _
from nettacker.core.module import Module
from nettacker.core.socks_proxy import set_socks_proxy
from nettacker.core.utils import common as common_utils
from nettacker.core.utils.common import wait_for_threads_to_finish
from nettacker.core.utils.common import wait_for_threads_to_finish, is_running_with_privileges
from nettacker.database.db import find_events, remove_old_logs
from nettacker.database.mysql import mysql_create_database, mysql_create_tables
from nettacker.database.postgresql import postgres_create_database
@ -66,7 +66,7 @@ class Nettacker(ArgParser):
log.reset_color()
def check_dependencies(self):
if sys.platform not in {"darwin", "freebsd13", "freebsd14", "freebsd15", "linux"}:
if sys.platform not in {"darwin", "freebsd13", "freebsd14", "freebsd15", "linux", "win32"}:
die_failure(_("error_platform"))
try:
@ -165,7 +165,7 @@ class Nettacker(ArgParser):
self.arguments.targets.append(sub_domain)
# icmp_scan
if self.arguments.ping_before_scan:
if os.geteuid() == 0:
if is_running_with_privileges():
selected_modules = self.arguments.selected_modules
self.arguments.selected_modules = ["icmp_scan"]
self.start_scan(scan_id)

View File

@ -1,6 +1,7 @@
import json
import sys
from argparse import ArgumentParser
from pathlib import Path
import yaml
@ -48,10 +49,9 @@ class ArgParser(ArgumentParser):
Returns:
an array of graph names
"""
graph_names = []
for graph_library in Config.path.graph_dir.glob("*/engine.py"):
graph_names.append(str(graph_library).split("/")[-2] + "_graph")
graph_names.append(graph_library.parent.name + "_graph")
return list(set(graph_names))
@staticmethod
@ -65,8 +65,7 @@ class ArgParser(ArgumentParser):
languages_list = []
for language in Config.path.locale_dir.glob("*.yaml"):
languages_list.append(str(language).split("/")[-1].split(".")[0])
languages_list.append(Path(language).stem)
return list(set(languages_list))
@staticmethod
@ -83,8 +82,9 @@ class ArgParser(ArgumentParser):
# Search for Modules
module_names = {}
for module_name in sorted(Config.path.modules_dir.glob("**/*.yaml")):
library = str(module_name).split("/")[-1].split(".")[0]
category = str(module_name).split("/")[-2]
module_path = Path(module_name)
library = module_path.stem
category = module_path.parent.name
module = f"{library}_{category}"
contents = yaml.safe_load(TemplateLoader(module).open().split("payload:")[0])
module_names[module] = contents["info"] if full_details else None

View File

@ -32,7 +32,9 @@ class TemplateLoader:
action = module_name_parts[-1]
library = "_".join(module_name_parts[:-1])
with open(Config.path.modules_dir / action / f"{library}.yaml") as yaml_file:
with open(
Config.path.modules_dir / action / f"{library}.yaml", encoding="utf-8"
) as yaml_file:
return yaml_file.read()
def format(self):

View File

@ -5,6 +5,7 @@ import hashlib
import importlib
import math
import multiprocessing
import os
import random
import re
import string
@ -450,3 +451,19 @@ def generate_compare_filepath(scan_id):
date_time=now(format="%Y_%m_%d_%H_%M_%S"),
scan_id=scan_id,
)
def is_running_with_privileges():
"""
Check if running with elevated privileges (root/admin)
Returns:
bool: True if running as root (Unix) or Administrator (Windows)
"""
if sys.platform == "win32":
try:
return ctypes.windll.shell32.IsUserAnAdmin() != 0
except Exception:
return False
else:
return os.geteuid() == 0

9
poetry.lock generated
View File

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
[[package]]
name = "aiohappyeyeballs"
@ -973,7 +973,7 @@ description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.8"
groups = ["main"]
markers = "python_version < \"3.10\""
markers = "python_version == \"3.9\""
files = [
{file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"},
{file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"},
@ -2023,7 +2023,7 @@ files = [
{file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"},
{file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"},
]
markers = {dev = "python_version < \"3.10\""}
markers = {dev = "python_version == \"3.9\""}
[[package]]
name = "urllib3"
@ -2050,6 +2050,7 @@ description = "Fast implementation of asyncio event loop on top of libuv"
optional = false
python-versions = ">=3.8.0"
groups = ["main"]
markers = "sys_platform != \"win32\""
files = [
{file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ec7e6b09a6fdded42403182ab6b832b71f4edaf7f37a9a0e371a01db5f0cb45f"},
{file = "uvloop-0.21.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:196274f2adb9689a289ad7d65700d37df0c0930fd8e4e743fa4834e850d7719d"},
@ -2254,4 +2255,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.1"
python-versions = "^3.9, <3.13"
content-hash = "0e1731401cd6acfc4d45ede5e18668530aae6a6b2e359d7dc8d8d635635a1257"
content-hash = "92b74357ab4116c32e15e55183aa60b5f30cce5c06da3a05e7d0eac945481721"

View File

@ -62,7 +62,7 @@ requests = "^2.32.3"
sqlalchemy = "^2.0.22"
texttable = "^1.7.0"
zipp = "^3.19.1"
uvloop = "^0.21.0"
uvloop = {version = "^0.21.0", markers = "sys_platform != 'win32'"}
pymysql = "^1.1.1"
impacket = "^0.11.0"