using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace LD_SysInfo.Services { /// /// Scheduled task that collects Windows update history using osquery /// and sends it to the backend API /// public class PatchComplianceTask : ScheduledTask { private readonly AppConfig _config; private readonly ApiClient _apiClient; public override string TaskName => "PatchCompliance"; public override TimeSpan Interval => TimeSpan.FromHours(_config.PatchCompliance.CheckIntervalHours); public PatchComplianceTask(AppConfig config, ApiClient apiClient) { _config = config; _apiClient = apiClient; // Initialize LastRun from config if available LastRun = _config.PatchCompliance.LastCheckTime; } public override bool ShouldRun() { // Don't run if disabled in config if (!_config.PatchCompliance.Enabled) return false; return base.ShouldRun(); } public override async Task ExecuteAsync() { try { // Query windows_update_history from osquery var updates = OsqueryService.Query(@" SELECT date, title, update_id FROM windows_update_history ORDER BY date DESC; "); if (!updates.Any()) { LogInfo("No Windows update history found."); return; } // Transform to API-friendly format var patchData = updates.Select(u => new { date = u.GetValueOrDefault("date"), title = u.GetValueOrDefault("title"), updateId = u.GetValueOrDefault("update_id") }).ToList(); LogInfo($"Collected {patchData.Count} Windows updates."); // Send to API await _apiClient.PostPatchComplianceAsync(patchData); // Update config with last check time _config.PatchCompliance.LastCheckTime = DateTime.Now; SaveConfig(); LogInfo("Patch compliance data sent successfully."); } catch (Exception ex) { LogError($"PatchComplianceTask failed: {ex.Message}"); throw; } } private void SaveConfig() { try { var configPath = System.IO.Path.Combine(AppContext.BaseDirectory, "config.json"); var json = Newtonsoft.Json.JsonConvert.SerializeObject(_config, Newtonsoft.Json.Formatting.Indented); System.IO.File.WriteAllText(configPath, json); } catch (Exception ex) { LogError($"Failed to save config: {ex.Message}"); } } private void LogInfo(string message) { if (!_config.EnableLogging) return; try { var logDir = System.IO.Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "PSG-Oversight" ); System.IO.Directory.CreateDirectory(logDir); var logPath = System.IO.Path.Combine(logDir, "patch_compliance.log"); System.IO.File.AppendAllText(logPath, $"[{DateTime.Now}] INFO: {message}\n"); } catch { } } private void LogError(string message) { try { var logDir = System.IO.Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "PSG-Oversight" ); System.IO.Directory.CreateDirectory(logDir); var logPath = System.IO.Path.Combine(logDir, "patch_compliance.log"); System.IO.File.AppendAllText(logPath, $"[{DateTime.Now}] ERROR: {message}\n"); } catch { } } } }