mirror of
https://github.com/gin-gonic/gin.git
synced 2026-07-25 07:05:39 +08:00
Compare commits
5 Commits
d6ce3ff9f5
...
99c1121a69
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99c1121a69 | ||
|
|
81dba46872 | ||
|
|
0c219e7902 | ||
|
|
00900fb3e1 | ||
|
|
8e66616db7 |
4
.github/workflows/gin.yml
vendored
4
.github/workflows/gin.yml
vendored
@ -26,14 +26,14 @@ jobs:
|
||||
- name: Setup golangci-lint
|
||||
uses: golangci/golangci-lint-action@v9
|
||||
with:
|
||||
version: v2.6
|
||||
version: v2.9
|
||||
args: --verbose
|
||||
test:
|
||||
needs: lint
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
go: ["1.24", "1.25"]
|
||||
go: ["1.24", "1.25", "1.26"]
|
||||
test-tags:
|
||||
[
|
||||
"",
|
||||
|
||||
32
.github/workflows/trivy-scan.yml
vendored
32
.github/workflows/trivy-scan.yml
vendored
@ -9,7 +9,7 @@ on:
|
||||
- master
|
||||
schedule:
|
||||
# Run daily at 00:00 UTC
|
||||
- cron: '0 0 * * *'
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
|
||||
permissions:
|
||||
@ -27,30 +27,30 @@ jobs:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Run Trivy vulnerability scanner (source code)
|
||||
uses: aquasecurity/trivy-action@0.34.0
|
||||
uses: aquasecurity/trivy-action@0.34.1
|
||||
with:
|
||||
scan-type: 'fs'
|
||||
scan-ref: '.'
|
||||
scanners: 'vuln,secret,misconfig'
|
||||
format: 'sarif'
|
||||
output: 'trivy-results.sarif'
|
||||
severity: 'CRITICAL,HIGH,MEDIUM'
|
||||
scan-type: "fs"
|
||||
scan-ref: "."
|
||||
scanners: "vuln,secret,misconfig"
|
||||
format: "sarif"
|
||||
output: "trivy-results.sarif"
|
||||
severity: "CRITICAL,HIGH,MEDIUM"
|
||||
ignore-unfixed: true
|
||||
|
||||
- name: Upload Trivy results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
if: always()
|
||||
with:
|
||||
sarif_file: 'trivy-results.sarif'
|
||||
sarif_file: "trivy-results.sarif"
|
||||
|
||||
- name: Run Trivy scanner (table output for logs)
|
||||
uses: aquasecurity/trivy-action@0.34.0
|
||||
uses: aquasecurity/trivy-action@0.34.1
|
||||
if: always()
|
||||
with:
|
||||
scan-type: 'fs'
|
||||
scan-ref: '.'
|
||||
scanners: 'vuln,secret,misconfig'
|
||||
format: 'table'
|
||||
severity: 'CRITICAL,HIGH,MEDIUM'
|
||||
scan-type: "fs"
|
||||
scan-ref: "."
|
||||
scanners: "vuln,secret,misconfig"
|
||||
format: "table"
|
||||
severity: "CRITICAL,HIGH,MEDIUM"
|
||||
ignore-unfixed: true
|
||||
exit-code: '1'
|
||||
exit-code: "1"
|
||||
|
||||
18
errorparser/error_parser.go
Normal file
18
errorparser/error_parser.go
Normal file
@ -0,0 +1,18 @@
|
||||
package errorparser
|
||||
|
||||
func ParseBindError(err error) (errs []ParseError, match bool) {
|
||||
|
||||
if errs, ok := parseValidatorError(err); ok {
|
||||
return errs, true
|
||||
}
|
||||
|
||||
if errs, ok := parseJsonDecodeError(err); ok {
|
||||
return errs, true
|
||||
}
|
||||
|
||||
// todo: protobuf
|
||||
// todo: xml
|
||||
// todo: yaml
|
||||
|
||||
return nil, false
|
||||
}
|
||||
23
errorparser/error_parser_test.go
Normal file
23
errorparser/error_parser_test.go
Normal file
@ -0,0 +1,23 @@
|
||||
package errorparser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestParseBindError(t *testing.T) {
|
||||
|
||||
_, ok := ParseBindError(fmt.Errorf("not match"))
|
||||
assert.False(t, ok)
|
||||
|
||||
_, ok = ParseBindError(validator.ValidationErrors([]validator.FieldError{}))
|
||||
assert.True(t, ok)
|
||||
|
||||
_, ok = ParseBindError(&json.SyntaxError{})
|
||||
assert.True(t, ok)
|
||||
|
||||
}
|
||||
48
errorparser/json_errors.go
Normal file
48
errorparser/json_errors.go
Normal file
@ -0,0 +1,48 @@
|
||||
package errorparser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func parseJsonDecodeError(err error) (errs []ParseError, match bool) {
|
||||
|
||||
if typeErr, ok := err.(*json.UnmarshalTypeError); ok {
|
||||
return parseJsonUnmarshalTypeError(typeErr), true
|
||||
}
|
||||
|
||||
if syntaxErr, ok := err.(*json.SyntaxError); ok {
|
||||
return parseJsonSyntaxError(syntaxErr), true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func parseJsonUnmarshalTypeError(err *json.UnmarshalTypeError) (errs []ParseError) {
|
||||
|
||||
errs = []ParseError{}
|
||||
|
||||
item := NewParseError(
|
||||
err.Field,
|
||||
ParseErrorTypeMismatch,
|
||||
err,
|
||||
)
|
||||
|
||||
errs = append(errs, item)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
func parseJsonSyntaxError(err *json.SyntaxError) (errs []ParseError) {
|
||||
|
||||
errs = []ParseError{}
|
||||
|
||||
item := NewParseError(
|
||||
"",
|
||||
ParseErrorTypeBadInput,
|
||||
err,
|
||||
)
|
||||
|
||||
errs = append(errs, item)
|
||||
|
||||
return errs
|
||||
}
|
||||
94
errorparser/json_errors_test.go
Normal file
94
errorparser/json_errors_test.go
Normal file
@ -0,0 +1,94 @@
|
||||
package errorparser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func TestParseJsonDecodeError(t *testing.T) {
|
||||
|
||||
_, ok := parseJsonDecodeError(fmt.Errorf("not match"))
|
||||
assert.False(t, ok)
|
||||
|
||||
_, ok = parseJsonDecodeError(&json.UnmarshalTypeError{})
|
||||
assert.True(t, ok)
|
||||
|
||||
_, ok = parseJsonDecodeError(&json.SyntaxError{})
|
||||
assert.True(t, ok)
|
||||
|
||||
}
|
||||
|
||||
func TestParseJsonUnmarshalTypeError(t *testing.T) {
|
||||
|
||||
jsonData := `{
|
||||
"text": "text",
|
||||
"count": "1"
|
||||
}`
|
||||
|
||||
rbody := bytes.NewReader([]byte(jsonData))
|
||||
|
||||
c, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest("POST", "/", rbody)
|
||||
c.Request.Header.Add("Content-Type", gin.MIMEJSON)
|
||||
|
||||
var obj struct {
|
||||
Text string `json:"text"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
err := c.Bind(&obj)
|
||||
require.Error(t, err)
|
||||
|
||||
typeErr, ok := err.(*json.UnmarshalTypeError)
|
||||
require.True(t, ok)
|
||||
|
||||
parseErrs := parseJsonUnmarshalTypeError(typeErr)
|
||||
require.Equal(t, len(parseErrs), 1)
|
||||
|
||||
assert.Equal(t, parseErrs[0].ParamName, "count")
|
||||
assert.Equal(t, parseErrs[0].ErrorType, ParseErrorTypeMismatch)
|
||||
assert.Equal(t, parseErrs[0].InitialError, err)
|
||||
|
||||
}
|
||||
|
||||
func TestParseJsonSyntaxError(t *testing.T) {
|
||||
|
||||
jsonData := `{
|
||||
"text": "text"
|
||||
"count": 1
|
||||
}`
|
||||
|
||||
rbody := bytes.NewReader([]byte(jsonData))
|
||||
|
||||
c, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest("POST", "/", rbody)
|
||||
c.Request.Header.Add("Content-Type", gin.MIMEJSON)
|
||||
|
||||
var obj struct {
|
||||
Text string `json:"text"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
err := c.Bind(&obj)
|
||||
require.Error(t, err)
|
||||
|
||||
typeErr, ok := err.(*json.SyntaxError)
|
||||
require.True(t, ok)
|
||||
|
||||
parseErrs := parseJsonSyntaxError(typeErr)
|
||||
require.Equal(t, len(parseErrs), 1)
|
||||
|
||||
assert.Equal(t, parseErrs[0].ParamName, "")
|
||||
assert.Equal(t, parseErrs[0].ErrorType, ParseErrorTypeBadInput)
|
||||
assert.Equal(t, parseErrs[0].InitialError, err)
|
||||
|
||||
}
|
||||
28
errorparser/parse_error.go
Normal file
28
errorparser/parse_error.go
Normal file
@ -0,0 +1,28 @@
|
||||
package errorparser
|
||||
|
||||
type ParseError struct {
|
||||
ParamName string
|
||||
ErrorType ParseErrorType
|
||||
InitialError error
|
||||
}
|
||||
|
||||
func NewParseError(
|
||||
paramName string,
|
||||
errorType ParseErrorType,
|
||||
initialError error,
|
||||
) ParseError {
|
||||
return ParseError{
|
||||
ParamName: paramName,
|
||||
ErrorType: errorType,
|
||||
InitialError: initialError,
|
||||
}
|
||||
}
|
||||
|
||||
type ParseErrorType string
|
||||
|
||||
const (
|
||||
ParseErrorTypeNone ParseErrorType = ""
|
||||
ParseErrorTypeBadInput ParseErrorType = "bad_input"
|
||||
ParseErrorTypeMismatch ParseErrorType = "type_mismatch"
|
||||
ParseErrorTypeValidation ParseErrorType = "validation"
|
||||
)
|
||||
27
errorparser/validation_errors.go
Normal file
27
errorparser/validation_errors.go
Normal file
@ -0,0 +1,27 @@
|
||||
package errorparser
|
||||
|
||||
import (
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
func parseValidatorError(err error) (errs []ParseError, match bool) {
|
||||
if vErr, ok := err.(validator.ValidationErrors); ok {
|
||||
return parseValidatorValidationErrors(vErr), true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func parseValidatorValidationErrors(vErr validator.ValidationErrors) (errs []ParseError) {
|
||||
fErrs := []validator.FieldError(vErr)
|
||||
errs = make([]ParseError, 0, len(fErrs))
|
||||
for _, fErr := range fErrs {
|
||||
item := NewParseError(
|
||||
fErr.Field(),
|
||||
ParseErrorTypeValidation,
|
||||
fErr,
|
||||
)
|
||||
|
||||
errs = append(errs, item)
|
||||
}
|
||||
return errs
|
||||
}
|
||||
61
errorparser/validation_errors_test.go
Normal file
61
errorparser/validation_errors_test.go
Normal file
@ -0,0 +1,61 @@
|
||||
package errorparser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func TestParseValidatorError(t *testing.T) {
|
||||
|
||||
_, ok := parseValidatorError(fmt.Errorf("not match"))
|
||||
assert.False(t, ok)
|
||||
|
||||
_, ok = parseValidatorError(validator.ValidationErrors{})
|
||||
assert.True(t, ok)
|
||||
|
||||
}
|
||||
|
||||
func TestParseValidatorValidationErrors(t *testing.T) {
|
||||
|
||||
jsonData := `{
|
||||
"text": "",
|
||||
"count": 1
|
||||
}`
|
||||
|
||||
rbody := bytes.NewReader([]byte(jsonData))
|
||||
|
||||
c, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||||
c.Request, _ = http.NewRequest("POST", "/", rbody)
|
||||
c.Request.Header.Add("Content-Type", gin.MIMEJSON)
|
||||
|
||||
var obj struct {
|
||||
Text string `json:"text" binding:"required"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
err := c.Bind(&obj)
|
||||
require.Error(t, err)
|
||||
|
||||
vErr, ok := err.(validator.ValidationErrors)
|
||||
require.True(t, ok)
|
||||
|
||||
fErrs := []validator.FieldError(vErr)
|
||||
require.Equal(t, len(fErrs), 1)
|
||||
|
||||
parseErrs := parseValidatorValidationErrors(vErr)
|
||||
require.Equal(t, len(parseErrs), 1)
|
||||
|
||||
assert.Equal(t, parseErrs[0].ParamName, fErrs[0].Field())
|
||||
assert.Equal(t, parseErrs[0].ErrorType, ParseErrorTypeValidation)
|
||||
assert.Equal(t, parseErrs[0].InitialError, fErrs[0])
|
||||
|
||||
}
|
||||
4
go.mod
4
go.mod
@ -7,7 +7,7 @@ toolchain go1.24.7
|
||||
require (
|
||||
github.com/bytedance/sonic v1.15.0
|
||||
github.com/gin-contrib/sse v1.1.0
|
||||
github.com/go-playground/validator/v10 v10.28.0
|
||||
github.com/go-playground/validator/v10 v10.30.1
|
||||
github.com/goccy/go-json v0.10.5
|
||||
github.com/goccy/go-yaml v1.19.2
|
||||
github.com/json-iterator/go v1.1.12
|
||||
@ -29,7 +29,7 @@ require (
|
||||
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
|
||||
8
go.sum
8
go.sum
@ -10,8 +10,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
@ -20,8 +20,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688=
|
||||
github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU=
|
||||
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user