diff --git a/backend/tests/Taskdeck.Api.Tests/CaptureAdversarialTests.cs b/backend/tests/Taskdeck.Api.Tests/CaptureAdversarialTests.cs
new file mode 100644
index 000000000..fa21999b1
--- /dev/null
+++ b/backend/tests/Taskdeck.Api.Tests/CaptureAdversarialTests.cs
@@ -0,0 +1,232 @@
+using System.Net;
+using System.Net.Http.Json;
+using System.Text;
+using FluentAssertions;
+using Taskdeck.Api.Tests.Support;
+using Taskdeck.Application.DTOs;
+using Xunit;
+
+namespace Taskdeck.Api.Tests;
+
+///
+/// Extended adversarial tests for capture/inbox endpoint.
+/// Exercises binary data, null bytes, very long strings, nested JSON,
+/// and random binary content — verifying NO 500 responses.
+///
+public class CaptureAdversarialTests : IClassFixture
+{
+ private readonly TestWebApplicationFactory _factory;
+ private readonly HttpClient _client;
+ private bool _isAuthenticated;
+
+ public CaptureAdversarialTests(TestWebApplicationFactory factory)
+ {
+ _factory = factory;
+ _client = factory.CreateClient();
+ }
+
+ private async Task EnsureAuthenticatedAsync()
+ {
+ if (_isAuthenticated) return;
+ await ApiTestHarness.AuthenticateAsync(_client, "capture-adversarial");
+ _isAuthenticated = true;
+ }
+
+ // ─────────────────────── Very long strings ───────────────────────
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(1000)]
+ [InlineData(20_000)] // at limit
+ [InlineData(20_001)] // just over limit
+ [InlineData(100_000)] // far over limit
+ public async Task CaptureItem_WithVariousTextLengths_NeverReturns500(int length)
+ {
+ await EnsureAuthenticatedAsync();
+
+ var text = length == 0 ? "" : new string('c', length);
+ var response = await _client.PostAsJsonAsync("/api/capture/items",
+ new CreateCaptureItemDto(null, text));
+
+ ((int)response.StatusCode).Should().BeLessThan(500,
+ $"Capture returned 500 for text of {length} chars");
+ }
+
+ // ─────────────────────── Null bytes and control characters ───────────────────────
+
+ [Theory]
+ [InlineData("\u0000")]
+ [InlineData("text\u0000with\u0000null\u0000bytes")]
+ [InlineData("\u0001\u0002\u0003\u0004\u0005\u0006\u0007")]
+ [InlineData("\u0008\u000B\u000C\u000E\u000F\u0010")]
+ [InlineData("\x1B[31mcolored\x1B[0m")]
+ [InlineData("\r\n\r\n\r\n")]
+ [InlineData("\t\t\t\t\t")]
+ [InlineData("before\u0000after")]
+ public async Task CaptureItem_WithControlChars_NeverReturns500(string text)
+ {
+ await EnsureAuthenticatedAsync();
+
+ var response = await _client.PostAsJsonAsync("/api/capture/items",
+ new CreateCaptureItemDto(null, text));
+
+ ((int)response.StatusCode).Should().BeLessThan(500,
+ $"Capture returned 500 for text with control chars");
+ }
+
+ // ─────────────────────── Unicode edge cases ───────────────────────
+
+ [Theory]
+ [InlineData("\uFEFF")] // BOM
+ [InlineData("\uFFFD")] // replacement character
+ [InlineData("\u200B")] // zero-width space
+ [InlineData("\u200E")] // LTR mark
+ [InlineData("\u202E")] // RTL override
+ [InlineData("\u0301")] // combining accent
+ [InlineData("e\u0301")] // decomposed e-acute
+ [InlineData("\u00E9")] // precomposed e-acute
+ [InlineData("👨👩👧👦")] // family emoji
+ [InlineData("𝕋𝕖𝕤𝕥")] // math bold
+ [InlineData("田中太郎")] // CJK
+ [InlineData("مرحبا")] // Arabic RTL
+ [InlineData("\u0E01\u0E38")] // Thai combining
+ [InlineData("\uDBFF\uDFFF")] // max surrogate pair
+ public async Task CaptureItem_WithUnicodeEdgeCases_NeverReturns500(string text)
+ {
+ await EnsureAuthenticatedAsync();
+
+ var response = await _client.PostAsJsonAsync("/api/capture/items",
+ new CreateCaptureItemDto(null, text));
+
+ ((int)response.StatusCode).Should().BeLessThan(500,
+ $"Capture returned 500 for unicode edge case");
+ }
+
+ // ─────────────────────── Nested JSON as text content ───────────────────────
+
+ [Theory]
+ [InlineData("{\"nested\": true}")]
+ [InlineData("[1, 2, 3]")]
+ [InlineData("{\"__proto__\": {\"admin\": true}}")]
+ [InlineData("{\"constructor\": {\"prototype\": {\"isAdmin\": true}}}")]
+ [InlineData("{\"action\": \"delete\", \"target\": \"all_boards\"}")]
+ [InlineData("{{7*7}}")]
+ [InlineData("${7*7}")]
+ [InlineData("#{7*7}")]
+ public async Task CaptureItem_WithNestedJsonAndTemplates_NeverReturns500(string text)
+ {
+ await EnsureAuthenticatedAsync();
+
+ var response = await _client.PostAsJsonAsync("/api/capture/items",
+ new CreateCaptureItemDto(null, text));
+
+ ((int)response.StatusCode).Should().BeLessThan(500,
+ $"Capture returned 500 for nested JSON/template content");
+
+ if (response.IsSuccessStatusCode)
+ {
+ var item = await response.Content.ReadFromJsonAsync();
+ item.Should().NotBeNull();
+ item!.RawText.Should().Contain(text,
+ "nested JSON should be stored as literal text, not interpreted");
+ }
+ }
+
+ // ─────────────────────── XSS/injection payloads ───────────────────────
+
+ [Theory]
+ [InlineData("")]
+ [InlineData("
")]
+ [InlineData("