making the audit fully automatic

This commit is contained in:
krut_ni 2026-04-07 17:59:43 +02:00
parent 054481abcf
commit 820c9e2596
No known key found for this signature in database
GPG Key ID: A5C0151B4DDB172C
3 changed files with 125 additions and 33 deletions

View File

@ -1,4 +1,5 @@
using AIStudio.Components;
using AIStudio.Agents.AssistantAudit;
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.PluginSystem.Assistants;
using Microsoft.AspNetCore.Components;
@ -7,22 +8,85 @@ namespace AIStudio.Pages;
public partial class Assistants : MSGComponentBase
{
private bool isAutoAuditing;
[Inject]
private AssistantPluginAuditService AssistantPluginAuditService { get; init; } = null!;
protected override async Task OnInitializedAsync()
{
this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED, Event.PLUGINS_RELOADED ]);
await base.OnInitializedAsync();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
await this.TryAutoAuditAssistantsAsync();
}
private IReadOnlyCollection<PluginAssistants> AssistantPlugins =>
PluginFactory.RunningPlugins.OfType<PluginAssistants>()
.Where(plugin => this.SettingsManager.IsPluginEnabled(plugin))
.ToList();
protected override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
private async Task TryAutoAuditAssistantsAsync()
{
if (triggeredEvent is Event.CONFIGURATION_CHANGED or Event.PLUGINS_RELOADED)
return this.InvokeAsync(this.StateHasChanged);
if (this.isAutoAuditing || !this.SettingsManager.ConfigurationData.AssistantPluginAudit.AutomaticallyAuditAssistants)
return;
return Task.CompletedTask;
this.isAutoAuditing = true;
try
{
var wasConfigurationChanged = false;
var assistantPlugins = PluginFactory.RunningPlugins.OfType<PluginAssistants>().ToList();
foreach (var assistantPlugin in assistantPlugins)
{
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
if (!securityState.RequiresAudit)
continue;
var audit = await this.AssistantPluginAuditService.RunAuditAsync(assistantPlugin);
if (audit.Level is AssistantAuditLevel.UNKNOWN)
{
await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.SettingsSuggest, string.Format(this.T("The automatic security audit for the assistant plugin '{0}' failed. Please run it manually from the plugins page."), assistantPlugin.Name)));
continue;
}
this.UpsertAuditCard(audit);
wasConfigurationChanged = true;
}
if (!wasConfigurationChanged)
return;
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
finally
{
this.isAutoAuditing = false;
await this.InvokeAsync(this.StateHasChanged);
}
}
private void UpsertAuditCard(PluginAssistantAudit audit)
{
var audits = this.SettingsManager.ConfigurationData.AssistantPluginAudits;
var existingIndex = audits.FindIndex(x => x.PluginId == audit.PluginId);
if (existingIndex >= 0)
audits[existingIndex] = audit;
else
audits.Add(audit);
}
protected override async Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
{
if (triggeredEvent is Event.PLUGINS_RELOADED)
await this.TryAutoAuditAssistantsAsync();
if (triggeredEvent is Event.CONFIGURATION_CHANGED or Event.PLUGINS_RELOADED)
await this.InvokeAsync(this.StateHasChanged);
}
}

View File

