Fix rescan not persisting results when no vulnerabilities found

When CorrelateAsync returned null (zero findings after correct version
filtering), the rescan code path fell through to returning the OLD
stored report instead of persisting the new result. Now properly
handles the null case by deleting the stale report and returning
a clean response.

Added DeleteVulnerabilityReportAsync to the repository interface
and implementations.

https://claude.ai/code/session_01Kv2aAEVqbeFSsyPv7gU7pF
This commit is contained in:
Claude
2026-03-28 22:21:47 +00:00
parent 9a7c2aa4b6
commit 53afa30068
4 changed files with 37 additions and 0 deletions
@@ -39,6 +39,11 @@ namespace AutopilotMonitor.Functions.DataAccess.TableStorage
public Task<string?> GetVulnerabilityReportJsonAsync(string tenantId, string sessionId)
=> _storage.GetVulnerabilityReportJsonAsync(tenantId, sessionId);
public async Task DeleteVulnerabilityReportAsync(string tenantId, string sessionId)
{
await _storage.DeleteVulnerabilityReportAsync(tenantId, sessionId);
}
// --- Software Inventory ---
public Task UpsertSoftwareInventoryAsync(string tenantId, List<Dictionary<string, object>> inventoryItems,
@@ -96,6 +96,15 @@ namespace AutopilotMonitor.Functions.Functions.Vulnerability
await rescanResponse.WriteAsJsonAsync(new { success = true, sessionId, report = freshReport, rescanned = true });
return rescanResponse;
}
// Rescan found no vulnerabilities — clear the stale report so the
// UI no longer shows outdated findings from a previous scan.
await _vulnRepo.DeleteVulnerabilityReportAsync(effectiveTenantId, sessionId);
_logger.LogInformation("Vulnerability re-scan complete for session {SessionId}: no findings, cleared old report", sessionId);
var noFindingsResponse = req.CreateResponse(HttpStatusCode.OK);
await noFindingsResponse.WriteAsJsonAsync(new { success = true, sessionId, report = (object?)null, rescanned = true, message = "Re-scan complete: no vulnerabilities found" });
return noFindingsResponse;
}
catch (Exception ex)
{
@@ -553,6 +553,28 @@ namespace AutopilotMonitor.Functions.Services
scanSummary?.GetValueOrDefault("overall_risk")?.ToString() ?? "none");
}
/// <summary>
/// Deletes the stored vulnerability report for a session.
/// Used when a re-scan finds no vulnerabilities, to clear stale data.
/// </summary>
public async Task DeleteVulnerabilityReportAsync(string tenantId, string sessionId)
{
SecurityValidator.EnsureValidGuid(tenantId, nameof(tenantId));
SecurityValidator.EnsureValidGuid(sessionId, nameof(sessionId));
try
{
var tableClient = _tableServiceClient.GetTableClient(Constants.TableNames.VulnerabilityReports);
var partitionKey = $"{tenantId}_{sessionId}";
await tableClient.DeleteEntityAsync(partitionKey, "report");
_logger.LogInformation("Deleted vulnerability report for session {SessionId}", sessionId);
}
catch (Azure.RequestFailedException ex) when (ex.Status == 404)
{
// Already deleted or never existed — nothing to do
}
}
/// <summary>
/// Gets the vulnerability correlation report for a session.
/// Returns the deserialized report data, or null if no report exists.
@@ -18,6 +18,7 @@ namespace AutopilotMonitor.Shared.DataAccess
/// to avoid Newtonsoft JToken serialization issues.
/// </summary>
Task<string?> GetVulnerabilityReportJsonAsync(string tenantId, string sessionId);
Task DeleteVulnerabilityReportAsync(string tenantId, string sessionId);
// --- Software Inventory ---
Task UpsertSoftwareInventoryAsync(string tenantId, List<Dictionary<string, object>> inventoryItems,