Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions nettacker/api/core.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import os
from pathlib import Path

from flask import abort

Expand Down Expand Up @@ -117,13 +117,15 @@ def get_file(filename):
Returns:
content of the file or abort(404)
"""
if not os.path.normpath(filename).startswith(str(Config.path.web_static_dir)):
base = Config.path.web_static_dir.resolve()
target = (base / filename).resolve()

if not target.is_relative_to(base):
abort(404)

try:
return open(filename, "rb").read()
except ValueError:
abort(404)
except IOError:
return target.read_bytes()
except (FileNotFoundError, OSError):
abort(404)


Expand Down
12 changes: 6 additions & 6 deletions nettacker/api/engine.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import csv
import json
import multiprocessing
import os
import random
import string
import time
from pathlib import Path
from threading import Thread
from types import SimpleNamespace

Expand Down Expand Up @@ -187,8 +187,8 @@ def get_statics(path):
"""
static_types = mime_types()
return Response(
get_file(os.path.join(Config.path.web_static_dir, path)),
mimetype=static_types.get(os.path.splitext(path)[1], "text/html"),
get_file(Config.path.web_static_dir / path),
mimetype=static_types.get(Path(path).suffix, "text/html"),
)


Expand Down Expand Up @@ -220,7 +220,7 @@ def sanitize_report_path_filename(report_path_filename):
Returns:
the sanitized report path filename
"""
filename = secure_filename(os.path.basename(report_path_filename))
filename = secure_filename(Path(report_path_filename).name)
if not filename:
return False
# Define a list or tuple of valid extensions
Expand Down Expand Up @@ -391,8 +391,8 @@ 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]},
mimetype=mime_types().get(Path(filename).suffix, "text/plain"),
headers={"Content-Disposition": f'attachment; filename="{Path(filename).name}"'},
)


Expand Down
11 changes: 7 additions & 4 deletions nettacker/core/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Nettacker(ArgParser):
def __init__(self, api_arguments=None):
if not api_arguments:
self.print_logo()

self.check_dependencies()

log.info(_("scan_started"))
Expand All @@ -64,11 +65,13 @@ def print_logo():
)
)
log.reset_color()


# Allow Windows and other platforms by removing hardcoded restrictions
def check_dependencies(self):
if sys.platform not in {"darwin", "freebsd13", "freebsd14", "freebsd15", "linux"}:
die_failure(_("error_platform"))

if sys.platform.startswith("win"):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't right. You're basically saying all platforms are supported (we're close but we don't want to make that statement yet). Please just add win32 to the set, should be good enough

Copy link
Author

@codeshazard codeshazard Sep 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the advise! Honestly I don't have much knowledge about it and I'm constantly learning while doing it! I've been using AI at instances to understand the code better in order to contribute efficiently! I'll stick to path for path management and try to refactor the code using only path. I've done the necessary changes u asked for such as adding only windows compatibility and removing every other improvements not related to it.

After refactoring the code to using only path now I need to check whether it is windows compatible or not.

In order to do that, I ran a test
pytest -v

Now solving the errors and debugging!

Any suggestions for what I'm doing!

log.info("Running on Windows — platform support enabled.")
else:
log.info(f"Running on {sys.platform} — platform support enabled.")
try:
Config.path.tmp_dir.mkdir(exist_ok=True, parents=True)
Config.path.results_dir.mkdir(exist_ok=True, parents=True)
Expand Down
29 changes: 12 additions & 17 deletions nettacker/core/arg_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,17 @@ def load_graphs():

graph_names = []
for graph_library in Config.path.graph_dir.glob("*/engine.py"):
graph_names.append(str(graph_library).split("/")[-2] + "_graph")
import os
graph_names.append(os.path.basename(os.path.dirname(str(graph_library))) + "_graph")
return list(set(graph_names))

@staticmethod
def load_languages():
"""
Get available languages

