From d1e72fdd369ccf62b39413ab7f47c0203b25ae7b Mon Sep 17 00:00:00 2001 From: Zayan Khan Date: Sun, 26 Jul 2026 18:37:11 -0400 Subject: [PATCH 1/6] cmd/cloudflared: fix dead license URL in --help copyright notice https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/license returns 404 with no redirect after the docs-site reorganizations. Point at the repository's own LICENSE file, which cannot rot with docs moves. Co-Authored-By: Claude Fable 5 --- cmd/cloudflared/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cloudflared/main.go b/cmd/cloudflared/main.go index e6ce73fe25a..911fca7d7e6 100644 --- a/cmd/cloudflared/main.go +++ b/cmd/cloudflared/main.go @@ -72,7 +72,7 @@ func main() { app.Copyright = fmt.Sprintf( `(c) %d Cloudflare Inc. Your installation of cloudflared software constitutes a symbol of your signature indicating that you accept - the terms of the Apache License Version 2.0 (https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/license), + the terms of the Apache License Version 2.0 (https://github.com/cloudflare/cloudflared/blob/master/LICENSE), Terms (https://www.cloudflare.com/terms/) and Privacy Policy (https://www.cloudflare.com/privacypolicy/).`, time.Now().Year(), ) From 11ecf0e05ed032e49517949143a50c2da7db743a Mon Sep 17 00:00:00 2001 From: Zayan Khan Date: Sun, 26 Jul 2026 18:37:11 -0400 Subject: [PATCH 2/6] cmd/cloudflared/tunnel: fix dead ingress docs URL in legacy flag help The configuration-file/ingress page under connect-apps returns 404 with no redirect; every legacy origin flag's help text linked it. Use the current canonical configuration-file page (verified 200). Co-Authored-By: Claude Fable 5 --- cmd/cloudflared/tunnel/cmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cloudflared/tunnel/cmd.go b/cmd/cloudflared/tunnel/cmd.go index 6320d0d278b..348f9867873 100644 --- a/cmd/cloudflared/tunnel/cmd.go +++ b/cmd/cloudflared/tunnel/cmd.go @@ -1094,7 +1094,7 @@ func legacyTunnelFlag(msg string) string { return fmt.Sprintf( "%s This flag only takes effect if you define your origin with `--url` and if you do not use ingress rules."+ " The recommended way is to rely on ingress rules and define this property under `originRequest` as per"+ - " https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/configuration-file/ingress", + " https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-tunnel/do-more-with-tunnels/local-management/configuration-file/", msg, ) } From d0f88e423cc9d4c393ab793e8a80e5b739e119b1 Mon Sep 17 00:00:00 2001 From: Zayan Khan Date: Sun, 26 Jul 2026 18:37:11 -0400 Subject: [PATCH 3/6] diagnostic: do not call WriteHeader after the response body is written The three diagnostic handlers encoded JSON straight to the ResponseWriter and, on error, then called WriteHeader(500). When Encode fails at the write stage the response has already begun, so the WriteHeader call is superfluous (net/http logs a warning) and cannot change the status. Marshal first, return a clean 500 on serialization failure before any bytes hit the wire, and reuse the package's writeResponse helper (previously unused) for the write path. Co-Authored-By: Claude Fable 5 --- diagnostic/handlers.go | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/diagnostic/handlers.go b/diagnostic/handlers.go index e4d85db4f24..08a5e8cf4ee 100644 --- a/diagnostic/handlers.go +++ b/diagnostic/handlers.go @@ -81,12 +81,15 @@ func (handler *Handler) SystemHandler(writer http.ResponseWriter, request *http. Err: err, } - encoder := json.NewEncoder(writer) - err = encoder.Encode(response) + body, err := json.Marshal(response) if err != nil { logger.Error().Err(err).Msgf("error occurred whilst serializing information") writer.WriteHeader(http.StatusInternalServerError) + + return } + + writeResponse(writer, body, &logger) } type TunnelState struct { @@ -108,13 +111,15 @@ func (handler *Handler) TunnelStateHandler(writer http.ResponseWriter, _ *http.R handler.tracker.GetActiveConnections(), handler.icmpSources, } - encoder := json.NewEncoder(writer) - - err := encoder.Encode(body) + response, err := json.Marshal(body) if err != nil { - handler.log.Error().Err(err).Msgf("error occurred whilst serializing information") + log.Error().Err(err).Msgf("error occurred whilst serializing information") writer.WriteHeader(http.StatusInternalServerError) + + return } + + writeResponse(writer, response, &log) } func (handler *Handler) ConfigurationHandler(writer http.ResponseWriter, _ *http.Request) { @@ -125,13 +130,15 @@ func (handler *Handler) ConfigurationHandler(writer http.ResponseWriter, _ *http log.Info().Msg("Collection finished") }() - encoder := json.NewEncoder(writer) - - err := encoder.Encode(handler.cliFlags) + body, err := json.Marshal(handler.cliFlags) if err != nil { - handler.log.Error().Err(err).Msgf("error occurred whilst serializing response") + log.Error().Err(err).Msgf("error occurred whilst serializing response") writer.WriteHeader(http.StatusInternalServerError) + + return } + + writeResponse(writer, body, &log) } func writeResponse(w http.ResponseWriter, bytes []byte, logger *zerolog.Logger) { From 9220dcd180b6ec9aa6516c83465b409c032800a3 Mon Sep 17 00:00:00 2001 From: Zayan Khan Date: Sun, 26 Jul 2026 18:37:11 -0400 Subject: [PATCH 4/6] diagnostic: add regression test for WriteHeader-after-body Each handler is driven with a ResponseWriter whose Write fails and which records whether WriteHeader is called after body bytes were written. Fails for all three handlers without the previous commit. Co-Authored-By: Claude Fable 5 --- diagnostic/handlers_test.go | 76 +++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/diagnostic/handlers_test.go b/diagnostic/handlers_test.go index 2849241cb2b..9ce93b6259e 100644 --- a/diagnostic/handlers_test.go +++ b/diagnostic/handlers_test.go @@ -167,6 +167,82 @@ func TestTunnelStateHandler(t *testing.T) { } } +type recordingFailingWriter struct { + header http.Header + wroteBody bool + headerAfterBody bool +} + +func newRecordingFailingWriter() *recordingFailingWriter { + return &recordingFailingWriter{header: make(http.Header)} +} + +func (writer *recordingFailingWriter) Header() http.Header { return writer.header } + +func (writer *recordingFailingWriter) Write([]byte) (int, error) { + writer.wroteBody = true + return 0, errors.New("write error") +} + +func (writer *recordingFailingWriter) WriteHeader(int) { + if writer.wroteBody { + writer.headerAfterBody = true + } +} + +func TestHandlersDoNotWriteHeaderAfterBody(t *testing.T) { + t.Parallel() + + log := zerolog.Nop() + handler := diagnostic.NewDiagnosticHandler(&log, 0, &SystemCollectorMock{ + systemInfo: nil, + err: nil, + }, uuid.New(), uuid.New(), newTrackerFromConns(t, nil), map[string]string{}, nil) + + tests := []struct { + name string + run func(t *testing.T, writer http.ResponseWriter) + }{ + { + name: "system handler", + run: func(t *testing.T, writer http.ResponseWriter) { + t.Helper() + ctx := context.Background() + request, err := http.NewRequestWithContext(ctx, http.MethodGet, "/diag/system", nil) + require.NoError(t, err) + handler.SystemHandler(writer, request) + }, + }, + { + name: "tunnel state handler", + run: func(t *testing.T, writer http.ResponseWriter) { + t.Helper() + handler.TunnelStateHandler(writer, nil) + }, + }, + { + name: "configuration handler", + run: func(t *testing.T, writer http.ResponseWriter) { + t.Helper() + handler.ConfigurationHandler(writer, nil) + }, + }, + } + + for _, tCase := range tests { + t.Run(tCase.name, func(t *testing.T) { + t.Parallel() + writer := newRecordingFailingWriter() + tCase.run(t, writer) + assert.False( + t, + writer.headerAfterBody, + "handler must not call WriteHeader after the response body has been written", + ) + }) + } +} + func TestConfigurationHandler(t *testing.T) { t.Parallel() From 7616bf70c771d545cb8e0c8bdb7b0e5d546c2c30 Mon Sep 17 00:00:00 2001 From: Zayan Khan Date: Sun, 26 Jul 2026 19:09:11 -0400 Subject: [PATCH 5/6] cmd/cloudflared: satisfy errcheck and prealloc in main.go Pre-existing findings surfaced by the whole-file lint policy on touched files: explicitly discard the os.Setenv error (matching the adjacent maxprocs.Set() style) and annotate the one-time command-registration literal rather than restructuring it for prealloc. Co-Authored-By: Claude Fable 5 --- cmd/cloudflared/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/cloudflared/main.go b/cmd/cloudflared/main.go index 911fca7d7e6..59728c90951 100644 --- a/cmd/cloudflared/main.go +++ b/cmd/cloudflared/main.go @@ -50,7 +50,7 @@ var ( func main() { // FIXME: TUN-8148: Disable QUIC_GO ECN due to bugs in proper detection if supported - os.Setenv("QUIC_GO_DISABLE_ECN", "1") + _ = os.Setenv("QUIC_GO_DISABLE_ECN", "1") metrics.RegisterBuildInfo(BuildType, BuildTime, Version) _, _ = maxprocs.Set() bInfo := cliutil.GetBuildInfo(BuildType, Version) @@ -97,7 +97,7 @@ func main() { } func commands(version func(c *cli.Context)) []*cli.Command { - cmds := []*cli.Command{ + cmds := []*cli.Command{ // nolint:prealloc // one-time startup registration; keep the literal readable { Name: "update", Action: cliutil.ConfiguredAction(updater.Update), From 0bfe9c757dbd04429691c4f87e0916de9b954cfc Mon Sep 17 00:00:00 2001 From: Zayan Khan Date: Sun, 26 Jul 2026 19:09:11 -0400 Subject: [PATCH 6/6] diagnostic: drop unused test constants systemInformationKey and errorKey are referenced nowhere in the module; flagged by the unused linter on the touched file. Co-Authored-By: Claude Fable 5 --- diagnostic/handlers_test.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/diagnostic/handlers_test.go b/diagnostic/handlers_test.go index 9ce93b6259e..5fa61370139 100644 --- a/diagnostic/handlers_test.go +++ b/diagnostic/handlers_test.go @@ -25,11 +25,6 @@ type SystemCollectorMock struct { err error } -const ( - systemInformationKey = "sikey" - errorKey = "errkey" -) - func newTrackerFromConns(t *testing.T, connections []tunnelstate.IndexedConnectionInfo) *tunnelstate.ConnTracker { t.Helper()