Implementation of basic PatchComplianceTask. First real iteration, basic/raw asf.
133 lines
4.3 KiB
C#
133 lines
4.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace LD_SysInfo.Services
|
|
{
|
|
/// <summary>
|
|
/// Scheduled task that collects Windows update history using osquery
|
|
/// and sends it to the backend API
|
|
/// </summary>
|
|
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 { }
|
|
}
|
|
}
|
|
}
|