Skip to content

Commit 9d98785

Browse files
Fix IP scanner export NullReferenceException and CSV format bug (#3290)
* Initial plan * Fix IP scanner export issues - add null checks and fix CSV comma bug Co-authored-by: BornToBeRoot <[email protected]> * Address code review - use empty enumerable instead of null for XML ports Co-authored-by: BornToBeRoot <[email protected]> * Optimize JSON export - use Array.Empty for null/empty ports Co-authored-by: BornToBeRoot <[email protected]> * Docs: #3290 --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: BornToBeRoot <[email protected]>
1 parent d139902 commit 9d98785

File tree

2 files changed

+82
-60
lines changed

2 files changed

+82
-60
lines changed

Source/NETworkManager.Models/Export/ExportManager.IPScannerHostInfo.cs

Lines changed: 80 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ private static void CreateCsv(IEnumerable<IPScannerHostInfo> collection, string
5555
$"{nameof(PingInfo.Timestamp)}," +
5656
$"{nameof(PingInfo.Time)}," +
5757
$"{nameof(PingInfo.TTL)}," +
58-
$"{nameof(PingInfo.Bytes)}" +
58+
$"{nameof(PingInfo.Bytes)}," +
5959
$"PortStatus," +
6060
$"{nameof(IPScannerHostInfo.Ports)}," +
6161
$"NetBIOSIsReachable," +
@@ -73,11 +73,18 @@ private static void CreateCsv(IEnumerable<IPScannerHostInfo> collection, string
7373

7474
foreach (var info in collection)
7575
{
76+
// Skip if critical data is null
77+
if (info?.PingInfo == null)
78+
continue;
79+
7680
var stringBuilderPorts = new StringBuilder();
7781

78-
foreach (var port in info.Ports)
79-
stringBuilderPorts.Append(
80-
$"{port.Port}/{port.LookupInfo.Protocol}/{port.LookupInfo.Service}/{port.LookupInfo.Description}/{port.State};");
82+
if (info.Ports != null)
83+
{
84+
foreach (var port in info.Ports)
85+
stringBuilderPorts.Append(
86+
$"{port.Port}/{port.LookupInfo.Protocol}/{port.LookupInfo.Service}/{port.LookupInfo.Description}/{port.State};");
87+
}
8188

8289
stringBuilder.AppendLine(
8390
$"{info.IsReachable}," +
@@ -87,16 +94,16 @@ private static void CreateCsv(IEnumerable<IPScannerHostInfo> collection, string
8794
$"{DateTimeHelper.DateTimeToFullDateTimeString(info.PingInfo.Timestamp)}," +
8895
$"{Ping.TimeToString(info.PingInfo.Status, info.PingInfo.Time, true)}," +
8996
$"{info.PingInfo.TTL}," +
90-
$"{info.PingInfo.Bytes}" +
97+
$"{info.PingInfo.Bytes}," +
9198
$"{(info.IsAnyPortOpen ? PortState.Open : PortState.Closed)}," +
9299
$"\"{stringBuilderPorts.ToString().TrimEnd(';')}\"," +
93-
$"{info.NetBIOSInfo.IsReachable}," +
94-
$"{info.NetBIOSInfo.IPAddress}," +
95-
$"{info.NetBIOSInfo.ComputerName}," +
96-
$"{info.NetBIOSInfo.UserName}," +
97-
$"{info.NetBIOSInfo.GroupName}," +
98-
$"{info.NetBIOSInfo.MACAddress}," +
99-
$"{info.NetBIOSInfo.Vendor}," +
100+
$"{info.NetBIOSInfo?.IsReachable}," +
101+
$"{info.NetBIOSInfo?.IPAddress}," +
102+
$"{info.NetBIOSInfo?.ComputerName}," +
103+
$"{info.NetBIOSInfo?.UserName}," +
104+
$"{info.NetBIOSInfo?.GroupName}," +
105+
$"{info.NetBIOSInfo?.MACAddress}," +
106+
$"{info.NetBIOSInfo?.Vendor}," +
100107
$"{info.MACAddress}," +
101108
$"\"{info.Vendor}\"," +
102109
$"{info.ARPMACAddress}," +
@@ -118,6 +125,7 @@ private static void CreateXml(IEnumerable<IPScannerHostInfo> collection, string
118125
new XElement(ApplicationName.IPScanner.ToString(),
119126
new XElement(nameof(IPScannerHostInfo) + "s",
120127
from info in collection
128+
where info?.PingInfo != null
121129
select
122130
new XElement(nameof(IPScannerHostInfo),
123131
new XElement(nameof(IPScannerHostInfo.IsReachable), info.IsReachable),
@@ -131,20 +139,22 @@ from info in collection
131139
new XElement(nameof(PingInfo.TTL), info.PingInfo.TTL),
132140
new XElement(nameof(PingInfo.Bytes), info.PingInfo.Bytes),
133141
new XElement("PortStatus", info.IsAnyPortOpen ? PortState.Open : PortState.Closed),
134-
from port in info.Ports
135-
select new XElement(nameof(PortInfo),
136-
new XElement(nameof(PortInfo.Port), port.Port),
137-
new XElement(nameof(PortInfo.LookupInfo.Protocol), port.LookupInfo.Protocol),
138-
new XElement(nameof(PortInfo.LookupInfo.Service), port.LookupInfo.Service),
139-
new XElement(nameof(PortInfo.LookupInfo.Description), port.LookupInfo.Description),
140-
new XElement(nameof(PortInfo.State), port.State)),
141-
new XElement("NetBIOSIsReachable", info.NetBIOSInfo.IsReachable),
142-
new XElement("NetBIOSIPAddress", info.NetBIOSInfo.IPAddress),
143-
new XElement("NetBIOSComputerName", info.NetBIOSInfo.ComputerName),
144-
new XElement("NetBIOSUserName", info.NetBIOSInfo.UserName),
145-
new XElement("NetBIOSGroupName", info.NetBIOSInfo.GroupName),
146-
new XElement("NetBIOSMACAddress", info.NetBIOSInfo.MACAddress),
147-
new XElement("NetBIOSVendor", info.NetBIOSInfo.Vendor),
142+
info.Ports != null
143+
? from port in info.Ports
144+
select new XElement(nameof(PortInfo),
145+
new XElement(nameof(PortInfo.Port), port.Port),
146+
new XElement(nameof(PortInfo.LookupInfo.Protocol), port.LookupInfo.Protocol),
147+
new XElement(nameof(PortInfo.LookupInfo.Service), port.LookupInfo.Service),
148+
new XElement(nameof(PortInfo.LookupInfo.Description), port.LookupInfo.Description),
149+
new XElement(nameof(PortInfo.State), port.State))
150+
: Enumerable.Empty<XElement>(),
151+
new XElement("NetBIOSIsReachable", info.NetBIOSInfo?.IsReachable),
152+
new XElement("NetBIOSIPAddress", info.NetBIOSInfo?.IPAddress),
153+
new XElement("NetBIOSComputerName", info.NetBIOSInfo?.ComputerName),
154+
new XElement("NetBIOSUserName", info.NetBIOSInfo?.UserName),
155+
new XElement("NetBIOSGroupName", info.NetBIOSInfo?.GroupName),
156+
new XElement("NetBIOSMACAddress", info.NetBIOSInfo?.MACAddress),
157+
new XElement("NetBIOSVendor", info.NetBIOSInfo?.Vendor),
148158
new XElement(nameof(IPScannerHostInfo.MACAddress), info.MACAddress),
149159
new XElement(nameof(IPScannerHostInfo.Vendor), info.Vendor),
150160
new XElement(nameof(IPScannerHostInfo.ARPMACAddress), info.ARPMACAddress),
@@ -164,46 +174,56 @@ from port in info.Ports
164174
/// <param name="filePath">Path to the export file.</param>
165175
private static void CreateJson(IReadOnlyList<IPScannerHostInfo> collection, string filePath)
166176
{
167-
var jsonData = new object[collection.Count];
177+
var validCollection = collection.Where(info => info?.PingInfo != null).ToList();
178+
var jsonData = new object[validCollection.Count];
168179

169-
for (var i = 0; i < collection.Count; i++)
180+
for (var i = 0; i < validCollection.Count; i++)
170181
{
171-
var jsonDataPorts = new object[collection[i].Ports.Count];
172-
173-
for (var j = 0; j < collection[i].Ports.Count; j++)
174-
jsonDataPorts[j] = new
175-
{
176-
collection[i].Ports[j].Port,
177-
Protocol = collection[i].Ports[j].LookupInfo.Protocol.ToString(),
178-
collection[i].Ports[j].LookupInfo.Service,
179-
collection[i].Ports[j].LookupInfo.Description,
180-
State = collection[i].Ports[j].State.ToString()
181-
};
182+
var info = validCollection[i];
183+
object[] jsonDataPorts;
184+
185+
if (info.Ports != null && info.Ports.Count > 0)
186+
{
187+
jsonDataPorts = new object[info.Ports.Count];
188+
for (var j = 0; j < info.Ports.Count; j++)
189+
jsonDataPorts[j] = new
190+
{
191+
info.Ports[j].Port,
192+
Protocol = info.Ports[j].LookupInfo.Protocol.ToString(),
193+
info.Ports[j].LookupInfo.Service,
194+
info.Ports[j].LookupInfo.Description,
195+
State = info.Ports[j].State.ToString()
196+
};
197+
}
198+
else
199+
{
200+
jsonDataPorts = Array.Empty<object>();
201+
}
182202

183203
jsonData[i] = new
184204
{
185-
collection[i].IsReachable,
186-
IPAddress = collection[i].PingInfo.IPAddress.ToString(),
187-
collection[i].Hostname,
188-
PingStatus = collection[i].PingInfo.Status.ToString(),
189-
Timestamp = DateTimeHelper.DateTimeToFullDateTimeString(collection[i].PingInfo.Timestamp),
190-
Time = Ping.TimeToString(collection[i].PingInfo.Status, collection[i].PingInfo.Time, true),
191-
collection[i].PingInfo.TTL,
192-
collection[i].PingInfo.Bytes,
193-
collection[i].DNSHostname,
194-
PortStatus = collection[i].IsAnyPortOpen ? PortState.Open.ToString() : PortState.Closed.ToString(),
205+
info.IsReachable,
206+
IPAddress = info.PingInfo.IPAddress.ToString(),
207+
info.Hostname,
208+
PingStatus = info.PingInfo.Status.ToString(),
209+
Timestamp = DateTimeHelper.DateTimeToFullDateTimeString(info.PingInfo.Timestamp),
210+
Time = Ping.TimeToString(info.PingInfo.Status, info.PingInfo.Time, true),
211+
info.PingInfo.TTL,
212+
info.PingInfo.Bytes,
213+
info.DNSHostname,
214+
PortStatus = info.IsAnyPortOpen ? PortState.Open.ToString() : PortState.Closed.ToString(),
195215
Ports = jsonDataPorts,
196-
NetBIOSIsReachable = collection[i].NetBIOSInfo.IsReachable,
197-
NetBIOSIPAddress = collection[i].NetBIOSInfo.IPAddress?.ToString(),
198-
NetBIOSComputerName = collection[i].NetBIOSInfo.ComputerName,
199-
NetBIOSUserName = collection[i].NetBIOSInfo.UserName,
200-
NetBIOSGroupName = collection[i].NetBIOSInfo.GroupName,
201-
NetBIOSMACAddress = collection[i].NetBIOSInfo.MACAddress,
202-
NetBIOSVendor = collection[i].NetBIOSInfo.Vendor,
203-
collection[i].MACAddress,
204-
collection[i].Vendor,
205-
collection[i].ARPMACAddress,
206-
collection[i].ARPVendor
216+
NetBIOSIsReachable = info.NetBIOSInfo?.IsReachable,
217+
NetBIOSIPAddress = info.NetBIOSInfo?.IPAddress?.ToString(),
218+
NetBIOSComputerName = info.NetBIOSInfo?.ComputerName,
219+
NetBIOSUserName = info.NetBIOSInfo?.UserName,
220+
NetBIOSGroupName = info.NetBIOSInfo?.GroupName,
221+
NetBIOSMACAddress = info.NetBIOSInfo?.MACAddress,
222+
NetBIOSVendor = info.NetBIOSInfo?.Vendor,
223+
info.MACAddress,
224+
info.Vendor,
225+
info.ARPMACAddress,
226+
info.ARPVendor
207227
};
208228
}
209229

Website/docs/changelog/next-release.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ Release date: **xx.xx.2025**
9191
**IP Scanner**
9292

9393
- Fix race condition when scan is complete but not all results have been processed yet, causing a wrong error message to be displayed. [#3287](https://github.com/BornToBeRoot/NETworkManager/pull/3287)
94+
- Fix potential error in export logic if some data is null. [#3290](https://github.com/BornToBeRoot/NETworkManager/pull/3290)
95+
- Fix missing simicolon separators in CSV output. [#3290](https://github.com/BornToBeRoot/NETworkManager/pull/3290)
9496

9597
**Port Scanner**
9698

0 commit comments

Comments
 (0)