From 15b6bd21c1138d2c862568986d9396ab02d2951e Mon Sep 17 00:00:00 2001
From: sscottgvit <sscott@govanguard.com>
Date: Mon, 20 Nov 2023 19:18:52 -0600
Subject: [PATCH] Updates

---
 CHANGELOG.txt            |  7 +++++++
 app/ApplicationInfo.py   |  4 ++--
 buildHugeNmapTest.py     | 37 +++++++++++++++++++++++++++++++++----
 controller/controller.py |  3 +++
 debian/changelog         |  9 +++++++++
 debian/control           |  2 +-
 ui/view.py               | 10 ++++++++++
 7 files changed, 65 insertions(+), 7 deletions(-)

diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 48fdd59c..5d109203 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,10 @@
+LEGION 0.4.3
+
+* Revise NMAP import process
+* Fix import progress calculations
+* Doubleckick to copy hostname (Linux only)
+* Script to generate huge bogus NMAP XML imports for testing.
+
 LEGION 0.4.2
 
 * Tweak the screenshooter to use eyewitness as suggested by daniruiz
diff --git a/app/ApplicationInfo.py b/app/ApplicationInfo.py
index cec3597b..c4eb6a6a 100644
--- a/app/ApplicationInfo.py
+++ b/app/ApplicationInfo.py
@@ -18,8 +18,8 @@
 
 applicationInfo = {
     "name": "LEGION",
-    "version": "0.4.2",
-    "build": '1700525714',
+    "version": "0.4.3",
+    "build": '1700529501',
     "author": "Gotham Security",
     "copyright": "2023",
     "links": ["http://github.com/GoVanguard/legion/issues", "https://gotham-security.com/legion"],
diff --git a/buildHugeNmapTest.py b/buildHugeNmapTest.py
index d0928b4c..feaf03fe 100644
--- a/buildHugeNmapTest.py
+++ b/buildHugeNmapTest.py
@@ -1,5 +1,5 @@
 import xml.etree.ElementTree as ET
-from random import randint, choice, sample
+from random import randint, choice, choices, sample
 import datetime
 
 def generate_nmap_xml(num_hosts=1000, base_subnet="172.16"):
@@ -62,20 +62,49 @@ def generate_nmap_xml(num_hosts=1000, base_subnet="172.16"):
         "afp": {"product": "Netatalk AFP", "version": "3.1.12"}
     }
 
+    # Expanded lists for generating hostnames
+    colors = ["Red", "Blue", "Green", "Yellow", "Purple", "Orange", "Cyan", "Magenta", "Lime", "Pink"]
+    foods = ["Apple", "Burger", "Cake", "Dumpling", "Eclair", "Pizza", "Sushi", "Taco", "Waffle", "Bagel"]
+    cities = ["Tokyo", "Paris", "London", "NewYork", "Sydney", "Berlin", "Rome", "Madrid", "Moscow", "Beijing"]
+    verbs = ["Jumping", "Running", "Flying", "Swimming", "Dancing", "Singing", "Playing", "Walking", "Reading", "Writing"]
+
+    # Unique hostname tracker
+    generated_hostnames = set()
+
+    # Function to create unique random hostnames
+    def generate_hostname():
+        while True:
+            parts = [choice(colors), choice(foods), choice(cities), choice(verbs)]
+            hostname = '.'.join(parts)
+            # Ensure uniqueness by appending a number if needed
+            if hostname not in generated_hostnames:
+                generated_hostnames.add(hostname)
+                return hostname
+            else:
+                hostname += str(randint(0, 9999))
+                if hostname not in generated_hostnames:
+                    generated_hostnames.add(hostname)
+                    return hostname
+
     # Function to create a random IP address within the extended subnet range
     def random_ip(base_subnet, host_number):
         subnet_third_octet = host_number // 254
         host_fourth_octet = host_number % 254 + 1
         return f"{base_subnet}.{subnet_third_octet}.{host_fourth_octet}"
 
-    # Generating hosts with updated IP address method
+    # Generating hosts with updated IP address and hostname method
     for i in range(num_hosts):
         host_os = choice(list(os_services.keys()))
 
         host = ET.Element("host")
         ET.SubElement(host, "status", {"state": "up", "reason": "arp-response", "reason_ttl": "0"})
         ET.SubElement(host, "address", {"addr": random_ip(base_subnet, i), "addrtype": "ipv4"})
