diff --git a/src/Abstractions/CrestApps.Core.AI.Abstractions/Security/PromptSecurityProfileSettings.cs b/src/Abstractions/CrestApps.Core.AI.Abstractions/Security/PromptSecurityProfileSettings.cs index fbecfa66..573f0cd4 100644 --- a/src/Abstractions/CrestApps.Core.AI.Abstractions/Security/PromptSecurityProfileSettings.cs +++ b/src/Abstractions/CrestApps.Core.AI.Abstractions/Security/PromptSecurityProfileSettings.cs @@ -1,68 +1,40 @@ namespace CrestApps.Core.AI.Security; /// -/// Per-profile settings that control prompt security behavior. +/// Per-profile anti-spam throttling overrides for an AI Profile. /// Stored on using profile.WithSettings(new PromptSecurityProfileSettings { ... }). -/// When present, these settings override the site-level defaults -/// for the specific profile. +/// When present, these values override the site-level throttling defaults defined on +/// for the specific profile, letting each use case raise or lower +/// its limits. High-level input and output security guards (injection detection, output filtering, +/// security preamble, input delimiters, blocking threshold, and maximum prompt length) are intentionally +/// not part of this model; those remain global concerns configured through . /// public sealed class PromptSecurityProfileSettings { /// - /// Gets or sets a value indicating whether the prompt security layer is enabled for this profile. - /// When , the site-level default is used. - /// Set to to explicitly disable security for this profile. - /// - public bool? IsEnabled { get; set; } - - /// - /// Gets or sets a value indicating whether injection pattern detection is enabled for this profile. - /// When , the site-level default is used. - /// - public bool? EnableInjectionDetection { get; set; } - - /// - /// Gets or sets a value indicating whether output security filtering is enabled for this profile. - /// When , the site-level default is used. - /// - public bool? EnableOutputFiltering { get; set; } - - /// - /// Gets or sets a value indicating whether the hardened security preamble is prepended - /// to the system prompt for this profile. - /// When , the site-level default is used. - /// - public bool? EnableSecurityPreamble { get; set; } - - /// - /// Gets or sets a value indicating whether user messages are wrapped with boundary - /// delimiters for this profile. - /// When , the site-level default is used. - /// - public bool? EnableInputDelimiters { get; set; } - - /// - /// Gets or sets the maximum allowed prompt length for this profile. + /// Gets or sets the maximum number of messages allowed within the rate limit window for this profile. /// When , the site-level default is used. + /// Set to 0 to explicitly disable message rate limiting for this profile. /// - public int? MaxPromptLength { get; set; } + public int? MaxMessagesPerWindow { get; set; } /// - /// Gets or sets the minimum risk level at which prompts are blocked for this profile. + /// Gets or sets the rate limit window duration for this profile. /// When , the site-level default is used. /// - public PromptRiskLevel? BlockingThreshold { get; set; } + public TimeSpan? RateLimitWindow { get; set; } /// - /// Gets or sets the maximum number of messages allowed within the rate limit window for this profile. + /// Gets or sets the maximum number of anonymous chat sessions that can be started + /// within the anonymous session rate-limit window for this profile. /// When , the site-level default is used. - /// Set to 0 to explicitly disable rate limiting for this profile. + /// Set to 0 to explicitly disable anonymous session-start throttling for this profile. /// - public int? MaxMessagesPerWindow { get; set; } + public int? MaxAnonymousSessionsPerWindow { get; set; } /// - /// Gets or sets the rate limit window duration for this profile. + /// Gets or sets the anonymous session-start rate-limit window duration for this profile. /// When , the site-level default is used. /// - public TimeSpan? RateLimitWindow { get; set; } + public TimeSpan? AnonymousSessionRateLimitWindow { get; set; } } diff --git a/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md b/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md index 1aa63b90..33687dbc 100644 --- a/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md +++ b/src/CrestApps.Core.Docs/docs/changelog/v1.0.0.md @@ -15,6 +15,7 @@ description: Initial standalone release notes for the CrestApps.Core repository. - ships the shared abstractions, infrastructure, AI runtime, provider integrations, and protocol packages under the `CrestApps.Core` name - assigns stable first-party anonymous visitor IDs to AI chat sessions, uses those visitor IDs for unique-visitor analytics, makes remote-address capture configurable with privacy-first hashed defaults plus optional plain-text or encrypted-at-rest storage, protects anonymous chat session starts with shared ASP.NET Core plus hub-level rate limiting, and documents how hosts can tune thresholds or replace the default endpoint policy with their own `Microsoft.AspNetCore.RateLimiting` policy +- lets AI Profiles and AI Profile-source templates override the site-wide anti-spam throttle limits through `PromptSecurityProfileSettings` (max messages per window, message window, max anonymous sessions per window, and anonymous session window), with each unset value falling back to the `PromptSecurityOptions` site default so profiles can raise or lower quotas per use case; both the message and anonymous session-start limiters honor the overrides, while high-level input and output security guards (injection detection, output filtering, security preamble, input delimiters, blocking threshold, and maximum prompt length) remain global-only concerns - defers AI Profile initial-prompt persistence until the first real user prompt arrives and stops the sample chat widgets from auto-creating empty sessions on initial page load - includes a reference MVC host and an Aspire host for local composition and testing - includes a dedicated `CrestApps.Core.Tests` project for framework validation diff --git a/src/CrestApps.Core.Docs/docs/core/ai-profiles.md b/src/CrestApps.Core.Docs/docs/core/ai-profiles.md index ce5fa4dc..61e03b9b 100644 --- a/src/CrestApps.Core.Docs/docs/core/ai-profiles.md +++ b/src/CrestApps.Core.Docs/docs/core/ai-profiles.md @@ -127,15 +127,17 @@ Profiles can opt into user memory so experiences can carry durable context forwa That toggle is stored directly as `MemoryMetadata`, so profile and template consumers read and write one shared metadata shape instead of carrying legacy memory-setting aliases forward. -### 8. Prompt security +### 8. Anti-spam throttling -Profiles can also override the site-level prompt security defaults through `PromptSecurityProfileSettings`. +Profiles can override the site-level anti-spam throttle limits through `PromptSecurityProfileSettings`. -That lets you keep a strong global baseline while adjusting individual profile behavior for cases such as: +That lets you keep a strong global baseline while raising or lowering throttle quotas for individual profiles, for cases such as: -- stricter blocking for high-sensitivity assistants -- longer prompt limits for carefully managed internal workflows -- disabling the security layer for intentionally operator-controlled profiles +- tighter per-minute message limits for public, unauthenticated widgets +- higher limits for carefully managed internal or authenticated workflows +- adjusting anonymous session-start quotas for a specific use case + +Only anti-spam throttling is per-profile. High-level input and output security guards (injection detection, output filtering, security preamble, input delimiters, blocking threshold, and maximum prompt length) remain global-only and are configured through `PromptSecurityOptions`. See [Prompt Security](./prompt-security.md) for the full option set, scoring model, and limitations. diff --git a/src/CrestApps.Core.Docs/docs/core/prompt-security.md b/src/CrestApps.Core.Docs/docs/core/prompt-security.md index 125730b0..e89df9d5 100644 --- a/src/CrestApps.Core.Docs/docs/core/prompt-security.md +++ b/src/CrestApps.Core.Docs/docs/core/prompt-security.md @@ -149,22 +149,20 @@ The prompt security layer is controlled by `PromptSecurityOptions`. The framework supports two layers of configuration: 1. **site-level defaults** through `PromptSecurityOptions` -2. **per-profile overrides** through `PromptSecurityProfileSettings` +2. **per-profile anti-spam throttle overrides** through `PromptSecurityProfileSettings` -Both MVC and Blazor sample hosts expose the site defaults in admin settings, and AI Profiles plus AI Profile-source templates can override the default behavior. +Both MVC and Blazor sample hosts expose the site defaults in admin settings. AI Profiles and AI Profile-source templates can override the anti-spam throttle limits so each use case can raise or lower its quotas. -Per-profile overrides support: +Per-profile overrides are intentionally scoped to anti-spam throttling only: -- enabling or disabling the prompt security layer -- enabling or disabling injection detection -- enabling or disabling output filtering -- enabling or disabling the security preamble -- enabling or disabling input delimiters -- overriding maximum prompt length -- overriding the blocking threshold -- overriding rate limit settings (messages per window, window duration) +- `MaxMessagesPerWindow` — maximum messages allowed within the message window +- `RateLimitWindow` — the message sliding-window duration +- `MaxAnonymousSessionsPerWindow` — maximum anonymous session starts within the session window +- `AnonymousSessionRateLimitWindow` — the anonymous session-start window duration -That model lets you keep strong defaults globally while allowing carefully chosen profiles to be more permissive or more strict. +High-level input and output security guards — injection detection, output filtering, the security preamble, input delimiters, the blocking threshold, and the maximum prompt length — remain **global concerns** configured only through `PromptSecurityOptions`. They are deliberately not overridable per profile so protective posture stays consistent across every profile. + +That model lets you keep strong global guards while allowing carefully chosen profiles to be more permissive or more strict on throttling. ## Rate limiting @@ -196,17 +194,19 @@ The default limiter behavior is privacy-first but configurable through `AIChatRa ### Per-profile override -Rate limiting can be customized per AI Profile using `PromptSecurityProfileSettings`: +Anti-spam throttling can be customized per AI Profile (or AI Profile-source template) using `PromptSecurityProfileSettings`. Any field left `null` falls back to the site-level default: ```csharp profile.WithSettings(new PromptSecurityProfileSettings { MaxMessagesPerWindow = 10, RateLimitWindow = TimeSpan.FromMinutes(2), + MaxAnonymousSessionsPerWindow = 3, + AnonymousSessionRateLimitWindow = TimeSpan.FromMinutes(10), }); ``` -Set `MaxMessagesPerWindow` to `0` on a profile to disable rate limiting for that profile even when site-level rate limiting is enabled. +Both the message throttle (`DefaultChatRateLimiter`) and the anonymous session-start throttle (`DefaultChatSessionStartRateLimiter`) honor these overrides, falling back to the site defaults on `PromptSecurityOptions` when a value is not set. Set `MaxMessagesPerWindow` (or `MaxAnonymousSessionsPerWindow`) to `0` on a profile to disable that throttle for the profile even when site-level rate limiting is enabled. ### How it works diff --git a/src/Primitives/CrestApps.Core.AI/Security/DefaultChatSessionStartRateLimiter.cs b/src/Primitives/CrestApps.Core.AI/Security/DefaultChatSessionStartRateLimiter.cs index 41275a6a..ddac5b55 100644 --- a/src/Primitives/CrestApps.Core.AI/Security/DefaultChatSessionStartRateLimiter.cs +++ b/src/Primitives/CrestApps.Core.AI/Security/DefaultChatSessionStartRateLimiter.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using CrestApps.Core.AI.Models; using CrestApps.Core.Support; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -7,7 +8,8 @@ namespace CrestApps.Core.AI.Security; /// /// Default implementation of that limits -/// anonymous session creation using a sliding window. +/// anonymous session creation using a sliding window. Per-profile overrides on +/// take precedence over the site-level defaults. /// public sealed class DefaultChatSessionStartRateLimiter : IChatSessionStartRateLimiter { @@ -50,7 +52,11 @@ public ValueTask EvaluateAsync(PromptSecurityContext context, C } var options = _options.Value; - var maxSessions = options.MaxAnonymousSessionsPerWindow; + + // Resolve per-profile anti-spam overrides, falling back to site-level defaults. + var profileSettings = context.Profile?.TryGetSettings(out var ps) == true ? ps : null; + var maxSessions = profileSettings?.MaxAnonymousSessionsPerWindow ?? options.MaxAnonymousSessionsPerWindow; + var window = profileSettings?.AnonymousSessionRateLimitWindow ?? options.AnonymousSessionRateLimitWindow; if (maxSessions <= 0) { @@ -64,7 +70,6 @@ public ValueTask EvaluateAsync(PromptSecurityContext context, C return ValueTask.FromResult(RateLimitResult.Allowed); } - var window = options.AnonymousSessionRateLimitWindow; var now = _timeProvider.GetUtcNow(); var windowStart = now - window; diff --git a/src/Primitives/CrestApps.Core.AI/Security/DefaultPromptSecurityService.cs b/src/Primitives/CrestApps.Core.AI/Security/DefaultPromptSecurityService.cs index 00eb3b8c..9eabd843 100644 --- a/src/Primitives/CrestApps.Core.AI/Security/DefaultPromptSecurityService.cs +++ b/src/Primitives/CrestApps.Core.AI/Security/DefaultPromptSecurityService.cs @@ -7,7 +7,8 @@ namespace CrestApps.Core.AI.Security; /// /// Default implementation of that validates user prompts /// against known prompt injection patterns and configurable security policies. -/// Respects per-profile security settings that override site-level defaults. +/// Security guards are governed by the site-level , while the +/// injected rate limiter honors per-profile anti-spam throttle overrides. /// public sealed class DefaultPromptSecurityService : IPromptSecurityService { @@ -50,16 +51,8 @@ public async Task ValidateInputAsync(PromptSecurityContext var siteOptions = _options.Value; - // Resolve per-profile security settings. - var profileSettings = context.Profile?.TryGetSettings(out var ps) == true ? ps : null; - - // Check if security is entirely disabled for this profile. - if (profileSettings?.IsEnabled == false) - { - return PromptSecurityResult.Safe; - } - // Rate limit check (before expensive regex evaluation). + // The rate limiter honors per-profile anti-spam throttle overrides. var rateLimitResult = await _rateLimiter.EvaluateAsync(context, cancellationToken); if (rateLimitResult.IsThrottled) @@ -77,7 +70,7 @@ public async Task ValidateInputAsync(PromptSecurityContext return rateLimitBlockedResult; } - var injectionDetectionEnabled = profileSettings?.EnableInjectionDetection ?? siteOptions.EnableInjectionDetection; + var injectionDetectionEnabled = siteOptions.EnableInjectionDetection; if (!injectionDetectionEnabled) { @@ -89,8 +82,8 @@ public async Task ValidateInputAsync(PromptSecurityContext return PromptSecurityResult.Safe; } - var blockingThreshold = profileSettings?.BlockingThreshold ?? siteOptions.BlockingThreshold; - var maxPromptLength = profileSettings?.MaxPromptLength ?? siteOptions.MaxPromptLength; + var blockingThreshold = siteOptions.BlockingThreshold; + var maxPromptLength = siteOptions.MaxPromptLength; var evaluationContext = new PromptSecurityEvaluationContext { OriginalInput = context.Prompt, diff --git a/src/Primitives/CrestApps.Core.AI/Security/SecurityPromptOrchestrationHandler.cs b/src/Primitives/CrestApps.Core.AI/Security/SecurityPromptOrchestrationHandler.cs index 7e87dc64..33f7af69 100644 --- a/src/Primitives/CrestApps.Core.AI/Security/SecurityPromptOrchestrationHandler.cs +++ b/src/Primitives/CrestApps.Core.AI/Security/SecurityPromptOrchestrationHandler.cs @@ -69,22 +69,16 @@ public async Task BuiltAsync(OrchestrationContextBuiltContext context, Cancellat // Only apply security to AI Profile-based chats. // Chat Interactions give the user full control (system prompt, model, MCP) // so the security layer is not applicable. - if (context.Resource is not AIProfile profile) + if (context.Resource is not AIProfile) { return; } - // Resolve effective options by merging site-level defaults with per-profile overrides. - var effectiveOptions = ResolveEffectiveOptions(profile); - - // If security is entirely disabled for this profile, skip. - if (!effectiveOptions.IsEnabled) - { - return; - } + // Security guards are governed globally by the site-level options. + var siteOptions = _options.Value; // Prepend the security preamble to the system message. - if (effectiveOptions.EnableSecurityPreamble) + if (siteOptions.EnableSecurityPreamble) { var preamble = await _templateService.RenderAsync(SecurityPreambleTemplateId, cancellationToken: cancellationToken); @@ -113,7 +107,7 @@ public async Task BuiltAsync(OrchestrationContextBuiltContext context, Cancellat } // Wrap user message with delimiters to establish clear input boundaries. - if (effectiveOptions.EnableInputDelimiters && !string.IsNullOrEmpty(context.OrchestrationContext.UserMessage)) + if (siteOptions.EnableInputDelimiters && !string.IsNullOrEmpty(context.OrchestrationContext.UserMessage)) { // Sanitize user input by removing any injected delimiter tokens. var sanitizedMessage = context.OrchestrationContext.UserMessage @@ -140,38 +134,4 @@ public async Task BuiltAsync(OrchestrationContextBuiltContext context, Cancellat } } } - - private EffectiveSecurityOptions ResolveEffectiveOptions(AIProfile profile) - { - var siteOptions = _options.Value; - var profileSettings = profile.TryGetSettings(out var settings) ? settings : null; - - return new EffectiveSecurityOptions - { - IsEnabled = profileSettings?.IsEnabled ?? true, - EnableSecurityPreamble = profileSettings?.EnableSecurityPreamble ?? siteOptions.EnableSecurityPreamble, - EnableInputDelimiters = profileSettings?.EnableInputDelimiters ?? siteOptions.EnableInputDelimiters, - EnableInjectionDetection = profileSettings?.EnableInjectionDetection ?? siteOptions.EnableInjectionDetection, - EnableOutputFiltering = profileSettings?.EnableOutputFiltering ?? siteOptions.EnableOutputFiltering, - MaxPromptLength = profileSettings?.MaxPromptLength ?? siteOptions.MaxPromptLength, - BlockingThreshold = profileSettings?.BlockingThreshold ?? siteOptions.BlockingThreshold, - }; - } - - private sealed class EffectiveSecurityOptions - { - public bool IsEnabled { get; init; } - - public bool EnableSecurityPreamble { get; init; } - - public bool EnableInputDelimiters { get; init; } - - public bool EnableInjectionDetection { get; init; } - - public bool EnableOutputFiltering { get; init; } - - public int MaxPromptLength { get; init; } - - public PromptRiskLevel BlockingThreshold { get; init; } - } } diff --git a/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/AIProfiles/Create.razor b/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/AIProfiles/Create.razor index 1e47239b..eafe94df 100644 --- a/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/AIProfiles/Create.razor +++ b/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/AIProfiles/Create.razor @@ -1144,73 +1144,34 @@
-
Prompt Security Override
+
Anti-Spam Throttle Override

