mirror of
https://github.com/gin-gonic/gin.git
synced 2026-06-20 23:58:42 +08:00
Covers the core pre-auth request parsing boundaries in Gin: - JSON request body binding (BindBody) - every JSON API - URL path matching - router parameter extraction - Form POST binding - form data parsing Gin is the most popular Go web framework with 88K+ stars and is imported by thousands of Go projects.
103 lines
2.5 KiB
Go
103 lines
2.5 KiB
Go
//go:build go1.18
|
|
// +build go1.18
|
|
|
|
// Copyright 2014 Manu Martinez-Almeida
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package gin_test
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/gin-gonic/gin/binding"
|
|
)
|
|
|
|
// FuzzJSONBinding tests JSON request body binding with arbitrary
|
|
// attacker-controlled byte input.
|
|
//
|
|
// This is the pre-auth boundary for every JSON API built with Gin.
|
|
// Every JSON request body passes through this code path.
|
|
// Gin has 88K+ GitHub stars and is imported by thousands of Go projects.
|
|
//
|
|
// 5 GitHub Security Advisories exist for Gin.
|
|
func FuzzJSONBinding(f *testing.F) {
|
|
jsonBinding := binding.JSON
|
|
|
|
f.Add([]byte(`{"name":"test","age":30}`))
|
|
f.Add([]byte(`{}`))
|
|
f.Add([]byte(``))
|
|
f.Add([]byte(`{"a":`))
|
|
f.Add([]byte(`null`))
|
|
f.Add([]byte(`[1,2,3]`))
|
|
f.Add(make([]byte, 10000))
|
|
|
|
f.Fuzz(func(t *testing.T, body []byte) {
|
|
if len(body) > 1<<16 {
|
|
return
|
|
}
|
|
|
|
var obj map[string]any
|
|
// BindBody must never panic on any input
|
|
_ = jsonBinding.BindBody(body, &obj)
|
|
})
|
|
}
|
|
|
|
// FuzzGinPathMatch tests URL path parameter matching with
|
|
// arbitrary attacker-controlled paths.
|
|
//
|
|
// Path matching determines routing and extracts parameters
|
|
// from URLs. This is the first code that processes every
|
|
// incoming HTTP request.
|
|
func FuzzGinPathMatch(f *testing.F) {
|
|
f.Add("/users/:id", "/users/123")
|
|
f.Add("/api/:version/users/:id", "/api/v1/users/42")
|
|
f.Add("/", "/")
|
|
f.Add("/static/*filepath", "/static/css/main.css")
|
|
f.Add(strings.Repeat("/a", 100), "/a/a")
|
|
|
|
f.Fuzz(func(t *testing.T, pattern, path string) {
|
|
if len(pattern) > 10000 || len(path) > 10000 {
|
|
return
|
|
}
|
|
if pattern == "" {
|
|
return
|
|
}
|
|
|
|
// Test route construction + matching
|
|
router := gin.New()
|
|
func() {
|
|
defer func() { _ = recover() }()
|
|
router.GET(pattern, func(c *gin.Context) {})
|
|
router.Handle("GET", path, func(c *gin.Context) {})
|
|
}()
|
|
})
|
|
}
|
|
|
|
// FuzzFormBinding tests form POST body binding with arbitrary
|
|
// key-value pairs from HTTP POST requests.
|
|
func FuzzFormBinding(f *testing.F) {
|
|
f.Add("name=test&age=30")
|
|
f.Add("")
|
|
f.Add("a=1&b=2&c=3")
|
|
|
|
f.Fuzz(func(t *testing.T, formData string) {
|
|
if len(formData) > 1<<16 {
|
|
return
|
|
}
|
|
|
|
// Form binding parses POST form data
|
|
// Create a minimal request with form body
|
|
req, err := http.NewRequest("POST", "/", strings.NewReader(formData))
|
|
if err != nil {
|
|
return
|
|
}
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
var obj map[string]string
|
|
_ = binding.Form.Bind(req, &obj)
|
|
})
|
|
}
|