From 53afa30068609c4a11200a5fa3cfdd95416f16b3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 28 Mar 2026 22:21:47 +0000 Subject: [PATCH] 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 --- .../TableVulnerabilityRepository.cs | 5 +++++ .../GetVulnerabilityReportFunction.cs | 9 ++++++++ .../Services/TableStorageService.Rules.cs | 22 +++++++++++++++++++ .../DataAccess/IVulnerabilityRepository.cs | 1 + 4 files changed, 37 insertions(+) diff --git a/src/Backend/AutopilotMonitor.Functions/DataAccess/TableStorage/TableVulnerabilityRepository.cs b/src/Backend/AutopilotMonitor.Functions/DataAccess/TableStorage/TableVulnerabilityRepository.cs index f88141c1..94aa0f59 100644 --- a/src/Backend/AutopilotMonitor.Functions/DataAccess/TableStorage/TableVulnerabilityRepository.cs +++ b/src/Backend/AutopilotMonitor.Functions/DataAccess/TableStorage/TableVulnerabilityRepository.cs @@ -39,6 +39,11 @@ namespace AutopilotMonitor.Functions.DataAccess.TableStorage public Task 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> inventoryItems, diff --git a/src/Backend/AutopilotMonitor.Functions/Functions/Vulnerability/GetVulnerabilityReportFunction.cs b/src/Backend/AutopilotMonitor.Functions/Functions/Vulnerability/GetVulnerabilityReportFunction.cs index b4b6b87a..b2bebb23 100644 --- a/src/Backend/AutopilotMonitor.Functions/Functions/Vulnerability/GetVulnerabilityReportFunction.cs +++ b/src/Backend/AutopilotMonitor.Functions/Functions/Vulnerability/GetVulnerabilityReportFunction.cs @@ -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) { diff --git a/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.Rules.cs b/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.Rules.cs index b8e55a9d..363040a7 100644 --- a/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.Rules.cs +++ b/src/Backend/AutopilotMonitor.Functions/Services/TableStorageService.Rules.cs @@ -553,6 +553,28 @@ namespace AutopilotMonitor.Functions.Services scanSummary?.GetValueOrDefault("overall_risk")?.ToString() ?? "none"); } + /// + /// Deletes the stored vulnerability report for a session. + /// Used when a re-scan finds no vulnerabilities, to clear stale data. + /// + 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 + } + } + /// /// Gets the vulnerability correlation report for a session. /// Returns the deserialized report data, or null if no report exists. diff --git a/src/Shared/AutopilotMonitor.Shared/DataAccess/IVulnerabilityRepository.cs b/src/Shared/AutopilotMonitor.Shared/DataAccess/IVulnerabilityRepository.cs index 708513b9..869de008 100644 --- a/src/Shared/AutopilotMonitor.Shared/DataAccess/IVulnerabilityRepository.cs +++ b/src/Shared/AutopilotMonitor.Shared/DataAccess/IVulnerabilityRepository.cs @@ -18,6 +18,7 @@ namespace AutopilotMonitor.Shared.DataAccess /// to avoid Newtonsoft JToken serialization issues. /// Task GetVulnerabilityReportJsonAsync(string tenantId, string sessionId); + Task DeleteVulnerabilityReportAsync(string tenantId, string sessionId); // --- Software Inventory --- Task UpsertSoftwareInventoryAsync(string tenantId, List> inventoryItems,