From 53ba3066e918eb8a46a576d2b9e92539fad12254 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 8 Apr 2026 11:35:08 +0200 Subject: [PATCH] fix: quote ETags per RFC 7232 in all backend implementations (#1307) Agent-Logs-Url: https://github.com/Squidex/squidex/sessions/e06e5e02-4c13-427f-84a3-09a0531531cd Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: SebastianStehle <1236435+SebastianStehle@users.noreply.github.com> --- .../Squidex.Web/CommandMiddlewares/ETagCommandMiddleware.cs | 2 +- backend/src/Squidex.Web/ETagExtensions.cs | 4 ++-- .../Squidex/Areas/Api/Controllers/Apps/AppImageController.cs | 2 +- .../Areas/Api/Controllers/Assets/AssetContentController.cs | 2 +- .../CommandMiddlewares/ETagCommandMiddlewareTests.cs | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/src/Squidex.Web/CommandMiddlewares/ETagCommandMiddleware.cs b/backend/src/Squidex.Web/CommandMiddlewares/ETagCommandMiddleware.cs index b5a6e0e01..c6c98d194 100644 --- a/backend/src/Squidex.Web/CommandMiddlewares/ETagCommandMiddleware.cs +++ b/backend/src/Squidex.Web/CommandMiddlewares/ETagCommandMiddleware.cs @@ -54,6 +54,6 @@ public class ETagCommandMiddleware(IHttpContextAccessor httpContextAccessor) : I private static void SetResponsEtag(HttpContext httpContext, long version) { - httpContext.Response.Headers[HeaderNames.ETag] = version.ToString(CultureInfo.InvariantCulture); + httpContext.Response.Headers[HeaderNames.ETag] = $"\"{version.ToString(CultureInfo.InvariantCulture)}\""; } } diff --git a/backend/src/Squidex.Web/ETagExtensions.cs b/backend/src/Squidex.Web/ETagExtensions.cs index 6a9f178e1..1ca70bfac 100644 --- a/backend/src/Squidex.Web/ETagExtensions.cs +++ b/backend/src/Squidex.Web/ETagExtensions.cs @@ -48,13 +48,13 @@ public static class ETagExtensions hasher.AppendLong(item.Version); } - return hasher.GetHexStringAndReset(); + return hasher.GetQuotedHexStringAndReset(); } } public static string ToEtag(this T entity) where T : Entity { - return entity.Version.ToString(CultureInfo.InvariantCulture); + return $"\"{entity.Version.ToString(CultureInfo.InvariantCulture)}\""; } public static bool TryParseEtagVersion(this HttpContext httpContext, string header, out long version) diff --git a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppImageController.cs b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppImageController.cs index 509509a14..d16ed039a 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Apps/AppImageController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Apps/AppImageController.cs @@ -47,7 +47,7 @@ public sealed class AppImageController( var etag = App.Image.Etag; - Response.Headers[HeaderNames.ETag] = etag; + Response.Headers[HeaderNames.ETag] = $"\"{etag}\""; var callback = new FileCallback(async (body, range, ct) => { diff --git a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs index 39e6eb2a0..dbba75b73 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Assets/AssetContentController.cs @@ -119,7 +119,7 @@ public sealed class AssetContentController( return NotFound(); } - Response.Headers[HeaderNames.ETag] = asset.FileVersion.ToString(CultureInfo.InvariantCulture); + Response.Headers[HeaderNames.ETag] = $"\"{asset.FileVersion.ToString(CultureInfo.InvariantCulture)}\""; if (request.CacheDuration > 0) { diff --git a/backend/tests/Squidex.Web.Tests/CommandMiddlewares/ETagCommandMiddlewareTests.cs b/backend/tests/Squidex.Web.Tests/CommandMiddlewares/ETagCommandMiddlewareTests.cs index 6e6d88b03..84e666107 100644 --- a/backend/tests/Squidex.Web.Tests/CommandMiddlewares/ETagCommandMiddlewareTests.cs +++ b/backend/tests/Squidex.Web.Tests/CommandMiddlewares/ETagCommandMiddlewareTests.cs @@ -75,7 +75,7 @@ public class ETagCommandMiddlewareTests : GivenContext await HandleAsync(new CreateContent(), actual); - Assert.Equal(new StringValues("17"), httpContextAccessor.HttpContext!.Response.Headers[HeaderNames.ETag]); + Assert.Equal(new StringValues("\"17\""), httpContextAccessor.HttpContext!.Response.Headers[HeaderNames.ETag]); } [Fact] @@ -85,7 +85,7 @@ public class ETagCommandMiddlewareTests : GivenContext await HandleAsync(new CreateContent(), actual); - Assert.Equal(new StringValues("17"), httpContextAccessor.HttpContext!.Response.Headers[HeaderNames.ETag]); + Assert.Equal(new StringValues("\"17\""), httpContextAccessor.HttpContext!.Response.Headers[HeaderNames.ETag]); } private async Task HandleAsync(ICommand command, object actual)