mirror of
https://github.com/securego/gosec.git
synced 2024-12-24 11:35:52 +00:00
Better SQLi testing
This prevents the string concat tests flagging a false positive if joining two literal strings (eg "SELECT * FROM " + " table" ... ) or with a constant (eg const tab = "name"; "SELECT * from " + tab)
This commit is contained in:
parent
2d0a26dafe
commit
3e4d96ef3e
2 changed files with 94 additions and 8 deletions
28
rules/sql.go
28
rules/sql.go
|
@ -15,10 +15,10 @@
|
||||||
package rules
|
package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gas "github.com/HewlettPackard/gas/core"
|
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"reflect"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
|
gas "github.com/HewlettPackard/gas/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SqlStatement struct {
|
type SqlStatement struct {
|
||||||
|
@ -30,13 +30,27 @@ type SqlStrConcat struct {
|
||||||
SqlStatement
|
SqlStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// see if we can figgure out what it is
|
||||||
|
func (s *SqlStrConcat) checkObject(n *ast.Ident) bool {
|
||||||
|
if n.Obj != nil {
|
||||||
|
return (n.Obj.Kind != ast.Var || n.Obj.Kind != ast.Fun)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// Look for "SELECT * FROM table WHERE " + " ' OR 1=1"
|
// Look for "SELECT * FROM table WHERE " + " ' OR 1=1"
|
||||||
func (s *SqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
func (s *SqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||||
a := reflect.TypeOf(&ast.BinaryExpr{})
|
if node, ok := n.(*ast.BinaryExpr); ok {
|
||||||
b := reflect.TypeOf(&ast.BasicLit{})
|
if start, ok := node.X.(*ast.BasicLit); ok {
|
||||||
if node := gas.SimpleSelect(n, a, b); node != nil {
|
if str, _ := gas.GetString(start); s.pattern.MatchString(str) {
|
||||||
if str, _ := gas.GetString(node); s.pattern.MatchString(str) {
|
if _, ok := node.Y.(*ast.BasicLit); ok {
|
||||||
return gas.NewIssue(c, n, s.What, s.Severity, s.Confidence), nil
|
return nil, nil // string cat OK
|
||||||
|
}
|
||||||
|
if second, ok := node.Y.(*ast.Ident); ok && s.checkObject(second) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return gas.NewIssue(c, n, s.What, s.Severity, s.Confidence), nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
@ -15,8 +15,9 @@
|
||||||
package rules
|
package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
gas "github.com/HewlettPackard/gas/core"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
gas "github.com/HewlettPackard/gas/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSQLInjectionViaConcatenation(t *testing.T) {
|
func TestSQLInjectionViaConcatenation(t *testing.T) {
|
||||||
|
@ -144,3 +145,74 @@ func TestSQLInjectionFalsePositiveB(t *testing.T) {
|
||||||
|
|
||||||
checkTestResults(t, issues, 0, "Not expected to match")
|
checkTestResults(t, issues, 0, "Not expected to match")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSQLInjectionFalsePositiveC(t *testing.T) {
|
||||||
|
analyzer := gas.NewAnalyzer(false, nil)
|
||||||
|
analyzer.AddRule(NewSqlStrConcat())
|
||||||
|
analyzer.AddRule(NewSqlStrFormat())
|
||||||
|
|
||||||
|
source := `
|
||||||
|
|
||||||
|
package main
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
var staticQuery = "SELECT * FROM foo WHERE age < "
|
||||||
|
|
||||||
|
func main(){
|
||||||
|
db, err := sql.Open("sqlite3", ":memory:")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
rows, err := db.Query(staticQuery + "32")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
|
issues := gasTestRunner(source, analyzer)
|
||||||
|
|
||||||
|
checkTestResults(t, issues, 0, "Not expected to match")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSQLInjectionFalsePositiveD(t *testing.T) {
|
||||||
|
analyzer := gas.NewAnalyzer(false, nil)
|
||||||
|
analyzer.AddRule(NewSqlStrConcat())
|
||||||
|
analyzer.AddRule(NewSqlStrFormat())
|
||||||
|
|
||||||
|
source := `
|
||||||
|
|
||||||
|
package main
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
_ "github.com/mattn/go-sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
const age = "32"
|
||||||
|
var staticQuery = "SELECT * FROM foo WHERE age < "
|
||||||
|
|
||||||
|
func main(){
|
||||||
|
db, err := sql.Open("sqlite3", ":memory:")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
rows, err := db.Query(staticQuery + age)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
`
|
||||||
|
issues := gasTestRunner(source, analyzer)
|
||||||
|
|
||||||
|
checkTestResults(t, issues, 0, "Not expected to match")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue