You've already forked postgres_exporter
mirror of
https://github.com/prometheus-community/postgres_exporter.git
synced 2025-08-08 04:42:07 +03:00
Add self-contained gometalinter build tooling.
This commit is contained in:
21
tools/vendor/github.com/stripe/safesql/LICENSE
generated
vendored
Normal file
21
tools/vendor/github.com/stripe/safesql/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015- Stripe, Inc. (https://stripe.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
79
tools/vendor/github.com/stripe/safesql/README.md
generated
vendored
Normal file
79
tools/vendor/github.com/stripe/safesql/README.md
generated
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
SafeSQL
|
||||
=======
|
||||
|
||||
SafeSQL is a static analysis tool for Go that protects against SQL injections.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```
|
||||
$ go get github.com/stripe/safesql
|
||||
|
||||
$ safesql
|
||||
Usage: safesql [-q] [-v] package1 [package2 ...]
|
||||
-q=false: Only print on failure
|
||||
-v=false: Verbose mode
|
||||
|
||||
$ safesql example.com/an/unsafe/package
|
||||
Found 1 potentially unsafe SQL statements:
|
||||
- /Users/alice/go/src/example.com/an/unsafe/package/db.go:14:19
|
||||
Please ensure that all SQL queries you use are compile-time constants.
|
||||
You should always use parameterized queries or prepared statements
|
||||
instead of building queries from strings.
|
||||
|
||||
$ safesql example.com/a/safe/package
|
||||
You're safe from SQL injection! Yay \o/
|
||||
```
|
||||
|
||||
|
||||
How does it work?
|
||||
-----------------
|
||||
|
||||
SafeSQL uses the static analysis utilities in [go/tools][tools] to search for
|
||||
all call sites of each of the `query` functions in package [database/sql][sql]
|
||||
(i.e., functions which accept a `string` parameter named `query`). It then makes
|
||||
sure that every such call site uses a query that is a compile-time constant.
|
||||
|
||||
The principle behind SafeSQL's safety guarantees is that queries that are
|
||||
compile-time constants cannot be subverted by user-supplied data: they must
|
||||
either incorporate no user-controlled values, or incorporate them using the
|
||||
package's safe placeholder mechanism. In particular, call sites which build up
|
||||
SQL statements via `fmt.Sprintf` or string concatenation or other mechanisms
|
||||
will not be allowed.
|
||||
|
||||
[tools]: https://godoc.org/golang.org/x/tools/go
|
||||
[sql]: http://golang.org/pkg/database/sql/
|
||||
|
||||
False positives
|
||||
---------------
|
||||
|
||||
If SafeSQL passes, your application is free from SQL injections (modulo bugs in
|
||||
the tool), however there are a great many safe programs which SafeSQL will
|
||||
declare potentially unsafe. These false positives fall roughly into two buckets:
|
||||
|
||||
First, SafeSQL does not currently recursively trace functions through the call
|
||||
graph. If you have a function that looks like this:
|
||||
|
||||
func MyQuery(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
return globalDBObject.Query(query, args...)
|
||||
}
|
||||
|
||||
and only call `MyQuery` with compile-time constants, your program is safe;
|
||||
however SafeSQL will report that `(*database/sql.DB).Query` is called with a
|
||||
non-constant parameter (namely the parameter to `MyQuery`). This is by no means
|
||||
a fundamental limitation: SafeSQL could recursively trace the `query` argument
|
||||
through every intervening helper function to ensure that its argument is always
|
||||
constant, but this code has yet to be written.
|
||||
|
||||
If you use a wrapper for `database/sql` (e.g., [`sqlx`][sqlx]), it's likely
|
||||
SafeSQL will not work for you because of this.
|
||||
|
||||
The second sort of false positive is based on a limitation in the sort of
|
||||
analysis SafeSQL performs: there are many safe SQL statements which are not
|
||||
feasible (or not possible) to represent as compile-time constants. More advanced
|
||||
static analysis techniques (such as taint analysis) or user-provided safety
|
||||
annotations would be able to reduce the number of false positives, but this is
|
||||
expected to be a significant undertaking.
|
||||
|
||||
[sqlx]: https://github.com/jmoiron/sqlx
|
7
tools/vendor/github.com/stripe/safesql/package15.go
generated
vendored
Normal file
7
tools/vendor/github.com/stripe/safesql/package15.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// +build !go1.6
|
||||
|
||||
package main
|
||||
|
||||
import "os"
|
||||
|
||||
var useVendor = os.Getenv("GO15VENDOREXPERIMENT") == "1"
|
7
tools/vendor/github.com/stripe/safesql/package16.go
generated
vendored
Normal file
7
tools/vendor/github.com/stripe/safesql/package16.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// +build go1.6
|
||||
|
||||
package main
|
||||
|
||||
import "os"
|
||||
|
||||
var useVendor = os.Getenv("GO15VENDOREXPERIMENT") == "0" || os.Getenv("GO15VENDOREXPERIMENT") == ""
|
248
tools/vendor/github.com/stripe/safesql/safesql.go
generated
vendored
Normal file
248
tools/vendor/github.com/stripe/safesql/safesql.go
generated
vendored
Normal file
@@ -0,0 +1,248 @@
|
||||
// Command safesql is a tool for performing static analysis on programs to
|
||||
// ensure that SQL injection attacks are not possible. It does this by ensuring
|
||||
// package database/sql is only used with compile-time constant queries.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/types"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/callgraph"
|
||||
"golang.org/x/tools/go/loader"
|
||||
"golang.org/x/tools/go/pointer"
|
||||
"golang.org/x/tools/go/ssa"
|
||||
"golang.org/x/tools/go/ssa/ssautil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var verbose, quiet bool
|
||||
flag.BoolVar(&verbose, "v", false, "Verbose mode")
|
||||
flag.BoolVar(&quiet, "q", false, "Only print on failure")
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s [-q] [-v] package1 [package2 ...]\n", os.Args[0])
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
pkgs := flag.Args()
|
||||
if len(pkgs) == 0 {
|
||||
flag.Usage()
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
c := loader.Config{
|
||||
FindPackage: FindPackage,
|
||||
}
|
||||
c.Import("database/sql")
|
||||
for _, pkg := range pkgs {
|
||||
c.Import(pkg)
|
||||
}
|
||||
p, err := c.Load()
|
||||
if err != nil {
|
||||
fmt.Printf("error loading packages %v: %v\n", pkgs, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
s := ssautil.CreateProgram(p, 0)
|
||||
s.Build()
|
||||
|
||||
qms := FindQueryMethods(p.Package("database/sql").Pkg, s)
|
||||
if verbose {
|
||||
fmt.Println("database/sql functions that accept queries:")
|
||||
for _, m := range qms {
|
||||
fmt.Printf("- %s (param %d)\n", m.Func, m.Param)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
mains := FindMains(p, s)
|
||||
if len(mains) == 0 {
|
||||
fmt.Println("Did not find any commands (i.e., main functions).")
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
res, err := pointer.Analyze(&pointer.Config{
|
||||
Mains: mains,
|
||||
BuildCallGraph: true,
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("error performing pointer analysis: %v\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
bad := FindNonConstCalls(res.CallGraph, qms)
|
||||
if len(bad) == 0 {
|
||||
if !quiet {
|
||||
fmt.Println(`You're safe from SQL injection! Yay \o/`)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Found %d potentially unsafe SQL statements:\n", len(bad))
|
||||
for _, ci := range bad {
|
||||
pos := p.Fset.Position(ci.Pos())
|
||||
fmt.Printf("- %s\n", pos)
|
||||
}
|
||||
fmt.Println("Please ensure that all SQL queries you use are compile-time constants.")
|
||||
fmt.Println("You should always use parameterized queries or prepared statements")
|
||||
fmt.Println("instead of building queries from strings.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// QueryMethod represents a method on a type which has a string parameter named
|
||||
// "query".
|
||||
type QueryMethod struct {
|
||||
Func *types.Func
|
||||
SSA *ssa.Function
|
||||
ArgCount int
|
||||
Param int
|
||||
}
|
||||
|
||||
// FindQueryMethods locates all methods in the given package (assumed to be
|
||||
// package database/sql) with a string parameter named "query".
|
||||
func FindQueryMethods(sql *types.Package, ssa *ssa.Program) []*QueryMethod {
|
||||
methods := make([]*QueryMethod, 0)
|
||||
scope := sql.Scope()
|
||||
for _, name := range scope.Names() {
|
||||
o := scope.Lookup(name)
|
||||
if !o.Exported() {
|
||||
continue
|
||||
}
|
||||
if _, ok := o.(*types.TypeName); !ok {
|
||||
continue
|
||||
}
|
||||
n := o.Type().(*types.Named)
|
||||
for i := 0; i < n.NumMethods(); i++ {
|
||||
m := n.Method(i)
|
||||
if !m.Exported() {
|
||||
continue
|
||||
}
|
||||
s := m.Type().(*types.Signature)
|
||||
if num, ok := FuncHasQuery(s); ok {
|
||||
methods = append(methods, &QueryMethod{
|
||||
Func: m,
|
||||
SSA: ssa.FuncValue(m),
|
||||
ArgCount: s.Params().Len(),
|
||||
Param: num,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return methods
|
||||
}
|
||||
|
||||
var stringType types.Type = types.Typ[types.String]
|
||||
|
||||
// FuncHasQuery returns the offset of the string parameter named "query", or
|
||||
// none if no such parameter exists.
|
||||
func FuncHasQuery(s *types.Signature) (offset int, ok bool) {
|
||||
params := s.Params()
|
||||
for i := 0; i < params.Len(); i++ {
|
||||
v := params.At(i)
|
||||
if v.Name() == "query" && v.Type() == stringType {
|
||||
return i, true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// FindMains returns the set of all packages loaded into the given
|
||||
// loader.Program which contain main functions
|
||||
func FindMains(p *loader.Program, s *ssa.Program) []*ssa.Package {
|
||||
ips := p.InitialPackages()
|
||||
mains := make([]*ssa.Package, 0, len(ips))
|
||||
for _, info := range ips {
|
||||
ssaPkg := s.Package(info.Pkg)
|
||||
if ssaPkg.Func("main") != nil {
|
||||
mains = append(mains, ssaPkg)
|
||||
}
|
||||
}
|
||||
return mains
|
||||
}
|
||||
|
||||
// FindNonConstCalls returns the set of callsites of the given set of methods
|
||||
// for which the "query" parameter is not a compile-time constant.
|
||||
func FindNonConstCalls(cg *callgraph.Graph, qms []*QueryMethod) []ssa.CallInstruction {
|
||||
cg.DeleteSyntheticNodes()
|
||||
|
||||
// package database/sql has a couple helper functions which are thin
|
||||
// wrappers around other sensitive functions. Instead of handling the
|
||||
// general case by tracing down callsites of wrapper functions
|
||||
// recursively, let's just whitelist the functions we're already
|
||||
// tracking, since it happens to be good enough for our use case.
|
||||
okFuncs := make(map[*ssa.Function]struct{}, len(qms))
|
||||
for _, m := range qms {
|
||||
okFuncs[m.SSA] = struct{}{}
|
||||
}
|
||||
|
||||
bad := make([]ssa.CallInstruction, 0)
|
||||
for _, m := range qms {
|
||||
node := cg.CreateNode(m.SSA)
|
||||
for _, edge := range node.In {
|
||||
if _, ok := okFuncs[edge.Site.Parent()]; ok {
|
||||
continue
|
||||
}
|
||||
cc := edge.Site.Common()
|
||||
args := cc.Args
|
||||
// The first parameter is occasionally the receiver.
|
||||
if len(args) == m.ArgCount+1 {
|
||||
args = args[1:]
|
||||
} else if len(args) != m.ArgCount {
|
||||
panic("arg count mismatch")
|
||||
}
|
||||
v := args[m.Param]
|
||||
if _, ok := v.(*ssa.Const); !ok {
|
||||
bad = append(bad, edge.Site)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bad
|
||||
}
|
||||
|
||||
// Deal with GO15VENDOREXPERIMENT
|
||||
func FindPackage(ctxt *build.Context, path, dir string, mode build.ImportMode) (*build.Package, error) {
|
||||
if !useVendor {
|
||||
return ctxt.Import(path, dir, mode)
|
||||
}
|
||||
|
||||
// First, walk up the filesystem from dir looking for vendor directories
|
||||
var vendorDir string
|
||||
for tmp := dir; vendorDir == "" && tmp != "/"; tmp = filepath.Dir(tmp) {
|
||||
dname := filepath.Join(tmp, "vendor", filepath.FromSlash(path))
|
||||
fd, err := os.Open(dname)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// Directories are only valid if they contain at least one file
|
||||
// with suffix ".go" (this also ensures that the file descriptor
|
||||
// we have is in fact a directory)
|
||||
names, err := fd.Readdirnames(-1)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, name := range names {
|
||||
if strings.HasSuffix(name, ".go") {
|
||||
vendorDir = filepath.ToSlash(dname)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if vendorDir != "" {
|
||||
pkg, err := ctxt.ImportDir(vendorDir, mode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Go tries to derive a valid import path for the package, but
|
||||
// it's wrong (it includes "/vendor/"). Overwrite it here.
|
||||
pkg.ImportPath = path
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
return ctxt.Import(path, dir, mode)
|
||||
}
|
Reference in New Issue
Block a user