mirror of
https://github.com/gin-gonic/gin.git
synced 2026-07-15 07:42:19 +08:00
Merge 9040029d9caa7373784bdbc3b604710a962081bb into d75fcd4c9ab260e5225de590f1f0f8c0e0e12d11
This commit is contained in:
commit
e84bf42cd0
43
.github/workflows/cifuzz.yml
vendored
Normal file
43
.github/workflows/cifuzz.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
name: CIFuzz
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '**.go'
|
||||||
|
- '.github/workflows/cifuzz.yml'
|
||||||
|
push:
|
||||||
|
branches: [main, master]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
fuzzing:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
sanitizer: [address]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||||
|
- name: Build Fuzzers (${{ matrix.sanitizer }})
|
||||||
|
id: build
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@ba0e2e0399a10b7b42afb16e7a6c4ccd3ff52431
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'gin'
|
||||||
|
language: go
|
||||||
|
sanitizer: ${{ matrix.sanitizer }}
|
||||||
|
- name: Run Fuzzers (${{ matrix.sanitizer }})
|
||||||
|
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@ba0e2e0399a10b7b42afb16e7a6c4ccd3ff52431
|
||||||
|
with:
|
||||||
|
oss-fuzz-project-name: 'gin'
|
||||||
|
language: go
|
||||||
|
fuzz-seconds: 300
|
||||||
|
sanitizer: ${{ matrix.sanitizer }}
|
||||||
|
output-sarif: true
|
||||||
|
- name: Upload Sarif
|
||||||
|
if: always() && steps.build.outcome == 'success'
|
||||||
|
uses: github/codeql-action/upload-sarif@601d5b1bcb3e5ef5eea97a6d0dcdbbb8c2b80116
|
||||||
|
with:
|
||||||
|
sarif_file: cifuzz-sarif/results.sarif
|
||||||
|
category: fuzz-${{ matrix.sanitizer }}
|
||||||
102
fuzz_test.go
Normal file
102
fuzz_test.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
//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)
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user