- Override the site-level prompt security defaults for this profile. Leave options at "Use default" to inherit the global settings. + Override the site-level anti-spam throttle limits for this profile. Leave a field empty to inherit the global setting.

- - - - - - -
Override whether the security layer is active for this profile.
-
- -
- - - - - - -
- -
- - - - - - + + +
Leave empty to use the site default. Set to 0 to disable message throttling for this profile.
- - - - - - + + +
Leave empty to use the site default.
- - - - - - + + +
Leave empty to use the site default. Set to 0 to disable anonymous session-start throttling for this profile.
- - + +
Leave empty to use the site default.
- -
- - - - - - - - -
diff --git a/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/AIProfiles/Edit.razor b/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/AIProfiles/Edit.razor index 398c14b9..ab13c4f2 100644 --- a/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/AIProfiles/Edit.razor +++ b/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/AIProfiles/Edit.razor @@ -1045,73 +1045,34 @@ else if (_model != null)
-
Prompt Security Override
+
Anti-Spam Throttle Override

- Override the site-level prompt security defaults for this profile. Leave options at "Use default" to inherit the global settings. + Override the site-level anti-spam throttle limits for this profile. Leave a field empty to inherit the global setting.

- - - - - - -
Override whether the security layer is active for this profile.
-
- -
- - - - - - -
- -
- - - - - - + + +
Leave empty to use the site default. Set to 0 to disable message throttling for this profile.
- - - - - - + + +
Leave empty to use the site default.
- - - - - - + + +
Leave empty to use the site default. Set to 0 to disable anonymous session-start throttling for this profile.
- - + +
Leave empty to use the site default.
- -
- - - - - - - - -
diff --git a/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/Templates/Create.razor b/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/Templates/Create.razor index 39a8af33..e1bf719d 100644 --- a/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/Templates/Create.razor +++ b/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/Templates/Create.razor @@ -543,70 +543,32 @@