@ -15,6 +15,7 @@ public partial class Plugins : MSGComponentBase
private const string GROUP_ENABLED = "Enabled";
private const string GROUP_DISABLED = "Disabled";
private const string GROUP_INTERNAL = "Internal";
private bool isAutoAuditing;
private DataAssistantPluginAudit AssistantPluginAuditSettings => this.SettingsManager.ConfigurationData.AssistantPluginAudit;
@ -50,6 +51,12 @@ public partial class Plugins : MSGComponentBase
await base.OnInitializedAsync();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
await this.TryAutoAuditAssistantsAsync();
}
#endregion
private async Task PluginActivationStateChanged(IPluginMetadata pluginMeta)
@ -75,31 +82,6 @@ public partial class Plugins : MSGComponentBase
return;
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
if (securityState.RequiresAudit)
{
if (this.AssistantPluginAuditSettings.AutomaticallyAuditAssistants)
{
var audit = await this.AssistantPluginAuditService.RunAuditAsync(assistantPlugin);
if (audit.Level is AssistantAuditLevel.UNKNOWN)
{
await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.SettingsSuggest, string.Format(T("The Security Audit returned an invalid result, please try again manually."))));
await this.OpenAssistantAuditDialogAsync(pluginMeta.Id);
return;
}
this.UpsertAuditCard(audit);
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
}
else
{
await this.OpenAssistantAuditDialogAsync(pluginMeta.Id);
return;
}
}
if (securityState.RequiresAudit)
{
await this.OpenAssistantAuditDialogAsync(pluginMeta.Id);
@ -204,6 +186,47 @@ public partial class Plugins : MSGComponentBase
private PluginAssistants? TryGetAssistantPlugin(Guid pluginId) => PluginFactory.RunningPlugins.OfType<PluginAssistants>().FirstOrDefault(x => x.Id == pluginId);
private async Task TryAutoAuditAssistantsAsync()
{
if (this.isAutoAuditing || !this.AssistantPluginAuditSettings.AutomaticallyAuditAssistants)
return;
this.isAutoAuditing = true;
try
{
var wasConfigurationChanged = false;
var assistantPlugins = PluginFactory.RunningPlugins.OfType<PluginAssistants>().ToList();
foreach (var assistantPlugin in assistantPlugins)
{
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin);
if (!securityState.RequiresAudit)
continue;
var audit = await this.AssistantPluginAuditService.RunAuditAsync(assistantPlugin);
if (audit.Level is AssistantAuditLevel.UNKNOWN)
{
await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.SettingsSuggest, string.Format(this.T("The automatic security audit for the assistant plugin '{0}' failed. Please run it manually."), assistantPlugin.Name)));
continue;
}
this.UpsertAuditCard(audit);
wasConfigurationChanged = true;
}
if (!wasConfigurationChanged)
return;
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
}
finally
{
this.isAutoAuditing = false;
await this.InvokeAsync(this.StateHasChanged);
}
}
private void UpsertAuditCard(PluginAssistantAudit audit)
{
var audits = this.SettingsManager.ConfigurationData.AssistantPluginAudits;
@ -220,7 +243,12 @@ public partial class Plugins : MSGComponentBase
{
switch (triggeredEvent)
{
case Event.PLUGINS_RELOADED or Event.CONFIGURATION_CHANGED:
case Event.PLUGINS_RELOADED:
await this.TryAutoAuditAssistantsAsync();
await this.InvokeAsync(this.StateHasChanged);
break;
case Event.CONFIGURATION_CHANGED:
await this.InvokeAsync(this.StateHasChanged);
break;
}

View File

@ -181,8 +181,8 @@ public static class PluginAssistantSecurityResolver
BadgeIcon = GetSecurityBadgeIcon(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlockedByMinimum, canOverride),
Headline = isBlockedByMinimum ? TB("This assistant is currently locked.") : TB("This assistant can still be used because your settings allow it."),
Description = isBlockedByMinimum
? string.Format(TB("The current audit result \"{0}\" is below your required minimum level \"{1}\". Your security settings therefore block this assistant plugin."), auditLevel.GetName(), auditSettings.MinimumLevel.GetName())
: string.Format(TB("The current audit result is \"{0}\", which is below your required minimum level \"{1}\". Your settings still allow manual activation, but the assistant keeps this security status and should be reviewed carefully."), auditLevel.GetName(), auditSettings.MinimumLevel.GetName()),
? string.Format(TB("The current audit result '{0}' is below your required minimum level '{1}'. Your security settings therefore block this assistant plugin."), auditLevel.GetName(), auditSettings.MinimumLevel.GetName())
: string.Format(TB("The current audit result is '{0}', which is below your required minimum level '{1}'. Your settings still allow manual activation, but the assistant keeps this security status and should be reviewed carefully."), auditLevel.GetName(), auditSettings.MinimumLevel.GetName()),
StatusColor = GetAvailabilityColor(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlockedByMinimum, canOverride),
StatusIcon = GetAvailabilityIcon(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlockedByMinimum, canOverride),
ActionLabel = TB("Open Security Check"),
@ -215,7 +215,7 @@ public static class PluginAssistantSecurityResolver
StatusLabel = GetAvailabilityLabel(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlocked: false, canOverride: false),
BadgeIcon = GetSecurityBadgeIcon(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlocked: false, canOverride: false),
Headline = TB("This assistant is currently unlocked."),
Description = string.Format(TB("The stored audit matches the current plugin code and meets your required minimum level \"{0}\"."), auditSettings.MinimumLevel.GetName()),
Description = string.Format(TB("The stored audit matches the current plugin code and meets your required minimum level '{0}'."), auditSettings.MinimumLevel.GetName()),
StatusColor = GetAvailabilityColor(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlocked: false, canOverride: false),
StatusIcon = GetAvailabilityIcon(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlocked: false, canOverride: false),
ActionLabel = TB("Open Security Check"),