Returns:
an array of languages
"""
languages_list = []

for language in Config.path.locale_dir.glob("*.yaml"):
languages_list.append(str(language).split("/")[-1].split(".")[0])

return list(set(languages_list))
from os.path import basename, splitext
return [
splitext(basename(str(path)))[0]
for path in Config.path.locale_dir.glob("*.yaml")
]

@staticmethod
def load_modules(limit=-1, full_details=False):
Expand All @@ -83,8 +77,9 @@ def load_modules(limit=-1, full_details=False):
# 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]
import os
library = os.path.splitext(os.path.basename(str(module_name)))[0]
category = os.path.basename(os.path.dirname(str(module_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
Expand Down Expand Up @@ -145,7 +140,7 @@ def add_arguments(self):
engine_options.add_argument(
"-v",
"--verbose",
action="store_true",
type=int,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this related to windows compatibility?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not!
Previously when trying to run the code it was giving an error so in order to resolve it I changed it.
I'll try to run the code without changing it.

dest="verbose_mode",
default=Config.settings.verbose_mode,
help=_("verbose_mode"),
Expand Down Expand Up @@ -210,7 +205,7 @@ def add_arguments(self):

# Exclude Module Name
exclude_modules = sorted(self.modules.keys())[:10]
exclude_modules.remove("all")
exclude_modules = [m for m in exclude_modules if m != "all"]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change related to windows compatibility?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not!
Previously when trying to run the code it was giving an error so in order to resolve it I changed it.
I'll try to run the code without changing it.


# Method Options
method_options = self.add_argument_group(_("Method"), _("scan_method_options"))
Expand Down
30 changes: 16 additions & 14 deletions nettacker/core/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import html
import importlib
import json
import os
import uuid
from datetime import datetime
from pathlib import Path
Expand Down Expand Up @@ -371,31 +370,34 @@ def get_modules_ports(item):
else generate_compare_filepath(scan_id)
)

base_path = str(nettacker_path_config.results_dir)
base_path = nettacker_path_config.results_dir
compare_report_path_filename = sanitize_path(compare_report_path_filename)
fullpath = os.path.normpath(os.path.join(base_path, compare_report_path_filename))
fullpath = (base_path / compare_report_path_filename).resolve()

if not fullpath.startswith(base_path):
if not fullpath.is_relative_to(base_path.resolve()):
raise PermissionError

if (len(fullpath) >= 5 and fullpath[-5:] == ".html") or (
len(fullpath) >= 4 and fullpath[-4:] == ".htm"
):
suffix = fullpath.suffix.lower()
suffixes = [s.lower() for s in fullpath.suffixes]
if suffix in (".html", ".htm"):
html_report = build_compare_report(compare_results)
with Path(fullpath).open("w", encoding="utf-8") as compare_report:
with fullpath.open("w", encoding="utf-8") as compare_report:
compare_report.write(html_report + "\n")
elif len(fullpath) >= 5 and fullpath[-5:] == ".json":
with Path(fullpath).open("w", encoding="utf-8") as compare_report:
compare_report.write(str(json.dumps(compare_results)) + "\n")
elif len(fullpath) >= 5 and fullpath[-4:] == ".csv":
elif suffixes[-2:] == [".dd", ".json"]:
with fullpath.open("w", encoding="utf-8") as compare_report:
compare_report.write(json.dumps(compare_results) + "\n")
elif suffix == ".json":
with fullpath.open("w", encoding="utf-8") as compare_report:
compare_report.write(json.dumps(compare_results) + "\n")
elif suffix == ".csv":
keys = compare_results.keys()
with Path(fullpath).open("a") as csvfile:
with fullpath.open("a") as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=keys)
if csvfile.tell() == 0:
writer.writeheader()
writer.writerow(compare_results)
else:
with Path(fullpath).open("w", encoding="utf-8") as compare_report:
with fullpath.open("w", encoding="utf-8") as compare_report:
compare_report.write(create_compare_text_table(compare_results))

log.write(create_compare_text_table(compare_results))
Expand Down
54 changes: 27 additions & 27 deletions nettacker/core/messages.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import sys
import os
from io import StringIO

import logging
log = logging.getLogger(__name__)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we use our own logger at nettacker.logger from where you import the get_logger() function

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, made the necessary changes!


import yaml

from nettacker.config import Config
from nettacker.core.utils.common import find_args_value


def application_language():
if "-L" in sys.argv:
language = find_args_value("-L") or "en"
Expand All @@ -20,52 +23,49 @@ def application_language():


def load_yaml(filename):
return yaml.load(StringIO(open(filename, "r").read()), Loader=yaml.FullLoader)
with open(filename, "r", encoding="utf-8") as f:
return yaml.safe_load(f)


def get_languages():
"""
Get available languages

