**Approach (brief)** Parse Nmap XML and Nessus CSV with only stdlib, normalize IPs, merge by IP, produce CSV with ip, hostname, open-ports, vuln-count, highest-severity. Focus on robust parsing, handling IPv6, missing hostnames, duplicate ports, and severity mapping.**Code (Python 3)**python
#!/usr/bin/env python3
import sys, csv, xml.etree.ElementTree as ET
from collections import defaultdict
SEV_ORDER = {'critical':4,'high':3,'medium':2,'low':1,'info':0}
def norm_ip(ip):
return ip.strip()
def parse_nmap(xml_path):
hosts = {}
tree = ET.parse(xml_path)
for h in tree.findall('.//host'):
addr = None
hostname = ''
ports = set()
for a in h.findall('address'):
if a.get('addrtype') in ('ipv4','ipv6'):
addr = norm_ip(a.get('addr'))
break
hn = h.find('.//hostname')
if hn is not None:
hostname = hn.get('name','')
for p in h.findall('.//port'):
state = p.find('state')
if state is not None and state.get('state') == 'open':
ports.add(p.get('portid'))
if addr:
hosts[addr] = {'hostname':hostname, 'ports':sorted(ports, key=int)}
return hosts
def parse_nessus(csv_path):
vulns = defaultdict(lambda: {'count':0,'highest':'info'})
with open(csv_path, newline='', encoding='utf-8', errors='replace') as f:
reader = csv.DictReader(f)
for row in reader:
ip = row.get('Host','').strip()
sev = row.get('Severity','').strip().lower()
if not ip: continue
vulns[ip]['count'] += 1
if SEV_ORDER.get(sev,0) > SEV_ORDER.get(vulns[ip]['highest'],0):
vulns[ip]['highest'] = sev or vulns[ip]['highest']
return vulns
def merge(nmap_h, nessus_v):
all_ips = set(nmap_h) | set(nessus_v)
rows = []
for ip in sorted(all_ips):
hn = nmap_h.get(ip,{}).get('hostname','')
ports = ','.join(nmap_h.get(ip,{}).get('ports',[]))
cnt = nessus_v.get(ip,{}).get('count',0)
high = nessus_v.get(ip,{}).get('highest','info')
rows.append((ip, hn, ports, str(cnt), high))
return rows
def write_csv(rows, out_path):
with open(out_path,'w',newline='') as f:
w = csv.writer(f)
w.writerow(['ip','hostname','open-ports','vuln-count','highest-severity'])
w.writerows(rows)
if __name__ == '__main__':
if len(sys.argv)!=4:
print("Usage: merge.py nmap.xml nessus.csv out.csv"); sys.exit(2)
nmap_h = parse_nmap(sys.argv[1])
nessus_v = parse_nessus(sys.argv[2])
write_csv(merge(nmap_h, nessus_v), sys.argv[3])
**Explanation & edge cases**- XML parsed with ElementTree; tolerant to missing nodes.- IP normalization minimal (strip); extend for CIDR or IPv6 canonicalization if needed.- Nessus CSV relies on headers like "Host" and "Severity"; map severity to order to get highest.- Handles hosts present in one source only, duplicate ports, and malformed rows.**Complexity**- O(N+M) time where N ports/hosts in Nmap, M rows in Nessus. Memory linear in distinct IPs.As a penetration tester, this gives a quick prioritized host list for triage and follow-up exploitation.