Drafted with AI assistance. Every command below was run on 9 November 2026 against a pair of sample Nessus CSV exports in the default column layout before publishing.
Last month's scan had 9 findings and this month's has 5. Does that mean 4 were fixed? Not necessarily. Here's how to compare two Nessus CSV exports line by line with grep, cut, sort, comm and awk, then check whether the findings that went away were really fixed. Two optional steps use Python 3.
1. Turn each export into one line per finding
A finding is the same finding in both scans when the host, the Nessus plugin ID and the port match. In a default Nessus CSV, Plugin ID is column 1, Risk is 4, Host is 5, Protocol is 6 and Port is 7. This skips plugins with a Risk of None (informational):
grep -E '^"[0-9]+",' october.csv | cut -d, -f1,4,5,6,7 | tr -d '"' | awk -F, '$2 != "None" {print $3","$1","$4"/"$5}' | LC_ALL=C sort -u > october.txt
grep -E '^"[0-9]+",' november.csv | cut -d, -f1,4,5,6,7 | tr -d '"' | awk -F, '$2 != "None" {print $3","$1","$4"/"$5}' | LC_ALL=C sort -u > november.txt
wc -l october.txt november.txt
Nessus puts long descriptions and plugin output on several lines inside quotes. The grep keeps only lines that start a new row with a quoted plugin ID, so those extra lines are dropped. cut is safe because the first seven columns never contain commas. A line looks like 10.0.0.5,104743,tcp/443. Our October sample had 9 findings and November had 5.
2. Split them into fixed, new and still open
LC_ALL=C comm -23 october.txt november.txt > fixed.txt # gone (looks fixed)
LC_ALL=C comm -13 october.txt november.txt > new.txt # new
LC_ALL=C comm -12 october.txt november.txt > open.txt # still open
wc -l fixed.txt new.txt open.txt
Our sample gave 5 gone, 1 new (SSH Terrapin, CVE-2023-48795, on 10.0.0.12) and 4 still open. LC_ALL=C keeps sort and comm in the same order.
3. Check for hosts that weren't scanned
If a host was switched off or left out of the scan range, every finding on it disappears. That isn't a fix.
grep -E '^"[0-9]+",' october.csv | cut -d, -f5 | tr -d '"' | LC_ALL=C sort -u > october-hosts.txt
grep -E '^"[0-9]+",' november.csv | cut -d, -f5 | tr -d '"' | LC_ALL=C sort -u > november-hosts.txt
LC_ALL=C comm -23 october-hosts.txt november-hosts.txt > gone-hosts.txt
awk -F, 'NR==FNR {gone[$1]; next} !($1 in gone)' gone-hosts.txt fixed.txt > fixed-scanned.txt
In our sample, 10.0.0.20 dropped out, so its two findings (TLS 1.0 and a self-signed certificate) only looked fixed. That left 3 in fixed-scanned.txt.
4. Check that both scans logged in
A credentialed scan logs in and checks installed packages. If the login fails next time, those local checks don't run and their findings vanish. Nessus records the result per host in plugin 19506 (Nessus Scan Information) as Credentialed checks : yes or no:
python3 -c 'import csv,sys; [print(r["Host"], l.split(":", 1)[1].strip()) for r in csv.DictReader(open(sys.argv[1], newline="")) if r["Plugin ID"] == "19506" for l in r["Plugin Output"].splitlines() if l.startswith("Credentialed checks")]' november.csv
Run it on both files. In our sample, 10.0.0.12 logged in during October but printed no in November, so its OpenSSH package finding went missing because the login failed, not because OpenSSH was updated.
5. Put names on the results
python3 -c 'import csv,sys; keys = {tuple(l.strip().split(",")) for l in open(sys.argv[2])}; rows = {(r["Host"], r["Plugin ID"], r["Protocol"] + "/" + r["Port"]): r for r in csv.DictReader(open(sys.argv[1], newline=""))}; [print(rows[k]["Risk"], k[0], rows[k]["Name"], sep="\t") for k in sorted(keys) if k in rows]' october.csv fixed-scanned.txt
Use the older export for fixed findings and the newer one (with new.txt) for new findings. Setting aside the OpenSSH finding from step 4, two findings in our sample were really fixed: MS17-010 (EternalBlue) on 10.0.0.10 and TLS 1.0 on 10.0.0.5. Five findings disappeared, but only two were fixed.
One more trap: Tenable sometimes retires a plugin and replaces it with a new one. The old ID then shows as fixed and the new ID as new, on the same host and port. If a fixed and a new finding share a host and port, read both before reporting either.
Putting it to use
- Compare scans from the same scanner with the same policy. Nessus and Qualys name and check issues differently, so their lists won't line up.
- Keep the dated lists with both exports. A list of what was fixed, and the scan that confirmed it, is useful evidence for audits and client reviews.
- Run steps 3 and 4 every time. They catch the two most common reasons a finding looks fixed when it isn't.
- If you changed the Nessus CSV columns, check
head -1 october.csvand change the numbers. The Python steps read columns by name.
The original version of this guide lives on varaxontech.com. To see which of the new and still-open findings are being exploited, see how to check your scan results against CISA KEV. We built Varaxon Scan Hub to do this comparison on every report, without counting a finding as fixed when the host wasn't rescanned or the login failed.
Top comments (0)