From 4c5312e141184f845bc0a6995a54b9489fcdaeef Mon Sep 17 00:00:00 2001 From: Prabhat Kumar Date: Mon, 30 Mar 2026 22:47:32 +0530 Subject: [PATCH 1/5] Reorganize profiles and scan methods into categorized accordions --- nettacker/api/core.py | 179 +++++++++++++++++++++++++------- nettacker/locale/en.yaml | 7 ++ nettacker/web/static/js/main.js | 99 ++++++++++++++---- 3 files changed, 225 insertions(+), 60 deletions(-) diff --git a/nettacker/api/core.py b/nettacker/api/core.py index b86905bd3..4f0901955 100644 --- a/nettacker/api/core.py +++ b/nettacker/api/core.py @@ -213,22 +213,81 @@ def profiles(): Returns: HTML content or available profiles """ - res = "" - for profile in sorted(Nettacker.load_profiles().keys()): - label = ( - "success" - if (profile == "scan") - else "warning" - if (profile == "brute") - else "danger" - if (profile == "vulnerability") - else "default" - ) + all_profiles = Nettacker.load_profiles() + if "all" in all_profiles: + del all_profiles["all"] + if "..." in all_profiles: + del all_profiles["..."] + + categories = { + "scan": { + "title": _("scan_modules_title"), + "desc": _("scan_modules_desc"), + "label": "success", + "profiles": [] + }, + "brute": { + "title": _("brute_modules_title"), + "desc": _("brute_modules_desc"), + "label": "warning", + "profiles": [] + }, + "vuln": { + "title": _("vuln_modules_title"), + "desc": _("vuln_modules_desc"), + "label": "danger", + "profiles": [] + } + } + + for profile in sorted(all_profiles.keys()): + modules = all_profiles[profile] + cats = set(m.split("_")[-1] for m in modules) + + for cat in cats: + if cat in categories: + categories[cat]["profiles"].append(profile) + elif cat == "vulnerability" or cat == "vuln": + categories["vuln"]["profiles"].append(profile) + + # Dedup and sort + for cat in categories: + categories[cat]["profiles"] = sorted(list(set(categories[cat]["profiles"]))) + + res = """ +
+ """ + for cat_name, cat_info in categories.items(): res += """ -      """.format( - profile, label - ) +
+
+

+ + {2} + {3} + +

+
+
+
+ """.format(cat_name, cat_info["label"], cat_info["title"], cat_info["desc"], _("select_all")) + + for profile in cat_info["profiles"]: + label_type = cat_info["label"] + res += """ +       + """.format(profile, cat_name, label_type) + + res += """ +
+
+
+ """ + res += "
" return res @@ -240,29 +299,69 @@ def scan_methods(): HTML content or available modules """ methods = Nettacker.load_modules() - methods.pop("all") - res = "" - for sm in methods.keys(): - label = ( - "success" - if sm.endswith("_scan") - else "warning" - if sm.endswith("_brute") - else "danger" - if sm.endswith("_vuln") - else "default" - ) - profile = ( - "scan" - if sm.endswith("_scan") - else "brute" - if sm.endswith("_brute") - else "vuln" - if sm.endswith("_vuln") - else "default" - ) - res += """     """.format( - sm, label, profile - ) + if "all" in methods: + methods.pop("all") + + categories = { + "scan": { + "title": _("scan_modules_title"), + "desc": _("scan_modules_desc"), + "label": "success", + "modules": [] + }, + "brute": { + "title": _("brute_modules_title"), + "desc": _("brute_modules_desc"), + "label": "warning", + "modules": [] + }, + "vuln": { + "title": _("vuln_modules_title"), + "desc": _("vuln_modules_desc"), + "label": "danger", + "modules": [] + } + } + + for sm in sorted(methods.keys()): + cat = sm.split("_")[-1] + if cat in categories: + categories[cat]["modules"].append(sm) + elif cat == "vulnerability": + categories["vuln"]["modules"].append(sm) + + res = """ +
+ """ + for cat_name, cat_info in categories.items(): + res += """ +
+
+

+ + {2} + {3} + +

+
+
+
+ """.format(cat_name, cat_info["label"], cat_info["title"], cat_info["desc"], _("select_all")) + + for module in cat_info["modules"]: + label_type = cat_info["label"] + res += """ +       + """.format(module, cat_name, label_type) + + res += """ +
+
+
+ """ + res += "
" return res diff --git a/nettacker/locale/en.yaml b/nettacker/locale/en.yaml index c3acde483..8782971e2 100644 --- a/nettacker/locale/en.yaml +++ b/nettacker/locale/en.yaml @@ -9,6 +9,13 @@ API_invalid: invalid API key API_key: " * API is accessible from https://nettacker-api.z3r0d4y.com:{0}/ via API Key: {1}" API_options: API options API_port: API port number +scan_modules_title: Scan Modules +scan_modules_desc: (Information Gathering and Reconnaissance) +brute_modules_title: Brute Force Modules +brute_modules_desc: (Authentication Attacks) +vuln_modules_title: Vulnerability Modules +vuln_modules_desc: (Exploit and Vulnerability Detection) +select_all: Select all Method: Method skip_service_discovery: skip service discovery before scan and enforce all modules to scan anyway no_live_service_found: no any live service found to scan. diff --git a/nettacker/web/static/js/main.js b/nettacker/web/static/js/main.js index 0ee398b36..bd2b4b4a9 100644 --- a/nettacker/web/static/js/main.js +++ b/nettacker/web/static/js/main.js @@ -350,20 +350,30 @@ $(document).ready(function () { // profiles var p = []; var n = 0; - $("#profiles input:checked").each(function () { - if (this.id !== "all_profiles") { + $("#profiles input[type='checkbox']:checked").each(function () { + if ( + this.id !== "all_profiles" && + !$(this).hasClass("check-all-category") + ) { p[n] = this.id; n += 1; } }); + // Deduplicate profiles as they might appear in multiple categories + p = Array.from(new Set(p)); var profiles = p.join(","); // scan_methods n = 0; sm = []; - $("#selected_modules input:checked").each(function () { - sm[n] = this.id; - n += 1; + $("#selected_modules input[type='checkbox']:checked").each(function () { + if ( + this.id !== "all" && + !$(this).hasClass("check-all-sm-category") + ) { + sm[n] = this.id; + n += 1; + } }); var selected_modules = sm.join(","); // language @@ -666,48 +676,97 @@ $(document).ready(function () { $(".checkbox").prop("checked", $(this).prop("checked")); }); - $(".checkbox-brute").click(function () { - $(".checkbox-brute-module").prop("checked", $(this).prop("checked")); + $(".checkbox-brute-profile").click(function () { + if (this.id === "brute") { + $(".checkbox-sm-brute-module").prop("checked", $(this).prop("checked")); + } }); - $(".checkbox-scan").click(function () { - $(".checkbox-scan-module").prop("checked", $(this).prop("checked")); + $(".checkbox-scan-profile").click(function () { + if (this.id === "scan") { + $(".checkbox-sm-scan-module").prop("checked", $(this).prop("checked")); + } }); - $(".checkbox-vulnerability").click(function () { - $(".checkbox-vuln-module").prop("checked", $(this).prop("checked")); + $(".checkbox-vuln-profile").click(function () { + if (this.id === "vulnerability" || this.id === "vuln") { + $(".checkbox-sm-vuln-module").prop("checked", $(this).prop("checked")); + } }); $(".check-all-profiles").click(function () { $("#profiles input[type='checkbox']").not(this).prop("checked", $(this).prop("checked")); + $(".check-all-category").prop("checked", $(this).prop("checked")); + }); + + $(document).on("change", ".check-all-category", function () { + var category = $(this).data("category"); + $(".checkbox-" + category + "-profile").prop("checked", $(this).prop("checked")); + }); + + $(document).on("show.bs.collapse", "#profile_accordion", function (e) { + $(e.target) + .prev(".panel-heading") + .find(".fa") + .removeClass("fa-chevron-right") + .addClass("fa-chevron-down"); + }); + + $(document).on("hide.bs.collapse", "#profile_accordion", function (e) { + $(e.target) + .prev(".panel-heading") + .find(".fa") + .removeClass("fa-chevron-down") + .addClass("fa-chevron-right"); }); $(".check-all-scans").click(function () { - $(".checkbox-brute-module").prop("checked", $(this).prop("checked")); - $(".checkbox-scan-module").prop("checked", $(this).prop("checked")); - $(".checkbox-vuln-module").prop("checked", $(this).prop("checked")); + $("#selected_modules input[type='checkbox']").not(this).prop("checked", $(this).prop("checked")); + $(".check-all-sm-category").prop("checked", $(this).prop("checked")); + }); + + $(document).on("change", ".check-all-sm-category", function () { + var category = $(this).data("category"); + $(".checkbox-sm-" + category + "-module").prop("checked", $(this).prop("checked")); + }); + + $(document).on("show.bs.collapse", "#scan_methods_accordion", function (e) { + $(e.target) + .prev(".panel-heading") + .find(".fa") + .removeClass("fa-chevron-right") + .addClass("fa-chevron-down"); + }); + + $(document).on("hide.bs.collapse", "#scan_methods_accordion", function (e) { + $(e.target) + .prev(".panel-heading") + .find(".fa") + .removeClass("fa-chevron-down") + .addClass("fa-chevron-right"); }); - $(".checkbox-vuln-module").click(function () { + $(document).on("click", ".checkbox-sm-vuln-module", function () { if (!$(this).is(":checked")) { $(".checkAll").prop("checked", false); - $(".checkbox-vulnerability").prop("checked", false); + $("#vulnerability").prop("checked", false); + $("#vuln").prop("checked", false); $(".check-all-scans").prop("checked", false); } }); - $(".checkbox-scan-module").click(function () { + $(document).on("click", ".checkbox-sm-scan-module", function () { if (!$(this).is(":checked")) { $(".checkAll").prop("checked", false); - $(".checkbox-scan").prop("checked", false); + $("#scan").prop("checked", false); $(".check-all-scans").prop("checked", false); } }); - $(".checkbox-brute-module").click(function () { + $(document).on("click", ".checkbox-sm-brute-module", function () { if (!$(this).is(":checked")) { $(".checkAll").prop("checked", false); - $(".checkbox-brute").prop("checked", false); + $("#brute").prop("checked", false); $(".check-all-scans").prop("checked", false); } }); From c7347facb7ad6a93ba43e4c182a9c45cbc4616a4 Mon Sep 17 00:00:00 2001 From: Prabhat Kumar Date: Mon, 30 Mar 2026 22:47:36 +0530 Subject: [PATCH 2/5] Synchronize profile selection with scan methods in frontend --- nettacker/api/core.py | 4 +-- nettacker/web/static/js/main.js | 46 ++++++++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/nettacker/api/core.py b/nettacker/api/core.py index 4f0901955..50fa75fd1 100644 --- a/nettacker/api/core.py +++ b/nettacker/api/core.py @@ -278,9 +278,9 @@ def profiles(): for profile in cat_info["profiles"]: label_type = cat_info["label"] res += """ -