$ git cherry --abbrev -v 8afd9cbb6cfb 66fb7fc33547 + 63b25c1 Fix typo in README (#235) + 419c929 G107 - SSRF (#236) + 145f1a0 Removed wrapping feature (#238) + ec32ce6 Support Go 1.11 (#239) + 762ff3a Allow quoted strings to be used to format SQL queries (#240) + 7f6509a Update README.md (#246) + 5f98926 Refactor Dockerfile (#245) + d3f1980 Fix false positives for SQL string concatenation with constants from another file (#247) + 64d58c2 Refactor the test code sample to support multiple files per sample + 1ecd47e bump Dockerfile golang from 1.10 to 1.11 + 027dc2b This fixes the html template when using '-fmt=html' - resolves HTML escaping issues within the template - resolves reference issues to reportInfo struct i.e. issues -> Issues, metrics -> Stats + 8c09a83 Add install.sh script + 97bc137 Add CI Installation steps and correct markdown lint errors + 3116b07 Fix typos in comments and rulelist (#256) + 443f84f Fix golint link (#263) + 4180994 Make G201 ignore CallExpr with no args (#262) + 9b966a4 add test case for strings.Builder G104 whitelist inclusion + adb4222 whitelist strings.Builder method in rule G104 + ae82798 Fix the WriteSring test by handling the error + 2695567 Build the code sample for string builder only fron Go 1.10 onwards + f14f17f Add a helper function which extracts the string parameters values of a call expression + 9b32fca Fix the bind rule to handle the case when the arguments of the net.Listen are returned by a function call + 24e3094 Extend the bind rule to handle the case when the net.Listen address in provided from a const + 72e95e8 Geneate and upload the test coverage report to codecove.io + 12400f9 Update README with the code coverage batch + 14ed63d Do not flag the unhandled errors which are explicitly ignored + f87af5f Detect the unhandled errors even though they are explicitly ignored if the 'audit: enabled' setting is defined in the global configuration (#274) + 5d33e6e Update the README with some details about the configuration file + b662615 Fix typo + a966ff7 Fix -conf example in README.md + 04ce7ba add a no-fail flag + e2752bc revert to default GOPATH if necessary (#279) - c04360f make API + 66fb7fc Replace import paths
		
			
				
	
	
		
			342 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			342 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
 | 
						|
 *
 | 
						|
 * Permission to use, copy, modify, and distribute this software for any
 | 
						|
 * purpose with or without fee is hereby granted, provided that the above
 | 
						|
 * copyright notice and this permission notice appear in all copies.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
						|
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
						|
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
						|
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
						|
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
						|
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
						|
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
package spew
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"reflect"
 | 
						|
	"sort"
 | 
						|
	"strconv"
 | 
						|
)
 | 
						|
 | 
						|
// Some constants in the form of bytes to avoid string overhead.  This mirrors
 | 
						|
// the technique used in the fmt package.
 | 
						|
var (
 | 
						|
	panicBytes            = []byte("(PANIC=")
 | 
						|
	plusBytes             = []byte("+")
 | 
						|
	iBytes                = []byte("i")
 | 
						|
	trueBytes             = []byte("true")
 | 
						|
	falseBytes            = []byte("false")
 | 
						|
	interfaceBytes        = []byte("(interface {})")
 | 
						|
	commaNewlineBytes     = []byte(",\n")
 | 
						|
	newlineBytes          = []byte("\n")
 | 
						|
	openBraceBytes        = []byte("{")
 | 
						|
	openBraceNewlineBytes = []byte("{\n")
 | 
						|
	closeBraceBytes       = []byte("}")
 | 
						|
	asteriskBytes         = []byte("*")
 | 
						|
	colonBytes            = []byte(":")
 | 
						|
	colonSpaceBytes       = []byte(": ")
 | 
						|
	openParenBytes        = []byte("(")
 | 
						|
	closeParenBytes       = []byte(")")
 | 
						|
	spaceBytes            = []byte(" ")
 | 
						|
	pointerChainBytes     = []byte("->")
 | 
						|
	nilAngleBytes         = []byte("<nil>")
 | 
						|
	maxNewlineBytes       = []byte("<max depth reached>\n")
 | 
						|
	maxShortBytes         = []byte("<max>")
 | 
						|
	circularBytes         = []byte("<already shown>")
 | 
						|
	circularShortBytes    = []byte("<shown>")
 | 
						|
	invalidAngleBytes     = []byte("<invalid>")
 | 
						|
	openBracketBytes      = []byte("[")
 | 
						|
	closeBracketBytes     = []byte("]")
 | 
						|
	percentBytes          = []byte("%")
 | 
						|
	precisionBytes        = []byte(".")
 | 
						|
	openAngleBytes        = []byte("<")
 | 
						|
	closeAngleBytes       = []byte(">")
 | 
						|
	openMapBytes          = []byte("map[")
 | 
						|
	closeMapBytes         = []byte("]")
 | 
						|
	lenEqualsBytes        = []byte("len=")
 | 
						|
	capEqualsBytes        = []byte("cap=")
 | 
						|
)
 | 
						|
 | 
						|
