1
0
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:
Will Rouesnel
2017-06-06 21:39:41 +10:00
parent 0de0311c22
commit e2b6c973a1
710 changed files with 277204 additions and 35 deletions

21
tools/vendor/github.com/stripe/safesql/LICENSE generated vendored Normal file
View 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
View 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
View 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
View 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
View 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)
}