From 6a6a66f50fc720cbe14bc22ef9409a0be65d930d Mon Sep 17 00:00:00 2001 From: Matt Jenkinson <75292329+mattdjenkinson@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:50:55 +0100 Subject: [PATCH] fix: allow RESPONSE_CODE_DETAILS and START_TIME in error page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The branded 5xx error page served on the downstream data plane shows a "Status code", "Error details", and "Timestamp" row. Only the first one ever rendered — the other two printed their literal Envoy %OPERATOR% tokens instead of real values. envoyBodyAllowedCommands only permitted %RESPONSE_CODE% through the escaper. Every other %...% token, including %RESPONSE_CODE_DETAILS% and %START_TIME(%Y-%m-%d %H:%M:%S UTC)%, got its percent signs doubled to %%, which Envoy renders back as a literal percent instead of invoking the operator. Add both operators to the allowlist so they resolve like %RESPONSE_CODE% already does. --- internal/extensionserver/mutate/localreply.go | 2 ++ internal/extensionserver/mutate/localreply_test.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/internal/extensionserver/mutate/localreply.go b/internal/extensionserver/mutate/localreply.go index 1963709f..15d397f4 100644 --- a/internal/extensionserver/mutate/localreply.go +++ b/internal/extensionserver/mutate/localreply.go @@ -163,6 +163,8 @@ func buildLocalReplyConfig(cfg *LocalReplyConfig) *hcmv3.LocalReplyConfig { // re-trigger the exact listener rejection this escaper exists to prevent // (issue #243). Keep entries longest-first so matching is unambiguous. var envoyBodyAllowedCommands = []string{ + "%START_TIME(%Y-%m-%d %H:%M:%S UTC)%", + "%RESPONSE_CODE_DETAILS%", "%RESPONSE_CODE%", } diff --git a/internal/extensionserver/mutate/localreply_test.go b/internal/extensionserver/mutate/localreply_test.go index a7bd2d5e..0bcaaca8 100644 --- a/internal/extensionserver/mutate/localreply_test.go +++ b/internal/extensionserver/mutate/localreply_test.go @@ -233,6 +233,8 @@ func TestEscapeEnvoyFormatLiterals(t *testing.T) { {"bare percent in css", "height: 100%;", "height: 100%%;"}, {"multiple bare percents", "120% 50% 0%", "120%% 50%% 0%%"}, {"preserve allowlisted command", "code %RESPONSE_CODE% end", "code %RESPONSE_CODE% end"}, + {"preserve allowlisted response code details", "details: %RESPONSE_CODE_DETAILS%", "details: %RESPONSE_CODE_DETAILS%"}, + {"preserve allowlisted start time", "at %START_TIME(%Y-%m-%d %H:%M:%S UTC)%", "at %START_TIME(%Y-%m-%d %H:%M:%S UTC)%"}, {"escape non-allowlisted command", "%REQ(x-header)%", "%%REQ(x-header)%%"}, {"escape non-allowlisted command with length", "%REQ(x-header):10%", "%%REQ(x-header):10%%"}, {"escape command-shaped literal", "progress %COMPLETE% now", "progress %%COMPLETE%% now"},