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.Components;
using AIStudio.Agents.AssistantAudit;
using AIStudio.Tools.PluginSystem; using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.PluginSystem.Assistants; using AIStudio.Tools.PluginSystem.Assistants;
using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components;
@ -7,22 +8,85 @@ namespace AIStudio.Pages;
public partial class Assistants : MSGComponentBase public partial class Assistants : MSGComponentBase
{ {
private bool isAutoAuditing;
[Inject]
private AssistantPluginAuditService AssistantPluginAuditService { get; init; } = null!;
protected override async Task OnInitializedAsync() protected override async Task OnInitializedAsync()
{ {
this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED, Event.PLUGINS_RELOADED ]); this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED, Event.PLUGINS_RELOADED ]);
await base.OnInitializedAsync(); await base.OnInitializedAsync();
} }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
await this.TryAutoAuditAssistantsAsync();
}
private IReadOnlyCollection<PluginAssistants> AssistantPlugins => private IReadOnlyCollection<PluginAssistants> AssistantPlugins =>
PluginFactory.RunningPlugins.OfType<PluginAssistants>() PluginFactory.RunningPlugins.OfType<PluginAssistants>()
.Where(plugin => this.SettingsManager.IsPluginEnabled(plugin)) .Where(plugin => this.SettingsManager.IsPluginEnabled(plugin))
.ToList(); .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) if (this.isAutoAuditing || !this.SettingsManager.ConfigurationData.AssistantPluginAudit.AutomaticallyAuditAssistants)
return this.InvokeAsync(this.StateHasChanged); 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_ENABLED = "Enabled";
private const string GROUP_DISABLED = "Disabled"; private const string GROUP_DISABLED = "Disabled";
private const string GROUP_INTERNAL = "Internal"; private const string GROUP_INTERNAL = "Internal";
private bool isAutoAuditing;
private DataAssistantPluginAudit AssistantPluginAuditSettings => this.SettingsManager.ConfigurationData.AssistantPluginAudit; private DataAssistantPluginAudit AssistantPluginAuditSettings => this.SettingsManager.ConfigurationData.AssistantPluginAudit;
@ -50,6 +51,12 @@ public partial class Plugins : MSGComponentBase
await base.OnInitializedAsync(); await base.OnInitializedAsync();
} }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
await this.TryAutoAuditAssistantsAsync();
}
#endregion #endregion
private async Task PluginActivationStateChanged(IPluginMetadata pluginMeta) private async Task PluginActivationStateChanged(IPluginMetadata pluginMeta)
@ -75,31 +82,6 @@ public partial class Plugins : MSGComponentBase
return; return;
var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin); 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) if (securityState.RequiresAudit)
{ {
await this.OpenAssistantAuditDialogAsync(pluginMeta.Id); 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 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) private void UpsertAuditCard(PluginAssistantAudit audit)
{ {
var audits = this.SettingsManager.ConfigurationData.AssistantPluginAudits; var audits = this.SettingsManager.ConfigurationData.AssistantPluginAudits;
@ -220,7 +243,12 @@ public partial class Plugins : MSGComponentBase
{ {
switch (triggeredEvent) 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); await this.InvokeAsync(this.StateHasChanged);
break; break;
} }

View File

@ -181,8 +181,8 @@ public static class PluginAssistantSecurityResolver
BadgeIcon = GetSecurityBadgeIcon(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlockedByMinimum, canOverride), 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."), Headline = isBlockedByMinimum ? TB("This assistant is currently locked.") : TB("This assistant can still be used because your settings allow it."),
Description = isBlockedByMinimum 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 '{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 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), StatusColor = GetAvailabilityColor(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlockedByMinimum, canOverride),
StatusIcon = GetAvailabilityIcon(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlockedByMinimum, canOverride), StatusIcon = GetAvailabilityIcon(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlockedByMinimum, canOverride),
ActionLabel = TB("Open Security Check"), ActionLabel = TB("Open Security Check"),
@ -215,7 +215,7 @@ public static class PluginAssistantSecurityResolver
StatusLabel = GetAvailabilityLabel(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlocked: false, canOverride: false), StatusLabel = GetAvailabilityLabel(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlocked: false, canOverride: false),
BadgeIcon = GetSecurityBadgeIcon(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."), 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), StatusColor = GetAvailabilityColor(requiresAudit: false, hasAudit, hasHashMismatch: false, isBlocked: false, canOverride: false),
StatusIcon = GetAvailabilityIcon(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"), ActionLabel = TB("Open Security Check"),