From 4f6fd8b89f71446971cd3496b96263cf7921013c Mon Sep 17 00:00:00 2001 From: Aaron Stannard Date: Tue, 14 Jul 2026 23:51:55 +0000 Subject: [PATCH] fix: add TSV (text/tab-separated-values) support to content scanner Add TSV alongside CSV as a supported text MIME type. TSV files are plain tab-delimited text with no magic bytes, so they use the same AnyContent matcher as CSV. Updates the MIME catalog, validator, and extension-to-MIME normalization override. --- src/Netclaw.Actors.Tests/Tools/WebFetchToolTests.cs | 1 + src/Netclaw.Media/MimeTypeCatalog.cs | 3 +++ src/Netclaw.Security.Tests/MagicByteValidatorTests.cs | 4 +++- src/Netclaw.Security/MagicByteValidator.cs | 3 ++- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/Netclaw.Actors.Tests/Tools/WebFetchToolTests.cs b/src/Netclaw.Actors.Tests/Tools/WebFetchToolTests.cs index ebbeaa12f..b9f48a723 100644 --- a/src/Netclaw.Actors.Tests/Tools/WebFetchToolTests.cs +++ b/src/Netclaw.Actors.Tests/Tools/WebFetchToolTests.cs @@ -700,6 +700,7 @@ public void GetExtensionFromUrl_extracts_extension(string url, string? expected) [InlineData("application/pdf", true, ".pdf")] [InlineData("application/json", false, ".json")] [InlineData("text/csv", false, ".csv")] + [InlineData("text/tab-separated-values", false, ".tsv")] [InlineData("image/png", true, ".png")] [InlineData("text/plain", false, ".txt")] public void GetFallbackExtension_returns_correct_extension(string contentType, bool isBinary, string expected) diff --git a/src/Netclaw.Media/MimeTypeCatalog.cs b/src/Netclaw.Media/MimeTypeCatalog.cs index 5b67c8a90..14a9849c1 100644 --- a/src/Netclaw.Media/MimeTypeCatalog.cs +++ b/src/Netclaw.Media/MimeTypeCatalog.cs @@ -13,6 +13,7 @@ public static class MimeTypeCatalog public const string TextPlain = "text/plain"; public const string TextMarkdown = "text/markdown"; public const string TextCsv = "text/csv"; + public const string TextTsv = "text/tab-separated-values"; public const string TextHtml = "text/html"; public const string ApplicationJson = "application/json"; public const string ApplicationXml = "application/xml"; @@ -222,6 +223,7 @@ private static MediaTypeDefinition[] BuildDefinitions() => Text(TextPlain, ".txt", ".txt", ".log"), Text(TextMarkdown, ".md", ".md", ".markdown"), Text(TextCsv, ".csv", ".csv"), + Text(TextTsv, ".tsv", ".tsv"), Text(TextHtml, ".html", ".html", ".htm"), Text(ApplicationJson, ".json", ".json"), Text(ApplicationXml, ".xml", ".xml"), @@ -281,6 +283,7 @@ private static MediaTypeDefinition DefinitionsByCanonical(string mimeType) => [(".yaml", TextPlain)] = ApplicationYaml, [(".yml", TextPlain)] = ApplicationYaml, [(".csv", TextPlain)] = TextCsv, + [(".tsv", TextPlain)] = TextTsv, [(".xml", TextPlain)] = ApplicationXml, [(".html", TextPlain)] = TextHtml, [(".htm", TextPlain)] = TextHtml diff --git a/src/Netclaw.Security.Tests/MagicByteValidatorTests.cs b/src/Netclaw.Security.Tests/MagicByteValidatorTests.cs index 0c6ec6708..c270a1952 100644 --- a/src/Netclaw.Security.Tests/MagicByteValidatorTests.cs +++ b/src/Netclaw.Security.Tests/MagicByteValidatorTests.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- // // Copyright (C) 2026 - 2026 Petabridge, LLC // @@ -143,6 +143,7 @@ public void Validate_DocxWithOleHeader_MimeTypeMismatch() [InlineData(nameof(PlainTextBytes), "text/plain", "notes.txt")] [InlineData(nameof(PlainTextBytes), "text/markdown", "readme.md")] [InlineData(nameof(PlainTextBytes), "text/csv", "data.csv")] + [InlineData(nameof(PlainTextBytes), "text/tab-separated-values", "data.tsv")] public void Validate_TextContentWithMatchingMime_Allowed(string headerField, string mime, string filename) { var header = ResolveHeader(headerField); @@ -189,6 +190,7 @@ public void Validate_MarkdownDeclaredAsTextPlain_NormalizesAndAllows(string file [InlineData("config.yaml")] [InlineData("config.yml")] [InlineData("data.csv")] + [InlineData("data.tsv")] [InlineData("doc.xml")] public void Validate_StructuredTextDeclaredAsTextPlain_NormalizesAndAllows(string filename) { diff --git a/src/Netclaw.Security/MagicByteValidator.cs b/src/Netclaw.Security/MagicByteValidator.cs index ce20c1700..89d10acfa 100644 --- a/src/Netclaw.Security/MagicByteValidator.cs +++ b/src/Netclaw.Security/MagicByteValidator.cs @@ -1,4 +1,4 @@ -// ----------------------------------------------------------------------- +// ----------------------------------------------------------------------- // // Copyright (C) 2026 - 2026 Petabridge, LLC // @@ -255,6 +255,7 @@ public static bool HasExecutableSignature(ReadOnlySpan content) ["text/plain"] = AnyContent, ["text/markdown"] = AnyContent, ["text/csv"] = AnyContent, + ["text/tab-separated-values"] = AnyContent, ["text/html"] = AnyContent, ["application/json"] = AnyContent, ["application/xml"] = AnyContent,