mirror of
https://github.com/gin-gonic/gin.git
synced 2026-09-04 14:49:27 +08:00
Compare commits
11 Commits
2630d94d4a
...
159edc5b7f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
159edc5b7f | ||
|
|
dcaa4296d1 | ||
|
|
8dd20118ba | ||
|
|
00cfe5aac2 | ||
|
|
528a95d48d | ||
|
|
3f1b179fdc | ||
|
|
473f9ddf55 | ||
|
|
4ce483f0f4 | ||
|
|
08e51b48be | ||
|
|
cf3be80b0e | ||
|
|
e7c8e50895 |
2
.github/workflows/codeql.yml
vendored
2
.github/workflows/codeql.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
|
||||
12
.github/workflows/gin.yml
vendored
12
.github/workflows/gin.yml
vendored
@ -16,11 +16,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: "^1"
|
||||
- name: Setup golangci-lint
|
||||
@ -55,17 +55,17 @@ jobs:
|
||||
GOPROXY: https://proxy.golang.org
|
||||
steps:
|
||||
- name: Set up Go ${{ matrix.go }}
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: ${{ matrix.go }}
|
||||
cache: false
|
||||
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ github.ref }}
|
||||
|
||||
- uses: actions/cache@v5
|
||||
- uses: actions/cache@v6
|
||||
with:
|
||||
path: |
|
||||
${{ matrix.go-build }}
|
||||
@ -78,6 +78,6 @@ jobs:
|
||||
run: make test
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v6
|
||||
uses: codecov/codecov-action@v7
|
||||
with:
|
||||
flags: ${{ matrix.os }},go-${{ matrix.go }},${{ matrix.test-tags }}
|
||||
|
||||
4
.github/workflows/goreleaser.yml
vendored
4
.github/workflows/goreleaser.yml
vendored
@ -13,11 +13,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
uses: actions/setup-go@v7
|
||||
with:
|
||||
go-version: "^1"
|
||||
- name: Run GoReleaser
|
||||
|
||||
2
.github/workflows/trivy-scan.yml
vendored
2
.github/workflows/trivy-scan.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@ -132,9 +132,10 @@ func (c *Context) Copy() *Context {
|
||||
cp.handlers = nil
|
||||
cp.fullPath = c.fullPath
|
||||
|
||||
cKeys := c.Keys
|
||||
c.mu.RLock()
|
||||
cp.Keys = maps.Clone(cKeys)
|
||||
if c.Keys != nil {
|
||||
cp.Keys = maps.Clone(c.Keys)
|
||||
}
|
||||
c.mu.RUnlock()
|
||||
|
||||
cParams := c.Params
|
||||
|
||||
8
go.mod
8
go.mod
@ -16,7 +16,7 @@ require (
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/ugorji/go/codec v1.3.1
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0
|
||||
golang.org/x/net v0.55.0
|
||||
golang.org/x/net v0.56.0
|
||||
google.golang.org/protobuf v1.36.11
|
||||
)
|
||||
|
||||
@ -39,7 +39,7 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
golang.org/x/arch v0.25.0 // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/text v0.37.0 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/text v0.39.0 // indirect
|
||||
)
|
||||
|
||||
16
go.sum
16
go.sum
@ -79,15 +79,15 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
golang.org/x/arch v0.25.0 h1:qnk6Ksugpi5Bz32947rkUgDt9/s5qvqDPl/gBKdMJLE=
|
||||
golang.org/x/arch v0.25.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
|
||||
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
2
path.go
2
path.go
@ -55,7 +55,7 @@ func cleanPath(p string) string {
|
||||
|
||||
// A bit more clunky without a 'lazybuf' like the path package, but the loop
|
||||
// gets completely inlined (bufApp calls).
|
||||
// loop has no expensive function calls (except 1x make) // So in contrast to the path package this loop has no expensive function
|
||||
// So in contrast to the path package this loop has no expensive function
|
||||
// calls (except make, if needed).
|
||||
|
||||
for r < n {
|
||||
|
||||
17
recovery.go
17
recovery.go
@ -91,16 +91,25 @@ func CustomRecoveryWithWriter(out io.Writer, handle RecoveryFunc) HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// secureRequestDump returns a sanitized HTTP request dump where the Authorization header,
|
||||
// if present, is replaced with a masked value ("Authorization: *") to avoid leaking sensitive credentials.
|
||||
// secureRequestDump returns a sanitized HTTP request dump where the Authorization
|
||||
// and Proxy-Authorization headers, if present, are replaced with a masked value
|
||||
// (e.g. "Authorization: *") to avoid leaking sensitive credentials.
|
||||
//
|
||||
// Currently, only the Authorization header is sanitized. All other headers and request data remain unchanged.
|
||||
// Header name matching is case-insensitive since HTTP headers are case-insensitive
|
||||
// per RFC 9110. All other headers and request data remain unchanged.
|
||||
func secureRequestDump(r *http.Request) string {
|
||||
httpRequest, _ := httputil.DumpRequest(r, false)
|
||||
lines := strings.Split(bytesconv.BytesToString(httpRequest), "\r\n")
|
||||
const (
|
||||
authPrefix = "Authorization:"
|
||||
proxyPrefix = "Proxy-Authorization:"
|
||||
)
|
||||
for i, line := range lines {
|
||||
if strings.HasPrefix(line, "Authorization:") {
|
||||
switch {
|
||||
case len(line) >= len(authPrefix) && strings.EqualFold(line[:len(authPrefix)], authPrefix):
|
||||
lines[i] = "Authorization: *"
|
||||
case len(line) >= len(proxyPrefix) && strings.EqualFold(line[:len(proxyPrefix)], proxyPrefix):
|
||||
lines[i] = "Proxy-Authorization: *"
|
||||
}
|
||||
}
|
||||
return strings.Join(lines, "\r\n")
|
||||
|
||||
@ -318,25 +318,67 @@ func TestSecureRequestDump(t *testing.T) {
|
||||
wantNotContain: "Bearer secret-token",
|
||||
},
|
||||
{
|
||||
// Bypass http.Header.Set canonicalization to put a lowercase
|
||||
// header name on the wire and verify case-insensitive matching.
|
||||
name: "authorization header lowercase",
|
||||
req: func() *http.Request {
|
||||
r, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
r.Header.Set("authorization", "some-secret")
|
||||
r.Header["authorization"] = []string{"some-secret"}
|
||||
return r
|
||||
}(),
|
||||
wantContains: "Authorization: *",
|
||||
wantNotContain: "some-secret",
|
||||
},
|
||||
{
|
||||
name: "AUTHORIZATION header uppercase",
|
||||
req: func() *http.Request {
|
||||
r, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
r.Header["AUTHORIZATION"] = []string{"UPPER-SECRET"}
|
||||
return r
|
||||
}(),
|
||||
wantContains: "Authorization: *",
|
||||
wantNotContain: "UPPER-SECRET",
|
||||
},
|
||||
{
|
||||
name: "Authorization header mixed case",
|
||||
req: func() *http.Request {
|
||||
r, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
r.Header.Set("AuThOrIzAtIoN", "token123")
|
||||
r.Header["AuThOrIzAtIoN"] = []string{"token123"}
|
||||
return r
|
||||
}(),
|
||||
wantContains: "Authorization: *",
|
||||
wantNotContain: "token123",
|
||||
},
|
||||
{
|
||||
name: "Proxy-Authorization header",
|
||||
req: func() *http.Request {
|
||||
r, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
r.Header.Set("Proxy-Authorization", "Basic cHJveHk6c2VjcmV0")
|
||||
return r
|
||||
}(),
|
||||
wantContains: "Proxy-Authorization: *",
|
||||
wantNotContain: "Basic cHJveHk6c2VjcmV0",
|
||||
},
|
||||
{
|
||||
name: "proxy-authorization header lowercase",
|
||||
req: func() *http.Request {
|
||||
r, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
r.Header["proxy-authorization"] = []string{"Basic bG93ZXI="}
|
||||
return r
|
||||
}(),
|
||||
wantContains: "Proxy-Authorization: *",
|
||||
wantNotContain: "Basic bG93ZXI=",
|
||||
},
|
||||
{
|
||||
name: "PROXY-AUTHORIZATION header uppercase",
|
||||
req: func() *http.Request {
|
||||
r, _ := http.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
r.Header["PROXY-AUTHORIZATION"] = []string{"Basic VVBQRVI="}
|
||||
return r
|
||||
}(),
|
||||
wantContains: "Proxy-Authorization: *",
|
||||
wantNotContain: "Basic VVBQRVI=",
|
||||
},
|
||||
{
|
||||
name: "No Authorization header",
|
||||
req: func() *http.Request {
|
||||
|
||||
@ -160,11 +160,19 @@ func (r AsciiJSON) Render(w http.ResponseWriter) error {
|
||||
}
|
||||
|
||||
var buffer bytes.Buffer
|
||||
escapeBuf := make([]byte, 0, 6) // Preallocate 6 bytes for Unicode escape sequences
|
||||
escapeBuf := make([]byte, 0, 12) // Preallocate for surrogate pair escape sequences
|
||||
|
||||
for _, r := range bytesconv.BytesToString(ret) {
|
||||
if r > unicode.MaxASCII {
|
||||
escapeBuf = fmt.Appendf(escapeBuf[:0], "\\u%04x", r) // Reuse escapeBuf
|
||||
if r > 0xFFFF {
|
||||
// Supplementary plane: encode as UTF-16 surrogate pair per RFC 8259
|
||||
r -= 0x10000
|
||||
high := 0xD800 + (r>>10)&0x3FF
|
||||
low := 0xDC00 + r&0x3FF
|
||||
escapeBuf = fmt.Appendf(escapeBuf[:0], "\\u%04x\\u%04x", high, low)
|
||||
} else {
|
||||
escapeBuf = fmt.Appendf(escapeBuf[:0], "\\u%04x", r)
|
||||
}
|
||||
buffer.Write(escapeBuf)
|
||||
} else {
|
||||
buffer.WriteByte(byte(r))
|
||||
|
||||
@ -261,6 +261,17 @@ func TestRenderAsciiJSON(t *testing.T) {
|
||||
assert.Equal(t, "3.1415926", w2.Body.String())
|
||||
}
|
||||
|
||||
func TestRenderAsciiJSONSupplementaryUnicode(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
data := map[string]string{"emoji": "😀"}
|
||||
|
||||
err := (AsciiJSON{data}).Render(w)
|
||||
require.NoError(t, err)
|
||||
// U+1F600 must be encoded as UTF-16 surrogate pair per RFC 8259.
|
||||
// Use Contains to verify the surrogate pair encoding in the raw output.
|
||||
assert.Contains(t, w.Body.String(), `\ud83d\ude00`)
|
||||
}
|
||||
|
||||
func TestRenderAsciiJSONFail(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
data := make(chan int)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user