-        ET.SubElement(host, "hostnames")
+
+        # Hostnames
+        hostnames = ET.SubElement(host, "hostnames")
+        num_hostnames = randint(1, 3)  # Random number of hostnames per host
+        for _ in range(num_hostnames):
+            ET.SubElement(hostnames, "hostname", {"name": generate_hostname(), "type": "user"})
 
         # Ports
         ports = ET.SubElement(host, "ports")
@@ -135,7 +164,7 @@ def random_ip(base_subnet, host_number):
     xml_str = xml_header + '\n' + ET.tostring(nmaprun, encoding='unicode', method='xml')
     return xml_str
 
-def save_nmap_xml(filename, num_hosts=1000, base_subnet="172.16"):
+def save_nmap_xml(filename, num_hosts=200, base_subnet="172.16"):
     # Generate the XML content
     xml_content = generate_nmap_xml(num_hosts, base_subnet)
 
diff --git a/controller/controller.py b/controller/controller.py
index 453f7011..0b33214d 100644
--- a/controller/controller.py
+++ b/controller/controller.py
@@ -236,6 +236,9 @@ def closeProject(self):
         self.view.updateProcessesTableView()                            # clear process table
         self.logic.projectManager.closeProject(self.logic.activeProject)
 
+    def copyToClipboard(self, data):
+        clipboard = QtWidgets.QApplication.clipboard()
+        clipboard.setText(data)  # Assuming item.text() contains the IP or hostname
 
     @timing
     def addHosts(self, targetHosts, runHostDiscovery, runStagedNmap, nmapSpeed, scanMode, nmapOptions = []):
diff --git a/debian/changelog b/debian/changelog
index 85075016..1aeb8142 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -32,3 +32,12 @@ legion (0.4.2-0) UNRELEASED; urgency=medium
   * Fix typo in startLegion.sh
 
  -- Shane Scott <sscot@gotham-security.com>  Mon, 20 Nov 2023 12:50:55 -0600
+
+legion (0.4.3-0) UNRELEASED; urgency=medium
+
+  * Revise NMAP import process
+  * Fix import progress calculations
+  * Doubleckick to copy hostname (Linux only)
+  * Script to generate huge bogus NMAP XML imports for testing.
+
+ -- Shane Scott <sscott@gotham-security.com> Mon, 20 Nov 2023 19:17:00 -0600
diff --git a/debian/control b/debian/control
index 304a99fd..30150457 100644
--- a/debian/control
+++ b/debian/control
@@ -4,7 +4,7 @@ Priority: optional
 Maintainer: GoVanguard <hello@gotham-security.com>
 Uploaders: Shane Scott <sscott@gotham-security.com>
 Build-Depends: debhelper, python3, python3-requests
-Standards-Version: 0.4.2
+Standards-Version: 0.4.3
 Homepage: https://github.com/GoVanguard/Legion
 
 Package: legion
diff --git a/ui/view.py b/ui/view.py
index 35c58eb3..ccfa3075 100644
--- a/ui/view.py
+++ b/ui/view.py
@@ -192,6 +192,7 @@ def initTables(self):  # this function prepares the default settings for each ta
         self.HostsTableModel.sort(3, Qt.SortOrder.DescendingOrder)
         # Connect the clicked signal of the HostsTableView to the hostTableClick() method
         self.ui.HostsTableView.clicked.connect(self.hostTableClick)
+        self.ui.HostsTableView.doubleClicked.connect(self.hostTableDoubleClick)
 
         ##
 
@@ -580,6 +581,15 @@ def hostTableClick(self):
     
     def connectServiceNamesTableClick(self):
         self.ui.ServiceNamesTableView.clicked.connect(self.serviceNamesTableClick)
+
+    def hostTableDoubleClick(self, index):
+        # Get the item from the model using the index
+        model = self.ui.HostsTableView.model()
+        row = index.row()
+        new_index = model.index(row, 3)
+        data = model.data(new_index, QtCore.Qt.ItemDataRole.DisplayRole)
+        if data:
+            self.controller.copyToClipboard(data)
         
     def serviceNamesTableClick(self):
         if self.ui.ServiceNamesTableView.selectionModel().selectedRows():