ci(sec): improve type safety and server organization in HTTP middleware (#4437)

- Update linting configuration to exclude G115 gosec check instead of including specific checks
- Add the safeInt8 helper for safer type conversions and use it to prevent int8 overflow in middleware handler execution
- Group related constants and variables together for better organization in gin.go
- Refactor HTTP server instantiation to use a dedicated http.Server object for all Run methods
- Add the safeUint16 helper and use it to safely handle conversions in tree node functions to prevent uint16 overflow

Signed-off-by: appleboy <appleboy.tw@gmail.com>
This commit is contained in:
Bo-Yi Wu 2025-11-15 23:03:32 +08:00 committed by GitHub
parent 58135f06cf
commit 93ff771e6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 50 additions and 22 deletions

View File

@ -18,15 +18,8 @@ linters:
- wastedassign - wastedassign
settings: settings:
gosec: gosec:
includes: excludes:
- G102 - G115
- G106
- G108
- G109
- G111
- G112
- G201
- G203
perfsprint: perfsprint:
int-conversion: true int-conversion: true
err-error: true err-error: true

View File

@ -55,6 +55,14 @@ const ContextRequestKey ContextKeyType = 0
// abortIndex represents a typical value used in abort functions. // abortIndex represents a typical value used in abort functions.
const abortIndex int8 = math.MaxInt8 >> 1 const abortIndex int8 = math.MaxInt8 >> 1
// safeInt8 converts int to int8 safely, capping at math.MaxInt8
func safeInt8(n int) int8 {
if n > math.MaxInt8 {
return math.MaxInt8
}
return int8(n)
}
// Context is the most important part of gin. It allows us to pass variables between middleware, // Context is the most important part of gin. It allows us to pass variables between middleware,
// manage the flow, validate the JSON of a request and render a JSON response for example. // manage the flow, validate the JSON of a request and render a JSON response for example.
type Context struct { type Context struct {
@ -186,7 +194,7 @@ func (c *Context) FullPath() string {
// See example in GitHub. // See example in GitHub.
func (c *Context) Next() { func (c *Context) Next() {
c.index++ c.index++
for c.index < int8(len(c.handlers)) { for c.index < safeInt8(len(c.handlers)) {
if c.handlers[c.index] != nil { if c.handlers[c.index] != nil {
c.handlers[c.index](c) c.handlers[c.index](c)
} }

38
gin.go
View File

@ -23,10 +23,12 @@ import (
"golang.org/x/net/http2/h2c" "golang.org/x/net/http2/h2c"
) )
const defaultMultipartMemory = 32 << 20 // 32 MB const (
const escapedColon = "\\:" defaultMultipartMemory = 32 << 20 // 32 MB
const colon = ":" escapedColon = "\\:"
const backslash = "\\" colon = ":"
backslash = "\\"
)
var ( var (
default404Body = []byte("404 page not found") default404Body = []byte("404 page not found")
@ -46,8 +48,10 @@ var defaultTrustedCIDRs = []*net.IPNet{
}, },
} }
var regSafePrefix = regexp.MustCompile("[^a-zA-Z0-9/-]+") var (
var regRemoveRepeatedChar = regexp.MustCompile("/{2,}") regSafePrefix = regexp.MustCompile("[^a-zA-Z0-9/-]+")
regRemoveRepeatedChar = regexp.MustCompile("/{2,}")
)
// HandlerFunc defines the handler used by gin middleware as return value. // HandlerFunc defines the handler used by gin middleware as return value.
type HandlerFunc func(*Context) type HandlerFunc func(*Context)
@ -537,7 +541,11 @@ func (engine *Engine) Run(addr ...string) (err error) {
engine.updateRouteTrees() engine.updateRouteTrees()
address := resolveAddress(addr) address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address) debugPrint("Listening and serving HTTP on %s\n", address)
err = http.ListenAndServe(address, engine.Handler()) server := &http.Server{ // #nosec G112
Addr: address,
Handler: engine.Handler(),
}
err = server.ListenAndServe()
return return
} }
@ -553,7 +561,11 @@ func (engine *Engine) RunTLS(addr, certFile, keyFile string) (err error) {
"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.") "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.")
} }
err = http.ListenAndServeTLS(addr, certFile, keyFile, engine.Handler()) server := &http.Server{ // #nosec G112
Addr: addr,
Handler: engine.Handler(),
}
err = server.ListenAndServeTLS(certFile, keyFile)
return return
} }
@ -576,7 +588,10 @@ func (engine *Engine) RunUnix(file string) (err error) {
defer listener.Close() defer listener.Close()
defer os.Remove(file) defer os.Remove(file)
err = http.Serve(listener, engine.Handler()) server := &http.Server{ // #nosec G112
Handler: engine.Handler(),
}
err = server.Serve(listener)
return return
} }
@ -630,7 +645,10 @@ func (engine *Engine) RunListener(listener net.Listener) (err error) {
"Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.") "Please check https://github.com/gin-gonic/gin/blob/master/docs/doc.md#dont-trust-all-proxies for details.")
} }
err = http.Serve(listener, engine.Handler()) server := &http.Server{ // #nosec G112
Handler: engine.Handler(),
}
err = server.Serve(listener)
return return
} }

13
tree.go
View File

@ -5,6 +5,7 @@
package gin package gin
import ( import (
"math"
"net/url" "net/url"
"strings" "strings"
"unicode" "unicode"
@ -77,14 +78,22 @@ func (n *node) addChild(child *node) {
} }
} }
// safeUint16 converts int to uint16 safely, capping at math.MaxUint16
func safeUint16(n int) uint16 {
if n > math.MaxUint16 {
return math.MaxUint16
}
return uint16(n)
}
func countParams(path string) uint16 { func countParams(path string) uint16 {
colons := strings.Count(path, ":") colons := strings.Count(path, ":")
stars := strings.Count(path, "*") stars := strings.Count(path, "*")
return uint16(colons + stars) return safeUint16(colons + stars)
} }
func countSections(path string) uint16 { func countSections(path string) uint16 {
return uint16(strings.Count(path, "/")) return safeUint16(strings.Count(path, "/"))
} }
type nodeType uint8 type nodeType uint8