diff --git a/nettacker/api/engine.py b/nettacker/api/engine.py index 9979e05b..e5475968 100644 --- a/nettacker/api/engine.py +++ b/nettacker/api/engine.py @@ -374,10 +374,12 @@ def get_results(): @app.route("/results/get", methods=["GET"]) def get_result_content(): """ - get a result HTML/TEXT/JSON content - + Retrieve the raw scan result content for a given report id and return it as an HTTP response. + + Validates the API key on the incoming request. If the "id" parameter is missing, responds with HTTP 400; if the report cannot be retrieved, responds with HTTP 500. + Returns: - content of the scan result + Response: HTTP response whose body is the scan file content, with the MIME type determined from the file extension and a `Content-Disposition` attachment filename header. """ api_key_is_valid(app, flask_request) scan_id = get_value(flask_request, "id") @@ -392,7 +394,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=" + os.path.basename(filename)}, ) @@ -612,4 +614,4 @@ def start_api_server(options): except KeyboardInterrupt: for process in multiprocessing.active_children(): process.terminate() - break + break \ No newline at end of file diff --git a/nettacker/core/arg_parser.py b/nettacker/core/arg_parser.py index e8aed121..210280c5 100644 --- a/nettacker/core/arg_parser.py +++ b/nettacker/core/arg_parser.py @@ -43,48 +43,52 @@ class ArgParser(ArgumentParser): @staticmethod def load_graphs(): """ - load all available graphs - + Discover available graph engine names. + Returns: - an array of graph names + graph_names (list[str]): Unique graph identifiers found under the graph directory, formatted as "_graph". """ 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 def load_languages(): """ - Get available languages - + Discover available language identifiers by scanning the locale directory for YAML files. + Returns: - an array of languages + list[str]: Unique language names derived from locale file stems (order not guaranteed). """ languages_list = [] for language in Config.path.locale_dir.glob("*.yaml"): - languages_list.append(str(language).split("/")[-1].split(".")[0]) + languages_list.append(language.stem) return list(set(languages_list)) @staticmethod def load_modules(limit=-1, full_details=False): """ - load all available modules - - limit: return limited number of modules - full: with full details - + Load available scanning modules from the configured modules directory. + + Parameters: + limit (int): Maximum number of modules to return; -1 means no limit. If the limit is reached a "..." placeholder will be inserted. + full_details (bool): If True, include each module's parsed `info` mapping as the value; otherwise the value will be None. + Returns: - an array of all module names + dict: Mapping of module keys ("_") to either their `info` dict (when full_details is True) or None. Always includes an "all" entry and may include a "..." placeholder when truncated. + + Notes: + This function also updates the module severity and description cache (all_module_severity_and_desc) for each discovered module. """ # 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] + library = module_name.stem + category = module_name.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 @@ -763,4 +767,4 @@ class ArgParser(ArgumentParser): options.time_sleep_between_requests = float(options.time_sleep_between_requests) options.retries = int(options.retries) - self.arguments = options + self.arguments = options \ No newline at end of file diff --git a/nettacker/core/messages.py b/nettacker/core/messages.py index 9f07cb73..c6599da7 100644 --- a/nettacker/core/messages.py +++ b/nettacker/core/messages.py @@ -25,15 +25,17 @@ def load_yaml(filename): def get_languages(): """ - Get available languages - + Return a deduplicated list of available locale identifiers. + + Scans Config.path.locale_dir for files with a .yaml extension and collects each file's stem (filename without extension) as a language identifier. + Returns: - an array of languages + languages (list[str]): List of unique language codes derived from locale YAML filenames. """ languages_list = [] for language in Config.path.locale_dir.glob("*.yaml"): - languages_list.append(str(language).split("/")[-1].split(".")[0]) + languages_list.append(language.stem) return list(set(languages_list)) @@ -68,4 +70,4 @@ def messages(msg_id): the message content in the selected language if message found otherwise return message in English """ - return message_cache[str(msg_id)] + return message_cache[str(msg_id)] \ No newline at end of file diff --git a/nettacker/core/template.py b/nettacker/core/template.py index ab620723..6cdba32e 100644 --- a/nettacker/core/template.py +++ b/nettacker/core/template.py @@ -28,15 +28,29 @@ class TemplateLoader: return module_content def open(self): + """ + Return the contents of the YAML module file identified by this loader's name. + + The name is split on underscores to derive an action (last segment) and a library (remaining segments joined with underscores); the file at Config.path.modules_dir / action / f"{library}.yaml" is opened with UTF-8 encoding and its full text is returned. + + Returns: + str: The raw text of the YAML file. + """ module_name_parts = self.name.split("_") 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): + """ + Render the module's YAML template by applying the instance's inputs as format placeholders. + + Returns: + formatted (str): The template string produced by calling `open()` and applying `str.format` with `self.inputs`. + """ return self.open().format(**self.inputs) def load(self): - return self.parse(yaml.safe_load(self.format()), self.inputs) + return self.parse(yaml.safe_load(self.format()), self.inputs) \ No newline at end of file diff --git a/nettacker/core/utils/common.py b/nettacker/core/utils/common.py index 6418bbb9..6a8e6b81 100644 --- a/nettacker/core/utils/common.py +++ b/nettacker/core/utils/common.py @@ -165,6 +165,16 @@ def find_args_value(args_name): def re_address_repeaters_key_name(key_name): + # Note: This uses "/" as a key delimiter, not a file path separator + """ + Format a slash-delimited key path into a sequence of dictionary-access segments excluding the final key. + + Parameters: + key_name (str): A "/"-delimited key path ("/" is a key delimiter, not a filesystem separator). + + Returns: + str: Concatenated "['key']" segments for every component of `key_name` except the last; returns an empty string if there are no components before the final segment. + """ return "".join(["['" + _key + "']" for _key in key_name.split("/")[:-1]]) @@ -449,4 +459,4 @@ def generate_compare_filepath(scan_id): return "/report_compare_{date_time}_{scan_id}.json".format( date_time=now(format="%Y_%m_%d_%H_%M_%S"), scan_id=scan_id, - ) + ) \ No newline at end of file