-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscan_diff.py
More file actions
162 lines (127 loc) · 6.88 KB
/
Copy pathscan_diff.py
File metadata and controls
162 lines (127 loc) · 6.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
import json
from datetime import datetime
from collections import defaultdict
def load_scan_data(filepath):
"""Load scan results from a JSON file."""
with open(filepath, 'r', encoding='utf-8') as f:
return json.load(f)
def find_port_changes(current_file="results.json", history_file="scan_history.json"):
"""Compare current scan against historical scans and report delta."""
print(f"[*] Loading current scan from {current_file}...")
current = load_scan_data(current_file)
if not current:
print("[!] Current scan is empty. No comparison possible.")
return
# Load history file (always exists — we append every scan)
try:
with open(history_file, 'r', encoding='utf-8') as f:
history = json.load(f)
if not history:
print("[!] Scan history is empty. Saving first record.")
history.append({"timestamp": datetime.now().isoformat(), "devices": current})
save_history(history, history_file)
return
previous = history[-1]["devices"] # Most recent scan
timestamp = history[-1]["timestamp"]
except FileNotFoundError:
print("[!] No scan history found. Saving first record.")
history.append({"timestamp": datetime.now().isoformat(), "devices": current})
save_history(history, history_file)
return
# Build lookup maps by IP
current_map = {d['ip']: d for d in current}
previous_map = {d['ip']: d for d in previous}
current_ips = set(current_map.keys())
previous_ips = set(previous_map.keys())
new_devices = current_ips - previous_ips
removed_devices = previous_ips - current_ips
common_ips = current_ips & previous_ips
# Track port changes within common devices
new_ports_by_ip = defaultdict(list) # Port appeared since last scan
closed_ports_by_ip = defaultdict(list) # Port disappeared since last scan
risk_changes = {} # IP -> old_risk -> new_risk
for ip in common_ips:
curr_device = current_map[ip]
prev_device = previous_map[ip]
curr_ports = {(p['port'], p['service']) for p in curr_device.get('open_ports', [])}
prev_ports = {(p['port'], p['service']) for p in prev_device.get('open_ports', [])}
if curr_ports != prev_ports:
new = curr_ports - prev_ports
closed = prev_ports - curr_ports
if new:
for port, service in new:
new_ports_by_ip[ip].append({"port": port, "service": service})
if closed:
for port, service in closed:
closed_ports_by_ip[ip].append({"port": port, "service": service})
# Track risk level changes
prev_risk = max((p.get('risk_level', 'safe') for p in prev_device.get('open_ports', [])), default='safe')
curr_risk = max((p.get('risk_level', 'safe') for p in curr_device.get('open_ports', [])), default='safe')
if prev_risk != curr_risk:
risk_changes[ip] = {"from": prev_risk, "to": curr_risk}
# Build change summary
changes = []
severity_count = {"critical": 0, "warning": 0, "info": 0}
for ip in new_devices:
dev = current_map[ip]
ports_str = ", ".join(f"{p['port']}/{p['service']}" for p in dev.get('open_ports', []))
changes.append({"type": "new_device", "ip": ip, "hostname": dev.get('hostname', 'Unknown'), "ports": ports_str})
for ip in removed_devices:
changes.append({"type": "device_removed", "ip": ip, "hostname": previous_map[ip].get('hostname', 'Unknown')})
for ip, port_list in new_ports_by_ip.items():
ports_str = ", ".join(f"{p['port']}/{p['service']}" for p in port_list)
risk = "critical" if any(p['port'] in {21, 23, 445, 3389, 1433, 3306} for p in port_list) else "warning"
changes.append({"type": "new_ports", "ip": ip, "ports": ports_str, "severity": risk})
for ip, port_list in closed_ports_by_ip.items():
ports_str = ", ".join(f"{p['port']}/{p['service']}" for p in port_list)
changes.append({"type": "closed_ports", "ip": ip, "ports": ports_str, "severity": "info"})
for ip, risk_map in risk_changes.items():
changes.append({"type": "risk_change", "ip": ip, "from": risk_map['from'], "to": risk_map['to']})
severity_count["warning"] += 1 if risk_map['to'] == 'critical' else 1
# Determine overall severity
has_critical = any(
c.get('severity') in ('critical', 'warning') or
c.get('type') in ('new_device', 'risk_change')
for c in changes
)
# Print results
print("\n" + "=" * 60)
print(" SCAN CHANGE REPORT")
print("=" * 60)
print(f"\nLast scan: {timestamp}")
print(f"Current devices: {len(current)} | Previous: {len(previous)}")
print(f"New devices: {len(new_devices)} | Removed: {len(removed_devices)}")
if changes:
# Sort: new devices first, then new ports, then risk changes, then removed
type_order = {"new_device": 0, "new_ports": 1, "risk_change": 2, "closed_ports": 3, "device_removed": 4}
changes.sort(key=lambda c: type_order.get(c['type'], 5))
print(f"\n[!] {len(changes)} change(s) detected:\n")
for i, change in enumerate(changes, 1):
change_type = change['type'].replace('_', ' ').title()
if change['type'] == 'new_device':
print(f" {i}. [NEW DEVICE] {change['ip']} ({change.get('hostname', 'Unknown')})")
if change.get('ports'):
print(f" Open ports: {change['ports']}")
elif change['type'] == 'new_ports':
sev = change.get('severity', '?').upper()
print(f" {i}. [NEW PORTS] {change['ip']} ({sev})")
print(f" {change['ports']}")
elif change['type'] == 'risk_change':
print(f" {i}. [RISK CHANGE] {change['ip']}: {change['from'].upper()} -> {change['to'].upper()}")
elif change['type'] == 'closed_ports':
print(f" {i}. [CLOSED PORTS] {change['ip']}")
print(f" {change['ports']}")
elif change['type'] == 'device_removed':
print(f" {i}. [DEVICE REMOVED] {change['ip']} ({change.get('hostname', 'Unknown')})")
else:
print("\n[+] No changes detected. Network unchanged since last scan.")
# Save current scan to history
history.append({"timestamp": datetime.now().isoformat(), "devices": current})
save_history(history, history_file)
print(f"\n[*] Current scan saved to {history_file}")
return changes
def save_history(history, filepath="scan_history.json"):
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(history, f, indent=4)
if __name__ == "__main__":
find_port_changes()