Added a prompt injection detection (#857)

Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
Sabrina-devopsandThorsten Sommer authored and GitHub committed 2026-08-23 11:09:11 +02:00
1 parent d3163badd9
commit 902a01a4d0
55 files changed
+5598 -70

No files matched your search

@@ -0,0 +1,128 @@
@using AIStudio.Tools.Security
@inherits MSGComponentBase
<MudDialog>
<DialogContent>
<MudPaper Class="pa-6 mb-4" Elevation="0" Outlined="true">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="3">
<MudAvatar Size="Size.Large" Color="Color.Warning" Variant="Variant.Filled">
<MudIcon Icon="@Icons.Material.Filled.GppMaybe"/>
</MudAvatar>
<MudStack Spacing="0">
<MudJustifiedText Typo="Typo.h5">
@T("Suspicious content was removed")
</MudJustifiedText>
<MudJustifiedText Typo="Typo.body2">
@T("AI Studio found instructions aimed at the AI inside your content and removed them. Everything around them was kept, so you can continue working with the content. Please review what was removed below.")
</MudJustifiedText>
</MudStack>
</MudStack>
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Class="mt-4">
<MudJustifiedText Typo="Typo.body2">
@T("Typical attacks on AI systems (e.g. prompt injection) hide instructions within untrusted content to trick an AI model into ignoring its intended rules or performing unintended actions.")
</MudJustifiedText>
</MudAlert>
</MudPaper>
<MudDivider />
<MudStack Row="true" Justify="Justify.Center" Class="my-2">
<MudButton Variant="Variant.Text"
EndIcon="@(this.showPromptInjectionInformation ? Icons.Material.Filled.ExpandLess : Icons.Material.Filled.ExpandMore)"
OnClick="@this.TogglePromptInjectionInformation">
@(this.showPromptInjectionInformation ? T("Hide more information") : T("More information"))
</MudButton>
</MudStack>
<MudCollapse Expanded="@this.showPromptInjectionInformation">
<MudPaper Outlined="true"
Class="pa-4 mb-4">
@foreach (var result in this.Alert.Results)
{
<MudGrid Class="mb-2">
<MudItem xs="12" md="3">
<MudStack>
<MudIcon Icon="@Icons.Material.Filled.Source" />
<MudJustifiedText Typo="Typo.subtitle2">
@T("Source type")
</MudJustifiedText>
<MudJustifiedText Typo="Typo.body2">
@result.Source.Kind.GetDisplayName()
</MudJustifiedText>
</MudStack>
</MudItem>
<MudItem xs="12" md="4">
<MudStack>
<MudIcon Icon="@Icons.Material.Outlined.Description"/>
<MudJustifiedText Typo="Typo.subtitle2">
@T("Content source")
</MudJustifiedText>
<MudJustifiedText Typo="Typo.body2" Style="word-break:break-all;">
@result.Source.Label
</MudJustifiedText>
</MudStack>
</MudItem>
<MudItem xs="12" md="5">
<MudStack>
<MudIcon Icon="@Icons.Material.Outlined.WarningAmber"/>
<MudJustifiedText Typo="Typo.subtitle2">
@T("Removed content")
</MudJustifiedText>
@foreach (var finding in result.Findings)
{
<MudStack Spacing="0">
<MudJustifiedText Typo="Typo.body2">
<b>
@finding.Category.GetDisplayName()
</b>
</MudJustifiedText>
<MudJustifiedText Typo="Typo.body2" Class="ml-4 mt-1">
@finding.Snippet
</MudJustifiedText>
</MudStack>
}
@* The runtime caps how many passages it describes, while it removes every one of them. *@
@if (result.RedactedCount > result.Findings.Count)
{
<MudJustifiedText Typo="Typo.body2" Class="mt-1">
@string.Format(T("And {0} more passages of the same kind."), result.RedactedCount - result.Findings.Count)
</MudJustifiedText>
}
</MudStack>
</MudItem>
</MudGrid>
}
</MudPaper>
<MudPaper Class="pa-4 mt-2" Outlined="true">
<MudJustifiedText Typo="Typo.body2" Class="mb-3">
@T("Prompt injection is a method used to manipulate AI systems such as chatbots. An attacker places misleading instructions in content so that the AI treats them as legitimate. This can cause the AI to ignore safeguards, expose private information, or generate harmful content.")
</MudJustifiedText>
<MudLink Href="@PromptInjectionGuardService.WIKI_URL" Target="_blank">
@PromptInjectionGuardService.WIKI_URL
</MudLink>
</MudPaper>
</MudCollapse>
</DialogContent>
<DialogActions>
@if (CanDisableFutureAlerts)
{
<MudButton Variant="Variant.Text" Color="Color.Default" OnClick="@this.CloseAndDisableFutureAlertsAsync">
@T("Close and don't show again")
</MudButton>
}
<MudButton Variant="Variant.Filled" Color="Color.Default" OnClick="@this.Close">
@T("Close")
</MudButton>
</DialogActions>
</MudDialog>
@@ -0,0 +1,39 @@
using AIStudio.Components;
using AIStudio.Settings;
using AIStudio.Tools.Security;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Dialogs;
public partial class PromptInjectionAlertDialog : MSGComponentBase
{
private bool showPromptInjectionInformation;
private static bool CanDisableFutureAlerts => !ManagedConfiguration.TryGet(x => x.App, x => x.ShowPromptInjectionAlert, out var meta) || !meta.IsLocked;
[CascadingParameter]
private IMudDialogInstance MudDialog { get; set; } = null!;
/// <summary>
/// What was filtered during the user action that triggered this dialog.
/// </summary>
/// <remarks>
/// Carries every affected source, because one action may involve many documents and the
/// user should acknowledge them together rather than one dialog at a time.
/// </remarks>
[Parameter, EditorRequired]
public PromptInjectionAlertMessage Alert { get; set; } = null!;
private void Close() => this.MudDialog.Close();
private async Task CloseAndDisableFutureAlertsAsync()
{
this.SettingsManager.ConfigurationData.App.ShowPromptInjectionAlert = false;
await this.SettingsManager.StoreSettings();
await this.MessageBus.SendMessage<bool>(this, Event.CONFIGURATION_CHANGED);
this.MudDialog.Close();
}
private void TogglePromptInjectionInformation() => this.showPromptInjectionInformation = !this.showPromptInjectionInformation;
}
@@ -14,7 +14,6 @@
<ConfigurationOption OptionDescription="@T("Show the latest message after loading?")" LabelOn="@T("Latest message is shown, after loading a chat")" LabelOff="@T("First (oldest) message is shown, after loading a chat")" State="@(() => this.SettingsManager.ConfigurationData.Chat.ShowLatestMessageAfterLoading)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Chat.ShowLatestMessageAfterLoading = updatedState)" OptionHelp="@T("When enabled, the latest message is shown after loading a chat. When disabled, the first (oldest) message is shown.")"/>
<ConfigurationSelect OptionDescription="@T("Provider selection when creating new chats")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.AddChatProviderBehavior)" Data="@ConfigurationSelectDataFactory.GetAddChatProviderBehavior()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.AddChatProviderBehavior = selectedValue)" OptionHelp="@T("Control how the LLM provider for added chats is selected.")"/>
<ConfigurationSelect OptionDescription="@T("Provider selection when loading a chat and sending assistant results to chat")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.LoadingProviderBehavior)" Data="@ConfigurationSelectDataFactory.GetLoadingChatProviderBehavior()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.LoadingProviderBehavior = selectedValue)" OptionHelp="@T("Control how the LLM provider for loaded chats is selected and when assistant results are sent to chat.")"/>
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
<ConfigurationOption OptionDescription="@T("Preselect chat options?")" LabelOn="@T("Chat options are preselected")" LabelOff="@T("No chat options are preselected")" State="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.Chat.PreselectOptions = updatedState)" OptionHelp="@T("When enabled, you can preselect chat options. This is might be useful when you prefer a specific provider.")" IsLocked="() => ManagedConfiguration.TryGet(x => x.Chat, x => x.PreselectOptions, out var meta) && meta.IsLocked"/>
<ConfigurationProviderSelection Component="Components.CHAT" Data="@this.AvailableLLMProviders" Disabled="@(() => !this.SettingsManager.ConfigurationData.Chat.PreselectOptions)" SelectedValue="@(() => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.Chat.PreselectedProvider = selectedValue)" IsLocked="() => ManagedConfiguration.TryGet(x => x.Chat, x => x.PreselectedProvider, out var meta) && meta.IsLocked"/>