mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-07-24 20:19:39 +02:00
fix: ASCII equal fold for authorization header (#8391)
For the "Authorization:" header only lowercase "token" was accepted. This change allows uppercase "Token" as well. Signed-off-by: Nis Wechselberg <enbewe@enbewe.de> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/8391 Reviewed-by: Gusted <gusted@noreply.codeberg.org> Reviewed-by: Michael Kriese <michael.kriese@gmx.de> Co-authored-by: Nis Wechselberg <enbewe@enbewe.de> Co-committed-by: Nis Wechselberg <enbewe@enbewe.de>
This commit is contained in:
parent
f324ee73c5
commit
24d6972f6b
4 changed files with 78 additions and 1 deletions
|
@ -95,3 +95,25 @@ func UnsafeBytesToString(b []byte) string {
|
||||||
func UnsafeStringToBytes(s string) []byte {
|
func UnsafeStringToBytes(s string) []byte {
|
||||||
return unsafe.Slice(unsafe.StringData(s), len(s))
|
return unsafe.Slice(unsafe.StringData(s), len(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AsciiEqualFold is taken from Golang, but reimplemented here, since the original is not exposed to public
|
||||||
|
// Taken from: https://cs.opensource.google/go/go/+/refs/tags/go1.24.4:src/net/http/internal/ascii/print.go
|
||||||
|
func ASCIIEqualFold(s, t string) bool {
|
||||||
|
if len(s) != len(t) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i := 0; i < len(s); i++ {
|
||||||
|
if ASCIILower(s[i]) != ASCIILower(t[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsciiLower returns the ASCII lowercase version of b.
|
||||||
|
func ASCIILower(b byte) byte {
|
||||||
|
if 'A' <= b && b <= 'Z' {
|
||||||
|
return b + ('a' - 'A')
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
|
@ -45,3 +45,29 @@ func TestToSnakeCase(t *testing.T) {
|
||||||
assert.Equal(t, expected, ToSnakeCase(input))
|
assert.Equal(t, expected, ToSnakeCase(input))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestASCIIEqualFold(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
First string
|
||||||
|
Second string
|
||||||
|
Expected bool
|
||||||
|
}{
|
||||||
|
"Empty String": {First: "", Second: "", Expected: true},
|
||||||
|
"Single Letter Ident": {First: "h", Second: "h", Expected: true},
|
||||||
|
"Single Letter Equal": {First: "h", Second: "H", Expected: true},
|
||||||
|
"Single Letter Unequal": {First: "h", Second: "g", Expected: false},
|
||||||
|
"Simple Match Ident": {First: "someString", Second: "someString", Expected: true},
|
||||||
|
"Simple Match Equal": {First: "someString", Second: "someSTRIng", Expected: true},
|
||||||
|
"Simple Match Unequal": {First: "someString", Second: "sameString", Expected: false},
|
||||||
|
"Different Length": {First: "abcdef", Second: "abcdefg", Expected: false},
|
||||||
|
"Unicode Kelvin": {First: "ghijklm", Second: "GHIJ\u212ALM", Expected: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range cases {
|
||||||
|
c := cases[name]
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
Actual := ASCIIEqualFold(c.First, c.Second)
|
||||||
|
assert.Equal(t, c.Expected, Actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"forgejo.org/modules/log"
|
"forgejo.org/modules/log"
|
||||||
"forgejo.org/modules/setting"
|
"forgejo.org/modules/setting"
|
||||||
"forgejo.org/modules/timeutil"
|
"forgejo.org/modules/timeutil"
|
||||||
|
"forgejo.org/modules/util"
|
||||||
"forgejo.org/modules/web/middleware"
|
"forgejo.org/modules/web/middleware"
|
||||||
"forgejo.org/services/actions"
|
"forgejo.org/services/actions"
|
||||||
"forgejo.org/services/auth/source/oauth2"
|
"forgejo.org/services/auth/source/oauth2"
|
||||||
|
@ -125,7 +126,7 @@ func parseToken(req *http.Request) (string, bool) {
|
||||||
// check header token
|
// check header token
|
||||||
if auHead := req.Header.Get("Authorization"); auHead != "" {
|
if auHead := req.Header.Get("Authorization"); auHead != "" {
|
||||||
auths := strings.Fields(auHead)
|
auths := strings.Fields(auHead)
|
||||||
if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") {
|
if len(auths) == 2 && (util.ASCIIEqualFold(auths[0], "token") || util.ASCIIEqualFold(auths[0], "bearer")) {
|
||||||
return auths[1], true
|
return auths[1], true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"forgejo.org/models/unittest"
|
"forgejo.org/models/unittest"
|
||||||
|
@ -52,3 +53,30 @@ func TestCheckTaskIsRunning(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParseToken(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
Header string
|
||||||
|
ExpectedToken string
|
||||||
|
Expected bool
|
||||||
|
}{
|
||||||
|
"Token Uppercase": {Header: "Token 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true},
|
||||||
|
"Token Lowercase": {Header: "token 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true},
|
||||||
|
"Token Unicode": {Header: "to\u212Aen 1234567890123456789012345687901325467890", ExpectedToken: "", Expected: false},
|
||||||
|
"Bearer Uppercase": {Header: "Bearer 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true},
|
||||||
|
"Bearer Lowercase": {Header: "bearer 1234567890123456789012345687901325467890", ExpectedToken: "1234567890123456789012345687901325467890", Expected: true},
|
||||||
|
"Missing type": {Header: "1234567890123456789012345687901325467890", ExpectedToken: "", Expected: false},
|
||||||
|
"Three Parts": {Header: "abc 1234567890 test", ExpectedToken: "", Expected: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name := range cases {
|
||||||
|
c := cases[name]
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
req, _ := http.NewRequest("GET", "/", nil)
|
||||||
|
req.Header.Add("Authorization", c.Header)
|
||||||
|
ActualToken, ActualSuccess := parseToken(req)
|
||||||
|
assert.Equal(t, c.ExpectedToken, ActualToken)
|
||||||
|
assert.Equal(t, c.Expected, ActualSuccess)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue