From 12d3511be43807ea92ca64120bb5101dac8661fa Mon Sep 17 00:00:00 2001 From: Andre Frank Date: Wed, 17 Sep 2025 09:10:54 -0700 Subject: [PATCH 01/11] fix: Replace os.path with pathlib.Path for cross-platform compatibility - Replace os.path.join, os.path.normpath, os.path.basename with pathlib equivalents - Update tests/conftest.py to use pathlib.Path instead of os.path functions - Modify nettacker/core/graph.py to use pathlib for path operations - Update nettacker/api/core.py and nettacker/api/engine.py to use Path objects - Fix tests/test_yaml_regexes.py to use pathlib.Path.name instead of os.path.basename - Remove obsolete os.path patches from tests/core/test_graph.py This ensures the application works consistently across Windows and Unix-like systems by eliminating platform-specific path assumptions throughout the codebase. Fixes #933 --- nettacker/api/core.py | 4 ++-- nettacker/api/engine.py | 10 +++++----- nettacker/core/graph.py | 7 +++---- tests/conftest.py | 12 ++++++------ tests/core/test_graph.py | 8 +------- tests/test_yaml_regexes.py | 11 ++++++----- 6 files changed, 23 insertions(+), 29 deletions(-) diff --git a/nettacker/api/core.py b/nettacker/api/core.py index 82163c1e0..029ae88ca 100644 --- a/nettacker/api/core.py +++ b/nettacker/api/core.py @@ -1,4 +1,4 @@ -import os +from pathlib import Path from flask import abort @@ -117,7 +117,7 @@ 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)): + if not str(Path(filename).resolve()).startswith(str(Config.path.web_static_dir.resolve())): abort(404) try: return open(filename, "rb").read() diff --git a/nettacker/api/engine.py b/nettacker/api/engine.py index 9979e05ba..8a791b7d9 100644 --- a/nettacker/api/engine.py +++ b/nettacker/api/engine.py @@ -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 @@ -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"), ) @@ -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 @@ -391,7 +391,7 @@ def get_result_content(): return Response( file_content, - mimetype=mime_types().get(os.path.splitext(filename)[1], "text/plain"), + mimetype=mime_types().get(Path(filename).suffix, "text/plain"), headers={"Content-Disposition": "attachment;filename=" + filename.split("/")[-1]}, ) diff --git a/nettacker/core/graph.py b/nettacker/core/graph.py index 0c92a604c..153f50d97 100644 --- a/nettacker/core/graph.py +++ b/nettacker/core/graph.py @@ -2,7 +2,6 @@ import html import importlib import json -import os import uuid from datetime import datetime from pathlib import Path @@ -371,11 +370,11 @@ 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 str(fullpath).startswith(str(base_path.resolve())): raise PermissionError if (len(fullpath) >= 5 and fullpath[-5:] == ".html") or ( diff --git a/tests/conftest.py b/tests/conftest.py index 318570998..048520168 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -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)) diff --git a/tests/core/test_graph.py b/tests/core/test_graph.py index 662f9b108..4406aa774 100644 --- a/tests/core/test_graph.py +++ b/tests/core/test_graph.py @@ -183,13 +183,9 @@ def test_text_report(mock_submit, mock_open_file, mock_build_text, mock_get_logs @patch("nettacker.core.graph.get_options_by_scan_id") @patch("nettacker.core.graph.build_compare_report", return_value="") @patch("nettacker.core.graph.Path.open", new_callable=mock_open) -@patch("nettacker.core.graph.os.path.normpath", side_effect=lambda x: x) -@patch("nettacker.core.graph.os.path.join", side_effect=lambda *args: "/".join(args)) @patch("nettacker.core.graph.create_compare_text_table", return_value="text-report") def test_html_json_csv_text( mock_text_table, - mock_join, - mock_norm, mock_open_file, mock_build_html, mock_get_opts, @@ -247,9 +243,7 @@ def logs_side_effect(scan_id): @patch("nettacker.core.graph.get_logs_by_scan_id") @patch("nettacker.core.graph.get_options_by_scan_id") -@patch("nettacker.core.graph.os.path.normpath", side_effect=lambda x: "/etc/passwd") -@patch("nettacker.core.graph.os.path.join", side_effect=lambda *args: "/etc/passwd") -def test_permission_error(mock_join, mock_norm, mock_opts, mock_logs): +def test_permission_error(mock_opts, mock_logs): dummy_log = { "target": "1.1.1.1", "module_name": "mod", diff --git a/tests/test_yaml_regexes.py b/tests/test_yaml_regexes.py index 8627aca62..6f3a46376 100644 --- a/tests/test_yaml_regexes.py +++ b/tests/test_yaml_regexes.py @@ -1,5 +1,5 @@ -import os import re +from pathlib import Path import pytest import yaml @@ -12,9 +12,10 @@ def get_yaml_files(): for base in BASE_DIRS: - for file in os.listdir(base): - if file.endswith(".yaml"): - yield os.path.join(base, file) + base_path = Path(base) + for file in base_path.iterdir(): + if file.suffix == ".yaml": + yield str(file) def load_yaml(file_path): @@ -77,7 +78,7 @@ def test_yaml_regexes_valid(yaml_file): if payloads[0].get("library") == "http": regexes = extract_http_regexes(payloads) elif payloads[0].get("library") == "socket": - file_name = os.path.basename(yaml_file) + file_name = Path(yaml_file).name regexes = extract_socket_regexes(file_name, payloads) else: pytest.skip(f"Unknown library type in {yaml_file}") From 4e9ce134089225121028c3cb923680c290849157 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Sun, 21 Sep 2025 21:12:08 -0700 Subject: [PATCH 02/11] security: Implement CodeRabbit security suggestions - Replace string startswith() with Path.is_relative_to() for secure path validation - Fix runtime TypeError where Path object was treated as string in file extension checks - Use proper pathlib.Path.suffix for file extension detection - Improve Content-Disposition header with proper filename extraction - Remove redundant str() call around json.dumps() Security improvements: - Prevents path traversal attacks using modern pathlib semantics - Uses resolve(strict=True) to ensure target file exists before validation - Replaces brittle string prefix checks with robust path relationship validation Bug fixes: - Fixes TypeError in report generation where len(Path_object) would fail - Handles complex file extensions like .dd.json correctly - Maintains backward compatibility while using modern Python patterns --- nettacker/api/core.py | 13 ++++++++----- nettacker/api/engine.py | 2 +- nettacker/core/graph.py | 25 ++++++++++++++----------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/nettacker/api/core.py b/nettacker/api/core.py index 029ae88ca..aaaa10873 100644 --- a/nettacker/api/core.py +++ b/nettacker/api/core.py @@ -117,13 +117,16 @@ def get_file(filename): Returns: content of the file or abort(404) """ - if not str(Path(filename).resolve()).startswith(str(Config.path.web_static_dir.resolve())): - abort(404) + base = Config.path.web_static_dir.resolve() try: - return open(filename, "rb").read() - except ValueError: + target = Path(filename).resolve(strict=True) + except FileNotFoundError: + abort(404) + if not target.is_relative_to(base): abort(404) - except IOError: + try: + return target.read_bytes() + except OSError: abort(404) diff --git a/nettacker/api/engine.py b/nettacker/api/engine.py index 8a791b7d9..45c85bffe 100644 --- a/nettacker/api/engine.py +++ b/nettacker/api/engine.py @@ -392,7 +392,7 @@ def get_result_content(): return Response( file_content, mimetype=mime_types().get(Path(filename).suffix, "text/plain"), - headers={"Content-Disposition": "attachment;filename=" + filename.split("/")[-1]}, + headers={"Content-Disposition": f'attachment; filename="{Path(filename).name}"'}, ) diff --git a/nettacker/core/graph.py b/nettacker/core/graph.py index 153f50d97..e58bfe245 100644 --- a/nettacker/core/graph.py +++ b/nettacker/core/graph.py @@ -374,27 +374,30 @@ def get_modules_ports(item): compare_report_path_filename = sanitize_path(compare_report_path_filename) fullpath = (base_path / compare_report_path_filename).resolve() - if not str(fullpath).startswith(str(base_path.resolve())): + 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)) From 304e78670aa8fcd437cd74bb48eb603eb147c7fb Mon Sep 17 00:00:00 2001 From: Claude Code Date: Sun, 21 Sep 2025 21:31:22 -0700 Subject: [PATCH 03/11] fix: Restore permission error test functionality - Add sanitize_path mock to ensure PermissionError test can trigger properly - Mock returns '../../etc/passwd' to simulate path traversal attempt - Preserves test intent while adapting to new pathlib.Path approach - Addresses reviewer concern about removed os.path mocks --- tests/core/test_graph.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/core/test_graph.py b/tests/core/test_graph.py index 4406aa774..21de098d6 100644 --- a/tests/core/test_graph.py +++ b/tests/core/test_graph.py @@ -243,7 +243,8 @@ def logs_side_effect(scan_id): @patch("nettacker.core.graph.get_logs_by_scan_id") @patch("nettacker.core.graph.get_options_by_scan_id") -def test_permission_error(mock_opts, mock_logs): +@patch("nettacker.core.graph.sanitize_path", return_value="../../etc/passwd") +def test_permission_error(mock_sanitize, mock_opts, mock_logs): dummy_log = { "target": "1.1.1.1", "module_name": "mod", From a30f91dc28be2aa6a15fbd1e01418be0c61bc431 Mon Sep 17 00:00:00 2001 From: Shaswat Date: Sat, 27 Sep 2025 03:24:13 +0530 Subject: [PATCH 04/11] Fix windows compatibility issue - Path handling, CLI parsing, Language detection --- nettacker/core/app.py | 8 +++--- nettacker/core/arg_parser.py | 29 ++++++++----------- nettacker/core/messages.py | 54 ++++++++++++++++++------------------ nettacker/core/template.py | 2 +- results | 45 ++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 49 deletions(-) create mode 100644 results diff --git a/nettacker/core/app.py b/nettacker/core/app.py index 5cd47a98b..b71549e93 100644 --- a/nettacker/core/app.py +++ b/nettacker/core/app.py @@ -41,7 +41,7 @@ class Nettacker(ArgParser): def __init__(self, api_arguments=None): if not api_arguments: self.print_logo() - self.check_dependencies() + #self.check_dependencies() log.info(_("scan_started")) super().__init__(api_arguments=api_arguments) @@ -65,9 +65,9 @@ def print_logo(): ) log.reset_color() - def check_dependencies(self): - if sys.platform not in {"darwin", "freebsd13", "freebsd14", "freebsd15", "linux"}: - die_failure(_("error_platform")) + #def check_dependencies(self): + #if sys.platform not in {"darwin", "freebsd13", "freebsd14", "freebsd15", "linux"}: + #die_failure(_("error_platform")) try: Config.path.tmp_dir.mkdir(exist_ok=True, parents=True) diff --git a/nettacker/core/arg_parser.py b/nettacker/core/arg_parser.py index e8aed1218..5bc250202 100644 --- a/nettacker/core/arg_parser.py +++ b/nettacker/core/arg_parser.py @@ -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): @@ -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 @@ -145,7 +140,7 @@ def add_arguments(self): engine_options.add_argument( "-v", "--verbose", - action="store_true", + type=int, dest="verbose_mode", default=Config.settings.verbose_mode, help=_("verbose_mode"), @@ -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"] # Method Options method_options = self.add_argument_group(_("Method"), _("scan_method_options")) diff --git a/nettacker/core/messages.py b/nettacker/core/messages.py index 9f07cb730..80a9b398c 100644 --- a/nettacker/core/messages.py +++ b/nettacker/core/messages.py @@ -1,4 +1,5 @@ import sys +import os from io import StringIO import yaml @@ -20,52 +21,51 @@ 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.load(f, Loader=yaml.FullLoader) 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 - ) - ) + print(f"[DEBUG] Selected language: {self.language}") + print(f"[DEBUG] Available languages: {get_languages()}") + # 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) - ) - for message_id in self.messages_en: - if message_id not in self.messages: - self.messages[message_id] = self.messages_en[message_id] + 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] + print(f"[DEBUG] Selected language: {self.language}") -message_cache = load_message().messages +try: + message_cache = load_message().messages +except Exception as e: + print(f"[!] Failed to load messages: {e}") + 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)) diff --git a/nettacker/core/template.py b/nettacker/core/template.py index ab6207237..646a1ad8a 100644 --- a/nettacker/core/template.py +++ b/nettacker/core/template.py @@ -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): diff --git a/results b/results new file mode 100644 index 000000000..f6f59ebb6 --- /dev/null +++ b/results @@ -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 From 6e180451a841294885c72b16ac13c56653cabb87 Mon Sep 17 00:00:00 2001 From: Shaswat Date: Sat, 27 Sep 2025 22:00:27 +0530 Subject: [PATCH 05/11] Fix static file resolution: anchor to web_static_dir --- nettacker/api/core.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nettacker/api/core.py b/nettacker/api/core.py index aaaa10873..3ded7d367 100644 --- a/nettacker/api/core.py +++ b/nettacker/api/core.py @@ -118,15 +118,14 @@ def get_file(filename): content of the file or abort(404) """ base = Config.path.web_static_dir.resolve() - try: - target = Path(filename).resolve(strict=True) - except FileNotFoundError: - abort(404) + target = (base / filename).resolve() + if not target.is_relative_to(base): abort(404) + try: return target.read_bytes() - except OSError: + except (FileNotFoundError, OSError): abort(404) From 8d39d1f257529e5c19caaf311e4554e725588f5f Mon Sep 17 00:00:00 2001 From: Shaswat Date: Sat, 27 Sep 2025 22:22:21 +0530 Subject: [PATCH 06/11] Refactor check_dependencies: enable Windows, preserve setup logic --- nettacker/core/app.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/nettacker/core/app.py b/nettacker/core/app.py index b71549e93..ace1717e2 100644 --- a/nettacker/core/app.py +++ b/nettacker/core/app.py @@ -41,7 +41,7 @@ class Nettacker(ArgParser): def __init__(self, api_arguments=None): if not api_arguments: self.print_logo() - #self.check_dependencies() + self.check_dependencies() log.info(_("scan_started")) super().__init__(api_arguments=api_arguments) @@ -64,11 +64,13 @@ def print_logo(): ) ) log.reset_color() - - #def check_dependencies(self): - #if sys.platform not in {"darwin", "freebsd13", "freebsd14", "freebsd15", "linux"}: - #die_failure(_("error_platform")) - + + # Allow Windows and other platforms by removing hardcoded restrictions + def check_dependencies(self): + if sys.platform.startswith("win"): + 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) From 489afa656ec90b27341c185849631730c89ab706 Mon Sep 17 00:00:00 2001 From: Shaswat Date: Sat, 27 Sep 2025 22:27:53 +0530 Subject: [PATCH 07/11] Use yaml.safe_load for secure locale parsing --- nettacker/core/messages.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nettacker/core/messages.py b/nettacker/core/messages.py index 80a9b398c..e03ee3615 100644 --- a/nettacker/core/messages.py +++ b/nettacker/core/messages.py @@ -22,7 +22,7 @@ def application_language(): def load_yaml(filename): with open(filename, "r", encoding="utf-8") as f: - return yaml.load(f, Loader=yaml.FullLoader) + return yaml.safe_load(f) def get_languages(): From 94332c2e26bac9f331eb3225403891687711f4df Mon Sep 17 00:00:00 2001 From: Shaswat Date: Sat, 27 Sep 2025 22:38:03 +0530 Subject: [PATCH 08/11] Clean up duplicate debug log in load_message --- nettacker/core/messages.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/nettacker/core/messages.py b/nettacker/core/messages.py index e03ee3615..fcb927123 100644 --- a/nettacker/core/messages.py +++ b/nettacker/core/messages.py @@ -34,21 +34,20 @@ def get_languages(): class load_message: def __init__(self): self.language = application_language() - print(f"[DEBUG] Selected language: {self.language}") - print(f"[DEBUG] Available languages: {get_languages()}") + log.debug(f"Selected language: {self.language}") + log.debug(f"Available languages: {get_languages()}") + # 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": - 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] - print(f"[DEBUG] Selected language: {self.language}") + 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] try: message_cache = load_message().messages From fcb3c7c55f1b22c336af6a5cadfa5892cd3e8e9a Mon Sep 17 00:00:00 2001 From: Shaswat Date: Sat, 27 Sep 2025 22:45:10 +0530 Subject: [PATCH 09/11] Unconditionally invoke check_dependencies; restrict logo to CLI --- nettacker/core/app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nettacker/core/app.py b/nettacker/core/app.py index ace1717e2..fddd17a56 100644 --- a/nettacker/core/app.py +++ b/nettacker/core/app.py @@ -41,7 +41,8 @@ class Nettacker(ArgParser): def __init__(self, api_arguments=None): if not api_arguments: self.print_logo() - self.check_dependencies() + + self.check_dependencies() log.info(_("scan_started")) super().__init__(api_arguments=api_arguments) From b41f6d9901b9a6d2c713e67cee39c58255f3c3fd Mon Sep 17 00:00:00 2001 From: Shaswat Date: Sat, 27 Sep 2025 22:53:08 +0530 Subject: [PATCH 10/11] Initialize logger in messages module to fix NameError --- nettacker/core/messages.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/nettacker/core/messages.py b/nettacker/core/messages.py index fcb927123..8ef25df9a 100644 --- a/nettacker/core/messages.py +++ b/nettacker/core/messages.py @@ -2,12 +2,14 @@ import os from io import StringIO +import logging +log = logging.getLogger(__name__) + 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" From 4f8cae9d83424b3503996865140b153b52651d5e Mon Sep 17 00:00:00 2001 From: Shaswat Date: Sat, 27 Sep 2025 23:23:22 +0530 Subject: [PATCH 11/11] Used logger exception --- nettacker/core/messages.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/nettacker/core/messages.py b/nettacker/core/messages.py index 8ef25df9a..9a3de7287 100644 --- a/nettacker/core/messages.py +++ b/nettacker/core/messages.py @@ -53,11 +53,10 @@ def __init__(self): try: message_cache = load_message().messages -except Exception as e: - print(f"[!] Failed to load messages: {e}") +except (OSError, yaml.YAMLError) as exc: + log.exception("Failed to load messages during initialization") message_cache = {} - def messages(msg_id): """ Load a message from the message library with the selected language.