From 0b5a4e7db483cbbfd8ccb666a11c77e17d59ea08 Mon Sep 17 00:00:00 2001
From: Giteabot <teabot@gitea.io>
Date: Thu, 7 Mar 2024 11:53:33 +0800
Subject: [PATCH] Use strict protocol check when redirect (#29642) (#29644)

Backport #29642 by wxiaoguang

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
---
 modules/context/base.go       |  2 +-
 services/context/base_test.go | 39 +++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 1 deletion(-)
 create mode 100644 services/context/base_test.go

diff --git a/modules/context/base.go b/modules/context/base.go
index 8df1dde866d..87da9ff3605 100644
--- a/modules/context/base.go
+++ b/modules/context/base.go
@@ -255,7 +255,7 @@ func (b *Base) Redirect(location string, status ...int) {
 		code = status[0]
 	}
 
-	if strings.Contains(location, "://") || strings.HasPrefix(location, "//") {
+	if strings.HasPrefix(location, "http://") || strings.HasPrefix(location, "https://") || strings.HasPrefix(location, "//") {
 		// Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
 		// 1. the first request to "/my-path" contains cookie
 		// 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
diff --git a/services/context/base_test.go b/services/context/base_test.go
new file mode 100644
index 00000000000..96ccfa73e39
--- /dev/null
+++ b/services/context/base_test.go
@@ -0,0 +1,39 @@
+// Copyright 2024 The Gitea Authors. All rights reserved.
+// SPDX-License-Identifier: MIT
+
+package context
+
+import (
+	"net/http"
+	"net/http/httptest"
+	"testing"
+
+	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/setting"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestRedirect(t *testing.T) {
+	req, _ := http.NewRequest("GET", "/", nil)
+
+	cases := []struct {
+		url  string
+		keep bool
+	}{
+		{"http://test", false},
+		{"https://test", false},
+		{"//test", false},
+		{"/://test", true},
+		{"/test", true},
+	}
+	for _, c := range cases {
+		resp := httptest.NewRecorder()
+		b, cleanup := context.NewBaseContext(resp, req)
+		resp.Header().Add("Set-Cookie", (&http.Cookie{Name: setting.SessionConfig.CookieName, Value: "dummy"}).String())
+		b.Redirect(c.url)
+		cleanup()
+		has := resp.Header().Get("Set-Cookie") == "i_like_gitea=dummy"
+		assert.Equal(t, c.keep, has, "url = %q", c.url)
+	}
+}