-
Prompt Security Override
-

Override the site-level prompt security defaults for profiles created from this template.

+
Anti-Spam Throttle Override
+

Override the site-level anti-spam throttle limits for profiles created from this template. Leave a field empty to inherit the global setting.

- - - - - - -
- -
- - - - - - -
- -
- - - - - - + + +
Leave empty to use the site default. Set to 0 to disable message throttling.
- - - - - - + + +
Leave empty to use the site default.
- - - - - - + + +
Leave empty to use the site default. Set to 0 to disable anonymous session-start throttling.
- - + +
Leave empty to use the site default.
- -
- - - - - - - - -
} diff --git a/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/Templates/Edit.razor b/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/Templates/Edit.razor index a9f3d498..ddf759d0 100644 --- a/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/Templates/Edit.razor +++ b/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/AI/Templates/Edit.razor @@ -562,70 +562,32 @@
Prevent modification of the system message after creation.

-
Prompt Security Override
-

Override the site-level prompt security defaults for profiles created from this template.

+
Anti-Spam Throttle Override
+

Override the site-level anti-spam throttle limits for profiles created from this template. Leave a field empty to inherit the global setting.

- - - - - - -
- -
- - - - - - -
- -
- - - - - - + + +
Leave empty to use the site default. Set to 0 to disable message throttling.
- - - - - - + + +
Leave empty to use the site default.
- - - - - - + + +
Leave empty to use the site default. Set to 0 to disable anonymous session-start throttling.
- - + +
Leave empty to use the site default.
- -
- - - - - - - - -
} diff --git a/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/Admin/Settings/Index.razor b/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/Admin/Settings/Index.razor index 3023cf55..76a0970c 100644 --- a/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/Admin/Settings/Index.razor +++ b/src/Startup/CrestApps.Core.Blazor.Web/Components/Pages/Admin/Settings/Index.razor @@ -294,7 +294,7 @@ else
- +
Master switch for all prompt security features. When disabled, none of the security defenses below are active.
@@ -304,7 +304,7 @@ else
- +
Detects and blocks known prompt injection patterns such as "ignore previous instructions", persona hijacking, and role impersonation.
@@ -312,7 +312,7 @@ else
- +
Scans AI responses for leaked system prompts, tool configurations, or internal data before delivering to the user.
@@ -320,7 +320,7 @@ else
- +
Prepends a hardened security instruction to system prompts that instructs the model to never reveal its configuration.
@@ -328,7 +328,7 @@ else
- +
Wraps user messages with boundary markers to help the model distinguish user content from system instructions.
@@ -336,13 +336,13 @@ else
- +
Logs blocked prompts and security events for forensic investigation of injection attempts.
- + @if (_errors.TryGetValue(nameof(_model.SecurityMaxPromptLength), out var maxLenErr)) { @@ -352,7 +352,7 @@ else
- + @@ -374,7 +374,7 @@ else
- + @if (_errors.TryGetValue(nameof(_model.SecurityMaxMessagesPerWindow), out var maxMessagesErr)) { @@ -384,7 +384,7 @@ else
- + @if (_errors.TryGetValue(nameof(_model.SecurityRateLimitWindowSeconds), out var messageWindowErr)) { @@ -394,7 +394,7 @@ else
- + @if (_errors.TryGetValue(nameof(_model.SecurityMaxAnonymousSessionsPerWindow), out var maxSessionsErr)) { @@ -404,7 +404,7 @@ else
- + @if (_errors.TryGetValue(nameof(_model.SecurityAnonymousSessionRateLimitWindowSeconds), out var anonymousWindowErr)) { diff --git a/src/Startup/CrestApps.Core.Blazor.Web/ViewModels/AIProfileViewModel.cs b/src/Startup/CrestApps.Core.Blazor.Web/ViewModels/AIProfileViewModel.cs index 81b1116c..06272bff 100644 --- a/src/Startup/CrestApps.Core.Blazor.Web/ViewModels/AIProfileViewModel.cs +++ b/src/Startup/CrestApps.Core.Blazor.Web/ViewModels/AIProfileViewModel.cs @@ -180,20 +180,14 @@ public sealed class AIProfileViewModel public int CopilotAuthenticationType { get; set; } - // Prompt Security override (per-profile) - public string SecurityIsEnabled { get; set; } = string.Empty; + // Anti-spam throttle overrides (per-profile). Leave blank to inherit the site-wide default. + public int? SecurityMaxMessagesPerWindow { get; set; } - public string SecurityEnableInjectionDetection { get; set; } = string.Empty; + public int? SecurityRateLimitWindowSeconds { get; set; } - public string SecurityEnableOutputFiltering { get; set; } = string.Empty; + public int? SecurityMaxAnonymousSessionsPerWindow { get; set; } - public string SecurityEnableSecurityPreamble { get; set; } = string.Empty; - - public string SecurityEnableInputDelimiters { get; set; } = string.Empty; - - public int? SecurityMaxPromptLength { get; set; } - - public string SecurityBlockingThreshold { get; set; } = string.Empty; + public int? SecurityAnonymousSessionRateLimitWindowSeconds { get; set; } // Dropdown options (populated at load time) public List> DataSources { get; set; } = []; @@ -382,13 +376,14 @@ public static AIProfileViewModel FromProfile(AIProfile profile) if (profile.TryGetSettings(out var securitySettings)) { - vm.SecurityIsEnabled = securitySettings.IsEnabled?.ToString() ?? string.Empty; - vm.SecurityEnableInjectionDetection = securitySettings.EnableInjectionDetection?.ToString() ?? string.Empty; - vm.SecurityEnableOutputFiltering = securitySettings.EnableOutputFiltering?.ToString() ?? string.Empty; - vm.SecurityEnableSecurityPreamble = securitySettings.EnableSecurityPreamble?.ToString() ?? string.Empty; - vm.SecurityEnableInputDelimiters = securitySettings.EnableInputDelimiters?.ToString() ?? string.Empty; - vm.SecurityMaxPromptLength = securitySettings.MaxPromptLength; - vm.SecurityBlockingThreshold = securitySettings.BlockingThreshold?.ToString() ?? string.Empty; + vm.SecurityMaxMessagesPerWindow = securitySettings.MaxMessagesPerWindow; + vm.SecurityRateLimitWindowSeconds = securitySettings.RateLimitWindow.HasValue + ? (int)Math.Round(securitySettings.RateLimitWindow.Value.TotalSeconds) + : null; + vm.SecurityMaxAnonymousSessionsPerWindow = securitySettings.MaxAnonymousSessionsPerWindow; + vm.SecurityAnonymousSessionRateLimitWindowSeconds = securitySettings.AnonymousSessionRateLimitWindow.HasValue + ? (int)Math.Round(securitySettings.AnonymousSessionRateLimitWindow.Value.TotalSeconds) + : null; } return vm; @@ -616,16 +611,17 @@ public void ApplyTo(AIProfile profile) profile.Remove(); } - // Per-profile prompt security override. + // Per-profile anti-spam throttle override. profile.WithSettings(new PromptSecurityProfileSettings { - IsEnabled = bool.TryParse(SecurityIsEnabled, out var se) ? se : null, - EnableInjectionDetection = bool.TryParse(SecurityEnableInjectionDetection, out var sid) ? sid : null, - EnableOutputFiltering = bool.TryParse(SecurityEnableOutputFiltering, out var sof) ? sof : null, - EnableSecurityPreamble = bool.TryParse(SecurityEnableSecurityPreamble, out var ssp) ? ssp : null, - EnableInputDelimiters = bool.TryParse(SecurityEnableInputDelimiters, out var sdl) ? sdl : null, - MaxPromptLength = SecurityMaxPromptLength, - BlockingThreshold = Enum.TryParse(SecurityBlockingThreshold, out var sbt) ? sbt : null, + MaxMessagesPerWindow = SecurityMaxMessagesPerWindow, + RateLimitWindow = SecurityRateLimitWindowSeconds.HasValue + ? TimeSpan.FromSeconds(SecurityRateLimitWindowSeconds.Value) + : null, + MaxAnonymousSessionsPerWindow = SecurityMaxAnonymousSessionsPerWindow, + AnonymousSessionRateLimitWindow = SecurityAnonymousSessionRateLimitWindowSeconds.HasValue + ? TimeSpan.FromSeconds(SecurityAnonymousSessionRateLimitWindowSeconds.Value) + : null, }); } } diff --git a/src/Startup/CrestApps.Core.Blazor.Web/ViewModels/AITemplateViewModel.cs b/src/Startup/CrestApps.Core.Blazor.Web/ViewModels/AITemplateViewModel.cs index 4e08c5f3..a4cd4890 100644 --- a/src/Startup/CrestApps.Core.Blazor.Web/ViewModels/AITemplateViewModel.cs +++ b/src/Startup/CrestApps.Core.Blazor.Web/ViewModels/AITemplateViewModel.cs @@ -170,20 +170,14 @@ public sealed class AITemplateViewModel public CopilotAuthenticationType CopilotAuthenticationType { get; set; } - // Security override properties - public string SecurityIsEnabled { get; set; } = string.Empty; + // Anti-spam throttle overrides. Leave blank to inherit the site-wide default. + public int? SecurityMaxMessagesPerWindow { get; set; } - public string SecurityEnableInjectionDetection { get; set; } = string.Empty; + public int? SecurityRateLimitWindowSeconds { get; set; } - public string SecurityEnableOutputFiltering { get; set; } = string.Empty; + public int? SecurityMaxAnonymousSessionsPerWindow { get; set; } - public string SecurityEnableSecurityPreamble { get; set; } = string.Empty; - - public string SecurityEnableInputDelimiters { get; set; } = string.Empty; - - public int? SecurityMaxPromptLength { get; set; } - - public string SecurityBlockingThreshold { get; set; } = string.Empty; + public int? SecurityAnonymousSessionRateLimitWindowSeconds { get; set; } // Dropdown options (populated at load time) public List> ChatDeployments { get; set; } = []; @@ -364,13 +358,14 @@ public static AITemplateViewModel FromTemplate(AIProfileTemplate template) if (template.TryGet(out var securitySettings)) { - model.SecurityIsEnabled = securitySettings.IsEnabled?.ToString() ?? string.Empty; - model.SecurityEnableInjectionDetection = securitySettings.EnableInjectionDetection?.ToString() ?? string.Empty; - model.SecurityEnableOutputFiltering = securitySettings.EnableOutputFiltering?.ToString() ?? string.Empty; - model.SecurityEnableSecurityPreamble = securitySettings.EnableSecurityPreamble?.ToString() ?? string.Empty; - model.SecurityEnableInputDelimiters = securitySettings.EnableInputDelimiters?.ToString() ?? string.Empty; - model.SecurityMaxPromptLength = securitySettings.MaxPromptLength; - model.SecurityBlockingThreshold = securitySettings.BlockingThreshold?.ToString() ?? string.Empty; + model.SecurityMaxMessagesPerWindow = securitySettings.MaxMessagesPerWindow; + model.SecurityRateLimitWindowSeconds = securitySettings.RateLimitWindow.HasValue + ? (int)Math.Round(securitySettings.RateLimitWindow.Value.TotalSeconds) + : null; + model.SecurityMaxAnonymousSessionsPerWindow = securitySettings.MaxAnonymousSessionsPerWindow; + model.SecurityAnonymousSessionRateLimitWindowSeconds = securitySettings.AnonymousSessionRateLimitWindow.HasValue + ? (int)Math.Round(securitySettings.AnonymousSessionRateLimitWindow.Value.TotalSeconds) + : null; } } @@ -605,22 +600,20 @@ public void ApplyTo(AIProfileTemplate template) var securitySettings = new PromptSecurityProfileSettings { - IsEnabled = bool.TryParse(SecurityIsEnabled, out var se) ? se : null, - EnableInjectionDetection = bool.TryParse(SecurityEnableInjectionDetection, out var sid) ? sid : null, - EnableOutputFiltering = bool.TryParse(SecurityEnableOutputFiltering, out var sof) ? sof : null, - EnableSecurityPreamble = bool.TryParse(SecurityEnableSecurityPreamble, out var ssp) ? ssp : null, - EnableInputDelimiters = bool.TryParse(SecurityEnableInputDelimiters, out var sdl) ? sdl : null, - MaxPromptLength = SecurityMaxPromptLength, - BlockingThreshold = Enum.TryParse(SecurityBlockingThreshold, out var sbt) ? sbt : null, + MaxMessagesPerWindow = SecurityMaxMessagesPerWindow, + RateLimitWindow = SecurityRateLimitWindowSeconds.HasValue + ? TimeSpan.FromSeconds(SecurityRateLimitWindowSeconds.Value) + : null, + MaxAnonymousSessionsPerWindow = SecurityMaxAnonymousSessionsPerWindow, + AnonymousSessionRateLimitWindow = SecurityAnonymousSessionRateLimitWindowSeconds.HasValue + ? TimeSpan.FromSeconds(SecurityAnonymousSessionRateLimitWindowSeconds.Value) + : null, }; - if (securitySettings.IsEnabled.HasValue - || securitySettings.EnableInjectionDetection.HasValue - || securitySettings.EnableOutputFiltering.HasValue - || securitySettings.EnableSecurityPreamble.HasValue - || securitySettings.EnableInputDelimiters.HasValue - || securitySettings.MaxPromptLength.HasValue - || securitySettings.BlockingThreshold.HasValue) + if (securitySettings.MaxMessagesPerWindow.HasValue + || securitySettings.RateLimitWindow.HasValue + || securitySettings.MaxAnonymousSessionsPerWindow.HasValue + || securitySettings.AnonymousSessionRateLimitWindow.HasValue) { template.Put(securitySettings); } diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AIProfileViewModel.cs b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AIProfileViewModel.cs index d1e3c7e0..f53d47fd 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AIProfileViewModel.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AIProfileViewModel.cs @@ -164,20 +164,14 @@ public sealed class AIProfileViewModel public int CopilotAuthenticationType { get; set; } - // Prompt Security override (per-profile) - public bool? SecurityIsEnabled { get; set; } + // Anti-spam throttle overrides (per-profile). Leave blank to inherit the site-wide default. + public int? SecurityMaxMessagesPerWindow { get; set; } - public bool? SecurityEnableInjectionDetection { get; set; } + public int? SecurityRateLimitWindowSeconds { get; set; } - public bool? SecurityEnableOutputFiltering { get; set; } + public int? SecurityMaxAnonymousSessionsPerWindow { get; set; } - public bool? SecurityEnableSecurityPreamble { get; set; } - - public bool? SecurityEnableInputDelimiters { get; set; } - - public int? SecurityMaxPromptLength { get; set; } - - public PromptRiskLevel? SecurityBlockingThreshold { get; set; } + public int? SecurityAnonymousSessionRateLimitWindowSeconds { get; set; } [BindNever] public IEnumerable DataSources { get; set; } = []; @@ -369,13 +363,14 @@ public static AIProfileViewModel FromProfile(AIProfile profile) if (profile.TryGetSettings(out var securitySettings)) { - vm.SecurityIsEnabled = securitySettings.IsEnabled; - vm.SecurityEnableInjectionDetection = securitySettings.EnableInjectionDetection; - vm.SecurityEnableOutputFiltering = securitySettings.EnableOutputFiltering; - vm.SecurityEnableSecurityPreamble = securitySettings.EnableSecurityPreamble; - vm.SecurityEnableInputDelimiters = securitySettings.EnableInputDelimiters; - vm.SecurityMaxPromptLength = securitySettings.MaxPromptLength; - vm.SecurityBlockingThreshold = securitySettings.BlockingThreshold; + vm.SecurityMaxMessagesPerWindow = securitySettings.MaxMessagesPerWindow; + vm.SecurityRateLimitWindowSeconds = securitySettings.RateLimitWindow.HasValue + ? (int)Math.Round(securitySettings.RateLimitWindow.Value.TotalSeconds) + : null; + vm.SecurityMaxAnonymousSessionsPerWindow = securitySettings.MaxAnonymousSessionsPerWindow; + vm.SecurityAnonymousSessionRateLimitWindowSeconds = securitySettings.AnonymousSessionRateLimitWindow.HasValue + ? (int)Math.Round(securitySettings.AnonymousSessionRateLimitWindow.Value.TotalSeconds) + : null; } return vm; @@ -605,16 +600,17 @@ public void ApplyTo(AIProfile profile) profile.Remove(); } - // Per-profile prompt security override. + // Per-profile anti-spam throttle override. profile.WithSettings(new PromptSecurityProfileSettings { - IsEnabled = SecurityIsEnabled, - EnableInjectionDetection = SecurityEnableInjectionDetection, - EnableOutputFiltering = SecurityEnableOutputFiltering, - EnableSecurityPreamble = SecurityEnableSecurityPreamble, - EnableInputDelimiters = SecurityEnableInputDelimiters, - MaxPromptLength = SecurityMaxPromptLength, - BlockingThreshold = SecurityBlockingThreshold, + MaxMessagesPerWindow = SecurityMaxMessagesPerWindow, + RateLimitWindow = SecurityRateLimitWindowSeconds.HasValue + ? TimeSpan.FromSeconds(SecurityRateLimitWindowSeconds.Value) + : null, + MaxAnonymousSessionsPerWindow = SecurityMaxAnonymousSessionsPerWindow, + AnonymousSessionRateLimitWindow = SecurityAnonymousSessionRateLimitWindowSeconds.HasValue + ? TimeSpan.FromSeconds(SecurityAnonymousSessionRateLimitWindowSeconds.Value) + : null, }); } } diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AITemplateViewModel.cs b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AITemplateViewModel.cs index 6d01d84b..34849c72 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AITemplateViewModel.cs +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/ViewModels/AITemplateViewModel.cs @@ -149,20 +149,14 @@ public sealed class AITemplateViewModel public CopilotAuthenticationType CopilotAuthenticationType { get; set; } - // Prompt Security override (per-profile). - public bool? SecurityIsEnabled { get; set; } + // Anti-spam throttle overrides. Leave blank to inherit the site-wide default. + public int? SecurityMaxMessagesPerWindow { get; set; } - public bool? SecurityEnableInjectionDetection { get; set; } + public int? SecurityRateLimitWindowSeconds { get; set; } - public bool? SecurityEnableOutputFiltering { get; set; } + public int? SecurityMaxAnonymousSessionsPerWindow { get; set; } - public bool? SecurityEnableSecurityPreamble { get; set; } - - public bool? SecurityEnableInputDelimiters { get; set; } - - public int? SecurityMaxPromptLength { get; set; } - - public PromptRiskLevel? SecurityBlockingThreshold { get; set; } + public int? SecurityAnonymousSessionRateLimitWindowSeconds { get; set; } [BindNever] public IEnumerable ChatDeployments { get; set; } = []; @@ -348,13 +342,14 @@ public static AITemplateViewModel FromTemplate(AIProfileTemplate template) if (template.TryGet(out var securitySettings)) { - model.SecurityIsEnabled = securitySettings.IsEnabled; - model.SecurityEnableInjectionDetection = securitySettings.EnableInjectionDetection; - model.SecurityEnableOutputFiltering = securitySettings.EnableOutputFiltering; - model.SecurityEnableSecurityPreamble = securitySettings.EnableSecurityPreamble; - model.SecurityEnableInputDelimiters = securitySettings.EnableInputDelimiters; - model.SecurityMaxPromptLength = securitySettings.MaxPromptLength; - model.SecurityBlockingThreshold = securitySettings.BlockingThreshold; + model.SecurityMaxMessagesPerWindow = securitySettings.MaxMessagesPerWindow; + model.SecurityRateLimitWindowSeconds = securitySettings.RateLimitWindow.HasValue + ? (int)Math.Round(securitySettings.RateLimitWindow.Value.TotalSeconds) + : null; + model.SecurityMaxAnonymousSessionsPerWindow = securitySettings.MaxAnonymousSessionsPerWindow; + model.SecurityAnonymousSessionRateLimitWindowSeconds = securitySettings.AnonymousSessionRateLimitWindow.HasValue + ? (int)Math.Round(securitySettings.AnonymousSessionRateLimitWindow.Value.TotalSeconds) + : null; } } @@ -583,13 +578,14 @@ public void ApplyTo(AIProfileTemplate template) template.Put(new PromptSecurityProfileSettings { - IsEnabled = SecurityIsEnabled, - EnableInjectionDetection = SecurityEnableInjectionDetection, - EnableOutputFiltering = SecurityEnableOutputFiltering, - EnableSecurityPreamble = SecurityEnableSecurityPreamble, - EnableInputDelimiters = SecurityEnableInputDelimiters, - MaxPromptLength = SecurityMaxPromptLength, - BlockingThreshold = SecurityBlockingThreshold, + MaxMessagesPerWindow = SecurityMaxMessagesPerWindow, + RateLimitWindow = SecurityRateLimitWindowSeconds.HasValue + ? TimeSpan.FromSeconds(SecurityRateLimitWindowSeconds.Value) + : null, + MaxAnonymousSessionsPerWindow = SecurityMaxAnonymousSessionsPerWindow, + AnonymousSessionRateLimitWindow = SecurityAnonymousSessionRateLimitWindowSeconds.HasValue + ? TimeSpan.FromSeconds(SecurityAnonymousSessionRateLimitWindowSeconds.Value) + : null, }); } else diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Create.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Create.cshtml index b27c0e50..b8837319 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Create.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Create.cshtml @@ -848,73 +848,34 @@

-
Prompt Security Override
+
Anti-Spam Throttle Override

- Override the site-level prompt security defaults for this profile. Leave options at "Use default" to inherit the global settings. + Override the site-level anti-spam throttle limits for this profile. Leave a field empty to inherit the global setting.

- - -
Override whether the security layer is active for this profile.
-
- -
- - -
- -
- - + + +
Leave empty to use the site default. Set to 0 to disable message throttling for this profile.
- - + + +
Leave empty to use the site default.
- - + + +
Leave empty to use the site default. Set to 0 to disable anonymous session-start throttling for this profile.
- - + +
Leave empty to use the site default.
- -
- - -
diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Edit.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Edit.cshtml index 631afacb..92c6603c 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Edit.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AIProfile/Edit.cshtml @@ -987,73 +987,34 @@
-
Prompt Security Override
+
Anti-Spam Throttle Override

- Override the site-level prompt security defaults for this profile. Leave options at "Use default" to inherit the global settings. + Override the site-level anti-spam throttle limits for this profile. Leave a field empty to inherit the global setting.

- - -
Override whether the security layer is active for this profile.
-
- -
- - -
- -
- - + + +
Leave empty to use the site default. Set to 0 to disable message throttling for this profile.
- - + + +
Leave empty to use the site default.
- - + + +
Leave empty to use the site default. Set to 0 to disable anonymous session-start throttling for this profile.
- - + +
Leave empty to use the site default.
- -
- - -
diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AITemplate/Create.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AITemplate/Create.cshtml index 789600df..bcd5b636 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AITemplate/Create.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AITemplate/Create.cshtml @@ -803,73 +803,34 @@
-
Prompt Security Override
+
Anti-Spam Throttle Override

- Override the site-level prompt security defaults for profiles created from this template. Leave options at "Use default" to inherit the global settings. + Override the site-level anti-spam throttle limits for profiles created from this template. Leave a field empty to inherit the global setting.

- - -
Override whether the security layer is active for profiles using this template.
-
- -
- - -
- -
- - + + +
Leave empty to use the site default. Set to 0 to disable message throttling for profiles using this template.
- - + + +
Leave empty to use the site default.
- - + + +
Leave empty to use the site default. Set to 0 to disable anonymous session-start throttling for profiles using this template.
- - + +
Leave empty to use the site default.
- -
- - -
diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AITemplate/Edit.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AITemplate/Edit.cshtml index b24fba84..39388dde 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AITemplate/Edit.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/AI/Views/AITemplate/Edit.cshtml @@ -874,73 +874,34 @@
-
Prompt Security Override
+
Anti-Spam Throttle Override

- Override the site-level prompt security defaults for profiles created from this template. Leave options at "Use default" to inherit the global settings. + Override the site-level anti-spam throttle limits for profiles created from this template. Leave a field empty to inherit the global setting.

- - -
Override whether the security layer is active for profiles using this template.
-
- -
- - -
- -
- - + + +
Leave empty to use the site default. Set to 0 to disable message throttling for profiles using this template.
- - + + +
Leave empty to use the site default.
- - + + +
Leave empty to use the site default. Set to 0 to disable anonymous session-start throttling for profiles using this template.
- - + +
Leave empty to use the site default.
- -
- - -
diff --git a/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Views/Settings/Index.cshtml b/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Views/Settings/Index.cshtml index 80899571..a3caf9b0 100644 --- a/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Views/Settings/Index.cshtml +++ b/src/Startup/CrestApps.Core.Mvc.Web/Areas/Admin/Views/Settings/Index.cshtml @@ -216,7 +216,7 @@
- +
Master switch for all prompt security features. When disabled, none of the security defenses below are active.
@@ -225,7 +225,7 @@
- +
Detects and blocks known prompt injection patterns such as "ignore previous instructions", persona hijacking, and role impersonation.
@@ -233,7 +233,7 @@
- +
Scans AI responses for leaked system prompts, tool configurations, or internal data before delivering to the user.
@@ -241,7 +241,7 @@
- +
Prepends a hardened security instruction to system prompts that instructs the model to never reveal its configuration.
@@ -249,7 +249,7 @@
- +
Wraps user messages with boundary markers to help the model distinguish user content from system instructions.
@@ -257,27 +257,27 @@
- +
Logs blocked prompts and security events for forensic investigation of injection attempts.
- +
Prompts exceeding this length are automatically blocked. Default is 8,000 characters.
- +
Controls the minimum risk level at which prompts are blocked. Lower thresholds are stricter.
- +
Anonymous and authenticated chat prompts are throttled after this many messages. Set to 0 to disable message throttling.
- +
Determines how long message-throttle counters remain active.
- +
Limits how many new anonymous widget sessions can be started from the same visitor/IP bucket. Set to 0 to disable session-start throttling.
- +
Determines how long anonymous session-start counters remain active for the ASP.NET Core and hub session throttles.
@@ -698,6 +698,13 @@