Centralize file extension validation logic

This commit is contained in:
Thorsten Sommer 2025-12-15 13:26:12 +01:00
parent 94e8ca7bac
commit a35a918ee5
Signed by: tsommer
GPG Key ID: 371BBA77A02C0108
3 changed files with 61 additions and 58 deletions

View File

@ -1,6 +1,7 @@
using AIStudio.Dialogs;
using AIStudio.Tools.Rust;
using AIStudio.Tools.Services;
using AIStudio.Tools.Validation;
using Microsoft.AspNetCore.Components;
@ -91,7 +92,7 @@ public partial class AttachDocuments : MSGComponentBase
foreach (var path in paths)
{
if(!await this.IsFileExtensionValid(path))
if(!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(path))
continue;
this.DocumentPaths.Add(path);
@ -133,7 +134,7 @@ public partial class AttachDocuments : MSGComponentBase
if (!File.Exists(selectedFile.SelectedFilePath))
return;
if (!await this.IsFileExtensionValid(selectedFile.SelectedFilePath))
if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(selectedFile.SelectedFilePath))
return;
this.DocumentPaths.Add(selectedFile.SelectedFilePath);
@ -141,36 +142,6 @@ public partial class AttachDocuments : MSGComponentBase
await this.OnChange(this.DocumentPaths);
}
private async Task<bool> IsFileExtensionValid(string selectedFile)
{
var ext = Path.GetExtension(selectedFile).TrimStart('.');
if (Array.Exists(FileTypeFilter.Executables.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
{
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.AppBlocking, this.T("Executables are not allowed")));
return false;
}
if (Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
{
await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.ImageNotSupported, this.T("Images are not supported yet")));
return false;
}
if (Array.Exists(FileTypeFilter.AllVideos.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
{
await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.FeaturedVideo, this.T("Videos are not supported yet")));
return false;
}
if (Array.Exists(FileTypeFilter.AllAudio.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
{
await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.AudioFile, this.T("Audio files are not supported yet")));
return false;
}
return true;
}
private async Task ClearAllFiles()
{
this.DocumentPaths.Clear();

View File

@ -1,5 +1,5 @@
using AIStudio.Tools.Rust;
using AIStudio.Tools.Services;
using AIStudio.Tools.Validation;
using Microsoft.AspNetCore.Components;
@ -55,35 +55,12 @@ public partial class ReadFileContent : MSGComponentBase
return;
}
var ext = Path.GetExtension(selectedFile.SelectedFilePath).TrimStart('.');
if (Array.Exists(FileTypeFilter.Executables.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
if (!await FileExtensionValidation.IsExtensionValidWithNotifyAsync(selectedFile.SelectedFilePath))
{
this.Logger.LogWarning("User attempted to load executable file: {FilePath} with extension: {Extension}", selectedFile.SelectedFilePath, ext);
await MessageBus.INSTANCE.SendError(new(Icons.Material.Filled.AppBlocking, T("Executables are not allowed")));
this.Logger.LogWarning("User attempted to load unsupported file: {FilePath}", selectedFile.SelectedFilePath);
return;
}
if (Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
{
this.Logger.LogWarning("User attempted to load image file: {FilePath} with extension: {Extension}", selectedFile.SelectedFilePath, ext);
await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.ImageNotSupported, T("Images are not supported yet")));
return;
}
if (Array.Exists(FileTypeFilter.AllVideos.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
{
this.Logger.LogWarning("User attempted to load video file: {FilePath} with extension: {Extension}", selectedFile.SelectedFilePath, ext);
await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.FeaturedVideo, this.T("Videos are not supported yet")));
return;
}
if (Array.Exists(FileTypeFilter.AllAudio.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
{
this.Logger.LogWarning("User attempted to load audio file: {FilePath} with extension: {Extension}", selectedFile.SelectedFilePath, ext);
await MessageBus.INSTANCE.SendWarning(new(Icons.Material.Filled.AudioFile, this.T("Audio files are not supported yet")));
return;
}
try
{
var fileContent = await UserFile.LoadFileData(selectedFile.SelectedFilePath, this.RustService, this.DialogService);

View File

@ -0,0 +1,55 @@
using AIStudio.Tools.PluginSystem;
using AIStudio.Tools.Rust;
namespace AIStudio.Tools.Validation;
/// <summary>
/// Provides centralized validation for file extensions.
/// </summary>
public static class FileExtensionValidation
{
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(FileExtensionValidation).Namespace, nameof(FileExtensionValidation));
/// <summary>
/// Validates the file extension and sends appropriate MessageBus notifications when invalid.
/// </summary>
/// <param name="filePath">The file path to validate.</param>
/// <returns>True if valid, false if invalid (error/warning already sent via MessageBus).</returns>
public static async Task<bool> IsExtensionValidWithNotifyAsync(string filePath)
{
var ext = Path.GetExtension(filePath).TrimStart('.');
if (Array.Exists(FileTypeFilter.Executables.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
{
await MessageBus.INSTANCE.SendError(new(
Icons.Material.Filled.AppBlocking,
TB("Executables are not allowed")));
return false;
}
if (Array.Exists(FileTypeFilter.AllImages.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
{
await MessageBus.INSTANCE.SendWarning(new(
Icons.Material.Filled.ImageNotSupported,
TB("Images are not supported yet")));
return false;
}
if (Array.Exists(FileTypeFilter.AllVideos.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
{
await MessageBus.INSTANCE.SendWarning(new(
Icons.Material.Filled.FeaturedVideo,
TB("Videos are not supported yet")));
return false;
}
if (Array.Exists(FileTypeFilter.AllAudio.FilterExtensions, x => x.Equals(ext, StringComparison.OrdinalIgnoreCase)))
{
await MessageBus.INSTANCE.SendWarning(new(
Icons.Material.Filled.AudioFile,
TB("Audio files are not supported yet")));
return false;
}
return true;
}
}