// hexDigits is used to map a decimal value to a hex digit.
 | 
						|
var hexDigits = "0123456789abcdef"
 | 
						|
 | 
						|
// catchPanic handles any panics that might occur during the handleMethods
 | 
						|
// calls.
 | 
						|
func catchPanic(w io.Writer, v reflect.Value) {
 | 
						|
	if err := recover(); err != nil {
 | 
						|
		w.Write(panicBytes)
 | 
						|
		fmt.Fprintf(w, "%v", err)
 | 
						|
		w.Write(closeParenBytes)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// handleMethods attempts to call the Error and String methods on the underlying
 | 
						|
// type the passed reflect.Value represents and outputes the result to Writer w.
 | 
						|
//
 | 
						|
// It handles panics in any called methods by catching and displaying the error
 | 
						|
// as the formatted value.
 | 
						|
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
 | 
						|
	// We need an interface to check if the type implements the error or
 | 
						|
	// Stringer interface.  However, the reflect package won't give us an
 | 
						|
	// interface on certain things like unexported struct fields in order
 | 
						|
	// to enforce visibility rules.  We use unsafe, when it's available,
 | 
						|
	// to bypass these restrictions since this package does not mutate the
 | 
						|
	// values.
 | 
						|
	if !v.CanInterface() {
 | 
						|
		if UnsafeDisabled {
 | 
						|
			return false
 | 
						|
		}
 | 
						|
 | 
						|
		v = unsafeReflectValue(v)
 | 
						|
	}
 | 
						|
 | 
						|
	// Choose whether or not to do error and Stringer interface lookups against
 | 
						|
	// the base type or a pointer to the base type depending on settings.
 | 
						|
	// Technically calling one of these methods with a pointer receiver can
 | 
						|
	// mutate the value, however, types which choose to satisify an error or
 | 
						|
	// Stringer interface with a pointer receiver should not be mutating their
 | 
						|
	// state inside these interface methods.
 | 
						|
	if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
 | 
						|
		v = unsafeReflectValue(v)
 | 
						|
	}
 | 
						|
	if v.CanAddr() {
 | 
						|
		v = v.Addr()
 | 
						|
	}
 | 
						|
 | 
						|
	// Is it an error or Stringer?
 | 
						|
	switch iface := v.Interface().(type) {
 | 
						|
	case error:
 | 
						|
		defer catchPanic(w, v)
 | 
						|
		if cs.ContinueOnMethod {
 | 
						|
			w.Write(openParenBytes)
 | 
						|
			w.Write([]byte(iface.Error()))
 | 
						|
			w.Write(closeParenBytes)
 | 
						|
			w.Write(spaceBytes)
 | 
						|
			return false
 | 
						|
		}
 | 
						|
 | 
						|
		w.Write([]byte(iface.Error()))
 | 
						|
		return true
 | 
						|
 | 
						|
	case fmt.Stringer:
 | 
						|
		defer catchPanic(w, v)
 | 
						|
		if cs.ContinueOnMethod {
 | 
						|
			w.Write(openParenBytes)
 | 
						|
			w.Write([]byte(iface.String()))
 | 
						|
			w.Write(closeParenBytes)
 | 
						|
			w.Write(spaceBytes)
 | 
						|
			return false
 | 
						|
		}
 | 
						|
		w.Write([]byte(iface.String()))
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// printBool outputs a boolean value as true or false to Writer w.
 | 
						|
func printBool(w io.Writer, val bool) {
 | 
						|
	if val {
 | 
						|
		w.Write(trueBytes)
 | 
						|
	} else {
 | 
						|
		w.Write(falseBytes)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// printInt outputs a signed integer value to Writer w.
 | 
						|
func printInt(w io.Writer, val int64, base int) {
 | 
						|
	w.Write([]byte(strconv.FormatInt(val, base)))
 | 
						|
}
 | 
						|
 | 
						|
// printUint outputs an unsigned integer value to Writer w.
 | 
						|
func printUint(w io.Writer, val uint64, base int) {
 | 
						|
	w.Write([]byte(strconv.FormatUint(val, base)))
 | 
						|
}
 | 
						|
 | 
						|
// printFloat outputs a floating point value using the specified precision,
 | 
						|
// which is expected to be 32 or 64bit, to Writer w.
 | 
						|
func printFloat(w io.Writer, val float64, precision int) {
 | 
						|
	w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
 | 
						|
}
 | 
						|
 | 
						|
// printComplex outputs a complex value using the specified float precision
 | 
						|
// for the real and imaginary parts to Writer w.
 | 
						|
func printComplex(w io.Writer, c complex128, floatPrecision int) {
 | 
						|
	r := real(c)
 | 
						|
	w.Write(openParenBytes)
 | 
						|
	w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
 | 
						|
	i := imag(c)
 | 
						|
	if i >= 0 {
 | 
						|
		w.Write(plusBytes)
 | 
						|
	}
 | 
						|
	w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
 | 
						|
	w.Write(iBytes)
 | 
						|
	w.Write(closeParenBytes)
 | 
						|
}
 | 
						|
 | 
						|
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
 | 
						|
// prefix to Writer w.
 | 
						|
func printHexPtr(w io.Writer, p uintptr) {
 | 
						|
	// Null pointer.
 | 
						|
	num := uint64(p)
 | 
						|
	if num == 0 {
 | 
						|
		w.Write(nilAngleBytes)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
 | 
						|
	buf := make([]byte, 18)
 | 
						|
 | 
						|
	// It's simpler to construct the hex string right to left.
 | 
						|
	base := uint64(16)
 | 
						|
	i := len(buf) - 1
 | 
						|
	for num >= base {
 | 
						|
		buf[i] = hexDigits[num%base]
 | 
						|
		num /= base
 | 
						|
		i--
 | 
						|
	}
 | 
						|
	buf[i] = hexDigits[num]
 | 
						|
 | 
						|
	// Add '0x' prefix.
 | 
						|
	i--
 | 
						|
	buf[i] = 'x'
 | 
						|
	i--
 | 
						|
	buf[i] = '0'
 | 
						|
 | 
						|
	// Strip unused leading bytes.
 | 
						|
	buf = buf[i:]
 | 
						|
	w.Write(buf)
 | 
						|
}
 | 
						|
 | 
						|
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
 | 
						|
// elements to be sorted.
 | 
						|
type valuesSorter struct {
 | 
						|
	values  []reflect.Value
 | 
						|
	strings []string // either nil or same len and values
 | 
						|
	cs      *ConfigState
 | 
						|
}
 | 
						|
 | 
						|
// newValuesSorter initializes a valuesSorter instance, which holds a set of
 | 
						|
// surrogate keys on which the data should be sorted.  It uses flags in
 | 
						|
// ConfigState to decide if and how to populate those surrogate keys.
 | 
						|
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
 | 
						|
	vs := &valuesSorter{values: values, cs: cs}
 | 
						|
	if canSortSimply(vs.values[0].Kind()) {
 | 
						|
		return vs
 | 
						|
	}
 | 
						|
	if !cs.DisableMethods {
 | 
						|
		vs.strings = make([]string, len(values))
 | 
						|
		for i := range vs.values {
 | 
						|
			b := bytes.Buffer{}
 | 
						|
			if !handleMethods(cs, &b, vs.values[i]) {
 | 
						|
				vs.strings = nil
 | 
						|
				break
 | 
						|
			}
 | 
						|
			vs.strings[i] = b.String()
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if vs.strings == nil && cs.SpewKeys {
 | 
						|
		vs.strings = make([]string, len(values))
 | 
						|
		for i := range vs.values {
 | 
						|
			vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return vs
 | 
						|
}
 | 
						|
 | 
						|
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
 | 
						|
// directly, or whether it should be considered for sorting by surrogate keys
 | 
						|
// (if the ConfigState allows it).
 | 
						|
func canSortSimply(kind reflect.Kind) bool {
 | 
						|
	// This switch parallels valueSortLess, except for the default case.
 | 
						|
	switch kind {
 | 
						|
	case reflect.Bool:
 | 
						|
		return true
 | 
						|
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
 | 
						|
		return true
 | 
						|
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
 | 
						|
		return true
 | 
						|
	case reflect.Float32, reflect.Float64:
 | 
						|
		return true
 | 
						|
	case reflect.String:
 | 
						|
		return true
 | 
						|
	case reflect.Uintptr:
 | 
						|
		return true
 | 
						|
	case reflect.Array:
 | 
						|
		return true
 | 
						|
	}
 | 
						|
	return false
 | 
						|
}
 | 
						|
 | 
						|
// Len returns the number of values in the slice.  It is part of the
 | 
						|
// sort.Interface implementation.
 | 
						|
func (s *valuesSorter) Len() int {
 | 
						|
	return len(s.values)
 | 
						|
}
 | 
						|
 | 
						|
// Swap swaps the values at the passed indices.  It is part of the
 | 
						|
// sort.Interface implementation.
 | 
						|
func (s *valuesSorter) Swap(i, j int) {
 | 
						|
	s.values[i], s.values[j] = s.values[j], s.values[i]
 | 
						|
	if s.strings != nil {
 | 
						|
		s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// valueSortLess returns whether the first value should sort before the second
 | 
						|
// value.  It is used by valueSorter.Less as part of the sort.Interface
 | 
						|
// implementation.
 | 
						|
func valueSortLess(a, b reflect.Value) bool {
 | 
						|
	switch a.Kind() {
 | 
						|
	case reflect.Bool:
 | 
						|
		return !a.Bool() && b.Bool()
 | 
						|
	case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
 | 
						|
		return a.Int() < b.Int()
 | 
						|
	case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
 | 
						|
		return a.Uint() < b.Uint()
 | 
						|
	case reflect.Float32, reflect.Float64:
 | 
						|
		return a.Float() < b.Float()
 | 
						|
	case reflect.String:
 | 
						|
		return a.String() < b.String()
 | 
						|
	case reflect.Uintptr:
 | 
						|
		return a.Uint() < b.Uint()
 | 
						|
	case reflect.Array:
 | 
						|
		// Compare the contents of both arrays.
 | 
						|
		l := a.Len()
 | 
						|
		for i := 0; i < l; i++ {
 | 
						|
			av := a.Index(i)
 | 
						|
			bv := b.Index(i)
 | 
						|
			if av.Interface() == bv.Interface() {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			return valueSortLess(av, bv)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return a.String() < b.String()
 | 
						|
}
 | 
						|
 | 
						|
// Less returns whether the value at index i should sort before the
 | 
						|
// value at index j.  It is part of the sort.Interface implementation.
 | 
						|
func (s *valuesSorter) Less(i, j int) bool {
 | 
						|
	if s.strings == nil {
 | 
						|
		return valueSortLess(s.values[i], s.values[j])
 | 
						|
	}
 | 
						|
	return s.strings[i] < s.strings[j]
 | 
						|
}
 | 
						|
 | 
						|
// sortValues is a sort function that handles both native types and any type that
 | 
						|
// can be converted to error or Stringer.  Other inputs are sorted according to
 | 
						|
// their Value.String() value to ensure display stability.
 | 
						|
func sortValues(values []reflect.Value, cs *ConfigState) {
 | 
						|
	if len(values) == 0 {
 | 
						|
		return
 | 
						|
	}
 | 
						|
	sort.Sort(newValuesSorter(values, cs))
 | 
						|
}
 |