54 lines
1.6 KiB
Go
54 lines
1.6 KiB
Go
package rules
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"go/ast"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
// web/httputil v1.6.0 added Bind/BindEmpty, which fill a typed Req from path and
|
||
|
|
// query struct tags and validate it once. Reading a parameter by hand — chi.URLParam
|
||
|
|
// or r.URL.Query() inside a handler — skips that validation, which is the path by
|
||
|
|
// which unclamped bounds and 500s-that-should-be-400s reach production. This rule
|
||
|
|
// nudges toward Bind; it is advisory (info), since a genuinely custom response
|
||
|
|
// (streaming, non-JSON) may still read parameters directly.
|
||
|
|
func init() {
|
||
|
|
registered = append(registered,
|
||
|
|
Rule{
|
||
|
|
ID: "httputil.prefer-bind",
|
||
|
|
Severity: SeverityInfo,
|
||
|
|
Module: "web",
|
||
|
|
Check: checkPreferBind,
|
||
|
|
},
|
||
|
|
)
|
||
|
|
}
|
||
|
|
|
||
|
|
func checkPreferBind(c *Context) []Finding {
|
||
|
|
const (
|
||
|
|
msg = "reading a path/query parameter by hand skips validation — httputil.Bind / BindEmpty (v1.6.0) fill a typed Req from path:/query: tags and validate it once"
|
||
|
|
hint = "Declare the parameter as a struct field (path:\"id\" / query:\"page\") and use httputil.Bind. Keep manual parsing only for genuinely custom responses (streaming, non-JSON)."
|
||
|
|
)
|
||
|
|
var hits []Finding
|
||
|
|
seen := map[int]bool{}
|
||
|
|
ast.Inspect(c.File, func(n ast.Node) bool {
|
||
|
|
call, ok := n.(*ast.CallExpr)
|
||
|
|
if !ok {
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
name := exprName(call.Fun)
|
||
|
|
manual := strings.HasSuffix(name, ".URLParam") ||
|
||
|
|
strings.HasSuffix(name, ".URLParamFromCtx") ||
|
||
|
|
strings.HasSuffix(name, ".URL.Query")
|
||
|
|
if !manual {
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
line := c.Fset.Position(call.Pos()).Line
|
||
|
|
if seen[line] {
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
seen[line] = true
|
||
|
|
hits = append(hits, Finding{Message: msg, Hint: hint, Line: line})
|
||
|
|
return true
|
||
|
|
})
|
||
|
|
return hits
|
||
|
|
}
|