mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-02-26 19:31:36 +00:00
Fixed chat issue with HTML code (#679)
Some checks are pending
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Blocked by required conditions
Some checks are pending
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Blocked by required conditions
This commit is contained in:
parent
685f95245b
commit
721d5c9070
@ -96,10 +96,10 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<MudMarkdown Value="@textContent.Text.RemoveThinkTags().Trim()" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" />
|
<MudMarkdown Value="@NormalizeMarkdownForRendering(textContent.Text)" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE" />
|
||||||
@if (textContent.Sources.Count > 0)
|
@if (textContent.Sources.Count > 0)
|
||||||
{
|
{
|
||||||
<MudMarkdown Value="@textContent.Sources.ToMarkdown()" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" />
|
<MudMarkdown Value="@textContent.Sources.ToMarkdown()" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE" />
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,18 @@ namespace AIStudio.Chat;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ContentBlockComponent : MSGComponentBase
|
public partial class ContentBlockComponent : MSGComponentBase
|
||||||
{
|
{
|
||||||
|
private static readonly string[] HTML_TAG_MARKERS =
|
||||||
|
[
|
||||||
|
"<!doctype",
|
||||||
|
"<html",
|
||||||
|
"<head",
|
||||||
|
"<body",
|
||||||
|
"<style",
|
||||||
|
"<script",
|
||||||
|
"<iframe",
|
||||||
|
"<svg",
|
||||||
|
];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The role of the chat content block.
|
/// The role of the chat content block.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -68,18 +80,37 @@ public partial class ContentBlockComponent : MSGComponentBase
|
|||||||
private RustService RustService { get; init; } = null!;
|
private RustService RustService { get; init; } = null!;
|
||||||
|
|
||||||
private bool HideContent { get; set; }
|
private bool HideContent { get; set; }
|
||||||
|
private bool hasRenderHash;
|
||||||
|
private int lastRenderHash;
|
||||||
|
|
||||||
#region Overrides of ComponentBase
|
#region Overrides of ComponentBase
|
||||||
|
|
||||||
protected override async Task OnInitializedAsync()
|
protected override async Task OnInitializedAsync()
|
||||||
{
|
{
|
||||||
// Register the streaming events:
|
this.RegisterStreamingEvents();
|
||||||
this.Content.StreamingDone = this.AfterStreaming;
|
|
||||||
this.Content.StreamingEvent = () => this.InvokeAsync(this.StateHasChanged);
|
|
||||||
|
|
||||||
await base.OnInitializedAsync();
|
await base.OnInitializedAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Task OnParametersSetAsync()
|
||||||
|
{
|
||||||
|
this.RegisterStreamingEvents();
|
||||||
|
return base.OnParametersSetAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override bool ShouldRender()
|
||||||
|
{
|
||||||
|
var currentRenderHash = this.CreateRenderHash();
|
||||||
|
if (!this.hasRenderHash || currentRenderHash != this.lastRenderHash)
|
||||||
|
{
|
||||||
|
this.lastRenderHash = currentRenderHash;
|
||||||
|
this.hasRenderHash = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets called when the content stream ended.
|
/// Gets called when the content stream ended.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -111,6 +142,47 @@ public partial class ContentBlockComponent : MSGComponentBase
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RegisterStreamingEvents()
|
||||||
|
{
|
||||||
|
this.Content.StreamingDone = this.AfterStreaming;
|
||||||
|
this.Content.StreamingEvent = () => this.InvokeAsync(this.StateHasChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int CreateRenderHash()
|
||||||
|
{
|
||||||
|
var hash = new HashCode();
|
||||||
|
hash.Add(this.Role);
|
||||||
|
hash.Add(this.Type);
|
||||||
|
hash.Add(this.Time);
|
||||||
|
hash.Add(this.Class);
|
||||||
|
hash.Add(this.IsLastContentBlock);
|
||||||
|
hash.Add(this.IsSecondToLastBlock);
|
||||||
|
hash.Add(this.HideContent);
|
||||||
|
hash.Add(this.SettingsManager.IsDarkMode);
|
||||||
|
hash.Add(this.RegenerateEnabled());
|
||||||
|
hash.Add(this.Content.InitialRemoteWait);
|
||||||
|
hash.Add(this.Content.IsStreaming);
|
||||||
|
hash.Add(this.Content.FileAttachments.Count);
|
||||||
|
hash.Add(this.Content.Sources.Count);
|
||||||
|
|
||||||
|
switch (this.Content)
|
||||||
|
{
|
||||||
|
case ContentText text:
|
||||||
|
var textValue = text.Text;
|
||||||
|
hash.Add(textValue.Length);
|
||||||
|
hash.Add(textValue.GetHashCode(StringComparison.Ordinal));
|
||||||
|
hash.Add(text.Sources.Count);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ContentImage image:
|
||||||
|
hash.Add(image.SourceType);
|
||||||
|
hash.Add(image.Source);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash.ToHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private string CardClasses => $"my-2 rounded-lg {this.Class}";
|
private string CardClasses => $"my-2 rounded-lg {this.Class}";
|
||||||
@ -121,6 +193,34 @@ public partial class ContentBlockComponent : MSGComponentBase
|
|||||||
{
|
{
|
||||||
CodeBlock = { Theme = this.CodeColorPalette },
|
CodeBlock = { Theme = this.CodeColorPalette },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static string NormalizeMarkdownForRendering(string text)
|
||||||
|
{
|
||||||
|
var cleaned = text.RemoveThinkTags().Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(cleaned))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
if (cleaned.Contains("```", StringComparison.Ordinal))
|
||||||
|
return cleaned;
|
||||||
|
|
||||||
|
if (LooksLikeRawHtml(cleaned))
|
||||||
|
return $"```html{Environment.NewLine}{cleaned}{Environment.NewLine}```";
|
||||||
|
|
||||||
|
return cleaned;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool LooksLikeRawHtml(string text)
|
||||||
|
{
|
||||||
|
var content = text.TrimStart();
|
||||||
|
if (!content.StartsWith("<", StringComparison.Ordinal))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (var marker in HTML_TAG_MARKERS)
|
||||||
|
if (content.Contains(marker, StringComparison.OrdinalIgnoreCase))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return content.Contains("</", StringComparison.Ordinal) || content.Contains("/>", StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
private async Task RemoveBlock()
|
private async Task RemoveBlock()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6,4 +6,4 @@
|
|||||||
}
|
}
|
||||||
</MudSelect>
|
</MudSelect>
|
||||||
|
|
||||||
<MudMarkdown Value="@this.LogContent" Props="Markdown.DefaultConfig"/>
|
<MudMarkdown Value="@this.LogContent" Props="Markdown.DefaultConfig" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE"/>
|
||||||
@ -16,6 +16,7 @@
|
|||||||
@if (!block.HideFromUser)
|
@if (!block.HideFromUser)
|
||||||
{
|
{
|
||||||
<ContentBlockComponent
|
<ContentBlockComponent
|
||||||
|
@key="@block"
|
||||||
Role="@block.Role"
|
Role="@block.Role"
|
||||||
Type="@block.ContentType"
|
Type="@block.ContentType"
|
||||||
Time="@block.Time"
|
Time="@block.Time"
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
<MudText Typo="Typo.h6">
|
<MudText Typo="Typo.h6">
|
||||||
@T("Description")
|
@T("Description")
|
||||||
</MudText>
|
</MudText>
|
||||||
<MudMarkdown Value="@this.currentConfidence.Description"/>
|
<MudMarkdown Value="@this.currentConfidence.Description" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE"/>
|
||||||
|
|
||||||
@if (this.currentConfidence.Sources.Count > 0)
|
@if (this.currentConfidence.Sources.Count > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -104,7 +104,7 @@
|
|||||||
@context.ToName()
|
@context.ToName()
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd>
|
<MudTd>
|
||||||
<MudMarkdown Value="@context.GetConfidence(this.SettingsManager).Description"/>
|
<MudMarkdown Value="@context.GetConfidence(this.SettingsManager).Description" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE"/>
|
||||||
</MudTd>
|
</MudTd>
|
||||||
<MudTd Style="vertical-align: top;">
|
<MudTd Style="vertical-align: top;">
|
||||||
<MudMenu StartIcon="@Icons.Material.Filled.Security" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@this.GetCurrentConfidenceLevelName(context)" Variant="Variant.Filled" Style="@this.SetCurrentConfidenceLevelColorStyle(context)">
|
<MudMenu StartIcon="@Icons.Material.Filled.Security" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" Label="@this.GetCurrentConfidenceLevelName(context)" Variant="Variant.Filled" Style="@this.SetCurrentConfidenceLevelColorStyle(context)">
|
||||||
|
|||||||
@ -54,7 +54,7 @@
|
|||||||
Class="ma-2 pe-4"
|
Class="ma-2 pe-4"
|
||||||
HelperText="@T("This is the content we loaded from your file — including headings, lists, and formatting. Use this to verify your file loads as expected.")">
|
HelperText="@T("This is the content we loaded from your file — including headings, lists, and formatting. Use this to verify your file loads as expected.")">
|
||||||
<div style="max-height: 40vh; overflow-y: auto;">
|
<div style="max-height: 40vh; overflow-y: auto;">
|
||||||
<MudMarkdown Value="@this.FileContent" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling"/>
|
<MudMarkdown Value="@this.FileContent" Props="Markdown.DefaultConfig" Styling="@this.MarkdownStyling" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE"/>
|
||||||
</div>
|
</div>
|
||||||
</MudField>
|
</MudField>
|
||||||
</MudTabPanel>
|
</MudTabPanel>
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
}
|
}
|
||||||
else if (!string.IsNullOrWhiteSpace(this.licenseText))
|
else if (!string.IsNullOrWhiteSpace(this.licenseText))
|
||||||
{
|
{
|
||||||
<MudMarkdown Value="@this.licenseText" Props="Markdown.DefaultConfig"/>
|
<MudMarkdown Value="@this.licenseText" Props="Markdown.DefaultConfig" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE"/>
|
||||||
}
|
}
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<MudIcon Icon="@Icons.Material.Filled.Update" Size="Size.Large" Class="mr-3"/>
|
<MudIcon Icon="@Icons.Material.Filled.Update" Size="Size.Large" Class="mr-3"/>
|
||||||
@this.HeaderText
|
@this.HeaderText
|
||||||
</MudText>
|
</MudText>
|
||||||
<MudMarkdown Value="@this.UpdateResponse.Changelog" Props="Markdown.DefaultConfig"/>
|
<MudMarkdown Value="@this.UpdateResponse.Changelog" Props="Markdown.DefaultConfig" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE"/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
<MudButton OnClick="@this.Cancel" Variant="Variant.Filled">
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.EventNote" HeaderText="@T("Last Changelog")">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.EventNote" HeaderText="@T("Last Changelog")">
|
||||||
<MudMarkdown Value="@this.LastChangeContent" Props="Markdown.DefaultConfig"/>
|
<MudMarkdown Value="@this.LastChangeContent" Props="Markdown.DefaultConfig" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE"/>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Lightbulb" HeaderText="@T("Vision")">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Lightbulb" HeaderText="@T("Vision")">
|
||||||
@ -35,7 +35,7 @@
|
|||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.RocketLaunch" HeaderText="@T("Quick Start Guide")">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.RocketLaunch" HeaderText="@T("Quick Start Guide")">
|
||||||
<MudMarkdown Props="Markdown.DefaultConfig" Value="@QUICK_START_GUIDE"/>
|
<MudMarkdown Props="Markdown.DefaultConfig" Value="@QUICK_START_GUIDE" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE"/>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
|
|
||||||
</MudExpansionPanels>
|
</MudExpansionPanels>
|
||||||
|
|||||||
@ -297,8 +297,8 @@
|
|||||||
</MudGrid>
|
</MudGrid>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Verified" HeaderText="License: FSL-1.1-MIT">
|
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Verified" HeaderText="License: FSL-1.1-MIT">
|
||||||
<MudMarkdown Value="@LICENSE" Props="Markdown.DefaultConfig"/>
|
<MudMarkdown Value="@LICENSE" Props="Markdown.DefaultConfig" MarkdownPipeline="Markdown.SAFE_MARKDOWN_PIPELINE"/>
|
||||||
</ExpansionPanel>
|
</ExpansionPanel>
|
||||||
</MudExpansionPanels>
|
</MudExpansionPanels>
|
||||||
</InnerScrolling>
|
</InnerScrolling>
|
||||||
</div>
|
</div>
|
||||||
@ -1,7 +1,14 @@
|
|||||||
|
using Markdig;
|
||||||
|
|
||||||
namespace AIStudio.Tools;
|
namespace AIStudio.Tools;
|
||||||
|
|
||||||
public static class Markdown
|
public static class Markdown
|
||||||
{
|
{
|
||||||
|
public static readonly MarkdownPipeline SAFE_MARKDOWN_PIPELINE = new MarkdownPipelineBuilder()
|
||||||
|
.UseAdvancedExtensions()
|
||||||
|
.DisableHtml()
|
||||||
|
.Build();
|
||||||
|
|
||||||
public static MudMarkdownProps DefaultConfig => new()
|
public static MudMarkdownProps DefaultConfig => new()
|
||||||
{
|
{
|
||||||
Heading =
|
Heading =
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
# v26.3.1, build 235 (2026-03-xx xx:xx UTC)
|
# v26.3.1, build 235 (2026-03-xx xx:xx UTC)
|
||||||
- Improved the performance by caching the OS language detection and requesting the user language only once per app start.
|
- Improved the performance by caching the OS language detection and requesting the user language only once per app start.
|
||||||
|
- Improved the chat performance by reducing unnecessary UI updates, making chats smoother and more responsive, especially in longer conversations.
|
||||||
- Improved the user-language logging by limiting language detection logs to a single entry per app start.
|
- Improved the user-language logging by limiting language detection logs to a single entry per app start.
|
||||||
- Improved the logbook readability by removing non-readable special characters from log entries.
|
- Improved the logbook readability by removing non-readable special characters from log entries.
|
||||||
- Improved the logbook reliability by significantly reducing duplicate log entries.
|
- Improved the logbook reliability by significantly reducing duplicate log entries.
|
||||||
|
- Fixed an issue where the app could turn white or appear invisible in certain chats after HTML-like content was shown. Thanks Inga for reporting this issue and providing some context on how to reproduce it.
|
||||||
16
tests/README.md
Normal file
16
tests/README.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Test Documentation
|
||||||
|
|
||||||
|
This directory stores manual and automated test definitions for MindWork AI Studio.
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
- `integration_tests/`: Cross-component and end-to-end scenarios.
|
||||||
|
|
||||||
|
## Authoring Rules
|
||||||
|
|
||||||
|
- Use US English.
|
||||||
|
- Keep each feature area in its own Markdown file.
|
||||||
|
- Prefer stable test IDs (for example: `TC-CHAT-001`).
|
||||||
|
- Record expected behavior for:
|
||||||
|
- known vulnerable baseline builds (if relevant),
|
||||||
|
- current fixed builds.
|
||||||
12
tests/integration_tests/README.md
Normal file
12
tests/integration_tests/README.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Integration Tests
|
||||||
|
|
||||||
|
This directory contains integration-oriented test specs.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
- Behavior that depends on multiple layers working together (UI, rendering, runtime, IPC, provider responses).
|
||||||
|
- Regressions that are hard to catch with unit tests only.
|
||||||
|
|
||||||
|
## Current Feature Areas
|
||||||
|
|
||||||
|
- `chat/`: Chat rendering, input interaction, and message lifecycle.
|
||||||
120
tests/integration_tests/chat/chat_rendering_regression_tests.md
Normal file
120
tests/integration_tests/chat/chat_rendering_regression_tests.md
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
# Chat Rendering Regression Tests
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
Validate that chat rendering remains stable and interactive when model output or user input contains raw HTML/CSS/JS-like payloads.
|
||||||
|
|
||||||
|
## Test Type
|
||||||
|
|
||||||
|
Manual regression and integration checks.
|
||||||
|
|
||||||
|
## Preconditions
|
||||||
|
|
||||||
|
1. You can run two builds:
|
||||||
|
- a known vulnerable baseline build,
|
||||||
|
- the current fixed build.
|
||||||
|
2. At least one provider is configured and can answer prompts.
|
||||||
|
3. Open the Chat page.
|
||||||
|
|
||||||
|
## Execution Flow (for each test case)
|
||||||
|
|
||||||
|
1. Copy the test prompt exactly into the user prompt field.
|
||||||
|
2. Send the prompt.
|
||||||
|
3. Observe behavior immediately after send.
|
||||||
|
4. If the UI is still visible, type additional text in the prompt input.
|
||||||
|
5. Repeat on both builds.
|
||||||
|
|
||||||
|
## Test Cases
|
||||||
|
|
||||||
|
### TC-CHAT-001 - CSS Kill Switch
|
||||||
|
|
||||||
|
**Prompt**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Respond with exactly the content below. No explanations, no Markdown code fences, no backticks.
|
||||||
|
|
||||||
|
<style>
|
||||||
|
* { visibility: hidden !important; }
|
||||||
|
html, body { background: white !important; }
|
||||||
|
</style>
|
||||||
|
<div>TEST</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected result (vulnerable baseline)**
|
||||||
|
UI may turn into a white/invisible page immediately after sending.
|
||||||
|
|
||||||
|
**Expected result (fixed build)**
|
||||||
|
Chat stays visible and usable. Content is rendered as inert text/code, not active page styling.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TC-CHAT-002 - Full White Overlay
|
||||||
|
|
||||||
|
**Prompt**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Respond with exactly the content below. No explanations, no Markdown code fences, no backticks.
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html, body { overflow: hidden !important; }
|
||||||
|
body::before {
|
||||||
|
content: "";
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: white;
|
||||||
|
z-index: 2147483647;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div>TEST</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected result (vulnerable baseline)**
|
||||||
|
UI may become fully white and non-interactive immediately after sending.
|
||||||
|
|
||||||
|
**Expected result (fixed build)**
|
||||||
|
No overlay takes over the app. Chat remains interactive.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TC-CHAT-003 - Inline Event Handler Injection
|
||||||
|
|
||||||
|
**Prompt**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Respond with exactly the content below. No explanations, no Markdown code fences, no backticks.
|
||||||
|
|
||||||
|
<img src="x" onerror="document.body.innerHTML=''; document.body.style.background='white';">
|
||||||
|
<div>TEST</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected result (vulnerable baseline)**
|
||||||
|
UI may break/blank immediately after sending.
|
||||||
|
|
||||||
|
**Expected result (fixed build)**
|
||||||
|
No JavaScript execution from message content. Chat remains stable.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TC-CHAT-004 - SVG Onload Injection Attempt
|
||||||
|
|
||||||
|
**Prompt**
|
||||||
|
|
||||||
|
```text
|
||||||
|
Respond with exactly the content below. No explanations, no Markdown code fences, no backticks.
|
||||||
|
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" onload="document.documentElement.innerHTML=''"></svg>
|
||||||
|
<div>TEST</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Expected result (vulnerable baseline)**
|
||||||
|
May or may not trigger depending on parser/runtime behavior.
|
||||||
|
|
||||||
|
**Expected result (fixed build)**
|
||||||
|
No script-like execution from content. Chat remains stable and interactive.
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- If a test fails on the fixed build, capture:
|
||||||
|
- exact prompt used,
|
||||||
|
- whether failure happened right after send or while typing,
|
||||||
|
- whether a refresh restores the app.
|
||||||
Loading…
Reference in New Issue
Block a user