Returns:
an array of languages
"""
languages_list = []

for language in Config.path.locale_dir.glob("*.yaml"):
languages_list.append(str(language).split("/")[-1].split(".")[0])
languages_list.append(os.path.splitext(os.path.basename(str(language)))[0])
return list(set(languages_list))


class load_message:
def __init__(self):
self.language = application_language()
self.messages = load_yaml(
"{messages_path}/{language}.yaml".format(
messages_path=Config.path.locale_dir, language=self.language
)
)
log.debug(f"Selected language: {self.language}")
log.debug(f"Available languages: {get_languages()}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have a .debug() in our logger

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThankYou! for letting me know that, I'll use log.info in accordance with Nettacker logger.


# Build path safely
language_file = os.path.join(Config.path.locale_dir, f"{self.language}.yaml")
self.messages = load_yaml(language_file)

if self.language != "en":
self.messages_en = load_yaml(
"{messages_path}/en.yaml".format(messages_path=Config.path.locale_dir)
)
fallback_file = os.path.join(Config.path.locale_dir, "en.yaml")
self.messages_en = load_yaml(fallback_file)

for message_id in self.messages_en:
if message_id not in self.messages:
self.messages[message_id] = self.messages_en[message_id]


message_cache = load_message().messages

try:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If unrelated to OS compatibility, please don't include them

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, made the necessary changes.

message_cache = load_message().messages
except (OSError, yaml.YAMLError) as exc:
log.exception("Failed to load messages during initialization")
message_cache = {}

def messages(msg_id):
"""
load a message from message library with specified language
Load a message from the message library with the selected language.

Args:
msg_id: message id
msg_id: message ID

Returns:
the message content in the selected language if
message found otherwise return message in English
The message content in the selected language if found,
otherwise returns the message ID itself as a fallback.
"""
return message_cache[str(msg_id)]
return message_cache.get(str(msg_id), str(msg_id))
2 changes: 1 addition & 1 deletion nettacker/core/template.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def open(self):
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):
Expand Down
45 changes: 45 additions & 0 deletions results
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
+-------------------+-----------------+-------------+-------+------------------+
| date | target | module_name | port | logs |
+===================+=================+=============+=======+==================+
| 2025-09-27 | scanme.nmap.org | port_scan | 80 | {'running_servic |
| 02:47:03.217353 | | | | e': 'http', |
| | | | | 'matched_regex': |
| | | | | ['HTTP/1.1 400', |
| | | | | 'Content-Type: |
| | | | | ', 'Content- |
| | | | | Length: 306', |
| | | | | 'Server: '], 'de |
| | | | | fault_service': |
| | | | | 'http', |
| | | | | 'ssl_flag': |
| | | | | False} |
+-------------------+-----------------+-------------+-------+------------------+
| 2025-09-27 | scanme.nmap.org | port_scan | 22 | {'running_servic |
| 02:47:03.221971 | | | | e': 'open_port', |
| | | | | 'matched_regex': |
| | | | | ['22'], 'default |
| | | | | _service': |
| | | | | 'ssh', |
| | | | | 'ssl_flag': |
| | | | | False} |
+-------------------+-----------------+-------------+-------+------------------+
| 2025-09-27 | scanme.nmap.org | port_scan | 9929 | {'running_servic |
| 02:47:29.738556 | | | | e': 'open_port', |
| | | | | 'matched_regex': |
| | | | | ['9929'], 'defau |
| | | | | lt_service': |
| | | | | 'unknown', |
| | | | | 'ssl_flag': |
| | | | | False} |
+-------------------+-----------------+-------------+-------+------------------+
| 2025-09-27 | scanme.nmap.org | port_scan | 31337 | {'running_servic |
| 02:47:31.377412 | | | | e': 'open_port', |
| | | | | 'matched_regex': |
| | | | | ['31337'], 'defa |
| | | | | ult_service': |
| | | | | 'unknown', |
| | | | | 'ssl_flag': |
| | | | | False} |
+-------------------+-----------------+-------------+-------+------------------+

Software Details: OWASP Nettacker version 0.4.0 [QUIN] in 2025-09-27 02:47:37
12 changes: 6 additions & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import sys
from os.path import abspath, dirname, join
from pathlib import Path

project_root = dirname(dirname(__file__))
nettacker_dir = abspath(join(project_root, "nettacker"))
tests_dir = abspath(join(project_root, "tests"))
project_root = Path(__file__).parent.parent
nettacker_dir = (project_root / "nettacker").resolve()
tests_dir = (project_root / "tests").resolve()

sys.path.insert(0, nettacker_dir)
sys.path.insert(1, tests_dir)
sys.path.insert(0, str(nettacker_dir))
sys.path.insert(1, str(tests_dir))
Loading