mirror of
https://github.com/securego/gosec.git
synced 2024-12-25 03:55:54 +00:00
Restructure and introduce a standalone config
This commit is contained in:
parent
cacf21f3c0
commit
bf78d027a9
26 changed files with 234 additions and 127 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -3,7 +3,6 @@
|
||||||
*.a
|
*.a
|
||||||
*.so
|
*.so
|
||||||
*.swp
|
*.swp
|
||||||
gas
|
|
||||||
|
|
||||||
# Folders
|
# Folders
|
||||||
_obj
|
_obj
|
||||||
|
|
16
analyzer.go
16
analyzer.go
|
@ -82,27 +82,33 @@ type Analyzer struct {
|
||||||
ignoreNosec bool
|
ignoreNosec bool
|
||||||
ruleset RuleSet
|
ruleset RuleSet
|
||||||
context *Context
|
context *Context
|
||||||
|
config Config
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
Issues []*Issue `json:"issues"`
|
Issues []*Issue `json:"issues"`
|
||||||
Stats *Metrics `json:"metrics"`
|
Stats *Metrics `json:"metrics"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAnalyzer builds a new anaylzer.
|
// NewAnalyzer builds a new anaylzer.
|
||||||
func NewAnalyzer(conf map[string]interface{}, logger *log.Logger) Analyzer {
|
func NewAnalyzer(conf Config, logger *log.Logger) Analyzer {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = log.New(os.Stdout, "[gas]", 0)
|
logger = log.New(os.Stdout, "[gas]", 0)
|
||||||
}
|
}
|
||||||
|
ignoreNoSec := false
|
||||||
|
if val, err := conf.Get("ignoreNoSec"); err == nil {
|
||||||
|
if override, ok := val.(bool); ok {
|
||||||
|
ignoreNoSec = override
|
||||||
|
}
|
||||||
|
}
|
||||||
a := Analyzer{
|
a := Analyzer{
|
||||||
ignoreNosec: conf["ignoreNosec"].(bool),
|
ignoreNosec: ignoreNoSec,
|
||||||
ruleset: make(RuleSet),
|
ruleset: make(RuleSet),
|
||||||
context: &Context{},
|
context: &Context{},
|
||||||
|
config: conf,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
Issues: make([]*Issue, 0, 16),
|
Issues: make([]*Issue, 0, 16),
|
||||||
Stats: &Metrics{0, 0, 0, 0},
|
Stats: &Metrics{0, 0, 0, 0},
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(tkelsey): use the inc/exc lists
|
|
||||||
|
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,6 +117,7 @@ func (gas *Analyzer) process(filename string, source interface{}) error {
|
||||||
gas.context.FileSet = token.NewFileSet()
|
gas.context.FileSet = token.NewFileSet()
|
||||||
root, err := parser.ParseFile(gas.context.FileSet, filename, source, mode)
|
root, err := parser.ParseFile(gas.context.FileSet, filename, source, mode)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
gas.context.Config = gas.config
|
||||||
gas.context.Comments = ast.NewCommentMap(gas.context.FileSet, root, root.Comments)
|
gas.context.Comments = ast.NewCommentMap(gas.context.FileSet, root, root.Comments)
|
||||||
gas.context.Root = root
|
gas.context.Root = root
|
||||||
|
|
||||||
|
@ -171,6 +178,7 @@ func (gas *Analyzer) Process(filename string) error {
|
||||||
func (gas *Analyzer) ProcessPackage(prog *loader.Program, pkg *loader.PackageInfo, file *ast.File) error {
|
func (gas *Analyzer) ProcessPackage(prog *loader.Program, pkg *loader.PackageInfo, file *ast.File) error {
|
||||||
|
|
||||||
gas.context.FileSet = prog.Fset
|
gas.context.FileSet = prog.Fset
|
||||||
|
gas.context.Config = gas.config
|
||||||
gas.context.Comments = ast.NewCommentMap(gas.context.FileSet, file, file.Comments)
|
gas.context.Comments = ast.NewCommentMap(gas.context.FileSet, file, file.Comments)
|
||||||
gas.context.Root = file
|
gas.context.Root = file
|
||||||
gas.context.Info = &pkg.Info
|
gas.context.Info = &pkg.Info
|
||||||
|
|
BIN
cmd/gas/gas
Executable file
BIN
cmd/gas/gas
Executable file
Binary file not shown.
|
@ -29,6 +29,7 @@ import (
|
||||||
|
|
||||||
"github.com/GoASTScanner/gas"
|
"github.com/GoASTScanner/gas"
|
||||||
"github.com/GoASTScanner/gas/output"
|
"github.com/GoASTScanner/gas/output"
|
||||||
|
"github.com/GoASTScanner/gas/rules"
|
||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/loader"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -138,7 +139,7 @@ func usage() {
|
||||||
fmt.Fprint(os.Stderr, "\n\nRULES:\n\n")
|
fmt.Fprint(os.Stderr, "\n\nRULES:\n\n")
|
||||||
|
|
||||||
// sorted rule list for eas of reading
|
// sorted rule list for eas of reading
|
||||||
rl := GetFullRuleList()
|
rl := rules.Generate()
|
||||||
keys := make([]string, 0, len(rl))
|
keys := make([]string, 0, len(rl))
|
||||||
for key := range rl {
|
for key := range rl {
|
||||||
keys = append(keys, key)
|
keys = append(keys, key)
|
||||||
|
@ -146,13 +147,13 @@ func usage() {
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
v := rl[k]
|
v := rl[k]
|
||||||
fmt.Fprintf(os.Stderr, "\t%s: %s\n", k, v.description)
|
fmt.Fprintf(os.Stderr, "\t%s: %s\n", k, v.Description)
|
||||||
}
|
}
|
||||||
fmt.Fprint(os.Stderr, "\n")
|
fmt.Fprint(os.Stderr, "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(gm) This needs to be refactored (potentially included in Analyzer)
|
// TODO(gm) This needs to be refactored (potentially included in Analyzer)
|
||||||
func analyzePackage(packageDirectory string, metrics *gas.Metrics, config map[string]interface{}, logger *log.Logger) ([]*gas.Issue, error) {
|
func analyzePackage(packageDirectory string, metrics *gas.Metrics, config gas.Config, logger *log.Logger, ruleDefs rules.RuleList) ([]*gas.Issue, error) {
|
||||||
|
|
||||||
basePackage, err := build.Default.ImportDir(packageDirectory, build.ImportComment)
|
basePackage, err := build.Default.ImportDir(packageDirectory, build.ImportComment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -174,7 +175,9 @@ func analyzePackage(packageDirectory string, metrics *gas.Metrics, config map[st
|
||||||
|
|
||||||
for _, pkg := range builtPackage.Created {
|
for _, pkg := range builtPackage.Created {
|
||||||
analyzer := gas.NewAnalyzer(config, logger)
|
analyzer := gas.NewAnalyzer(config, logger)
|
||||||
AddRules(&analyzer, config)
|
for _, rule := range ruleDefs {
|
||||||
|
analyzer.AddRule(rule.Create(config))
|
||||||
|
}
|
||||||
for _, file := range pkg.Files {
|
for _, file := range pkg.Files {
|
||||||
analyzer.ProcessPackage(builtPackage, pkg, file)
|
analyzer.ProcessPackage(builtPackage, pkg, file)
|
||||||
}
|
}
|
||||||
|
@ -227,10 +230,30 @@ func main() {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
config := buildConfig(incRules, excRules)
|
// Load config
|
||||||
|
config := gas.NewConfig()
|
||||||
|
if flagConfig != nil && *flagConfig != "" {
|
||||||
|
file, err := os.Open(*flagConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
if _, err := config.ReadFrom(file); err != nil {
|
||||||
|
logger.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filters := make([]rules.RuleFilter, 0)
|
||||||
|
if incRules != "" {
|
||||||
|
including := strings.Split(incRules, ",")
|
||||||
|
filters = append(filters, rules.NewRuleFilter(false, including...))
|
||||||
|
}
|
||||||
|
if excRules != "" {
|
||||||
|
excluding := strings.Split(excRules, ",")
|
||||||
|
filters = append(filters, rules.NewRuleFilter(true, excluding...))
|
||||||
|
}
|
||||||
|
ruleDefinitions := rules.Generate(filters...)
|
||||||
issues := make([]*gas.Issue, 0)
|
issues := make([]*gas.Issue, 0)
|
||||||
metrics := &gas.Metrics{}
|
metrics := &gas.Metrics{}
|
||||||
|
|
||||||
for _, arg := range flag.Args() {
|
for _, arg := range flag.Args() {
|
||||||
if arg == "./..." {
|
if arg == "./..." {
|
||||||
baseDirectory, err := os.Getwd()
|
baseDirectory, err := os.Getwd()
|
||||||
|
@ -246,7 +269,7 @@ func main() {
|
||||||
log.Printf("Skipping %s\n", path)
|
log.Printf("Skipping %s\n", path)
|
||||||
return filepath.SkipDir
|
return filepath.SkipDir
|
||||||
}
|
}
|
||||||
newIssues, err := analyzePackage(path, metrics, config, logger)
|
newIssues, err := analyzePackage(path, metrics, config, logger, ruleDefinitions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -256,7 +279,7 @@ func main() {
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
newIssues, err := analyzePackage(arg, metrics, config, logger)
|
newIssues, err := analyzePackage(arg, metrics, config, logger, ruleDefinitions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
|
@ -1,91 +0,0 @@
|
||||||
// (c) Copyright 2016 Hewlett Packard Enterprise Development LP
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"go/ast"
|
|
||||||
|
|
||||||
"github.com/GoASTScanner/gas"
|
|
||||||
"github.com/GoASTScanner/gas/rules"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RuleInfo struct {
|
|
||||||
description string
|
|
||||||
build func(map[string]interface{}) (gas.Rule, []ast.Node)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFullRuleList get the full list of all rules available to GAS
|
|
||||||
func GetFullRuleList() map[string]RuleInfo {
|
|
||||||
return map[string]RuleInfo{
|
|
||||||
// misc
|
|
||||||
"G101": RuleInfo{"Look for hardcoded credentials", rules.NewHardcodedCredentials},
|
|
||||||
"G102": RuleInfo{"Bind to all interfaces", rules.NewBindsToAllNetworkInterfaces},
|
|
||||||
"G103": RuleInfo{"Audit the use of unsafe block", rules.NewUsingUnsafe},
|
|
||||||
"G104": RuleInfo{"Audit errors not checked", rules.NewNoErrorCheck},
|
|
||||||
"G105": RuleInfo{"Audit the use of big.Exp function", rules.NewUsingBigExp},
|
|
||||||
|
|
||||||
// injection
|
|
||||||
"G201": RuleInfo{"SQL query construction using format string", rules.NewSqlStrFormat},
|
|
||||||
"G202": RuleInfo{"SQL query construction using string concatenation", rules.NewSqlStrConcat},
|
|
||||||
"G203": RuleInfo{"Use of unescaped data in HTML templates", rules.NewTemplateCheck},
|
|
||||||
"G204": RuleInfo{"Audit use of command execution", rules.NewSubproc},
|
|
||||||
|
|
||||||
// filesystem
|
|
||||||
"G301": RuleInfo{"Poor file permissions used when creating a directory", rules.NewMkdirPerms},
|
|
||||||
"G302": RuleInfo{"Poor file permisions used when creation file or using chmod", rules.NewFilePerms},
|
|
||||||
"G303": RuleInfo{"Creating tempfile using a predictable path", rules.NewBadTempFile},
|
|
||||||
|
|
||||||
// crypto
|
|
||||||
"G401": RuleInfo{"Detect the usage of DES, RC4, or MD5", rules.NewUsesWeakCryptography},
|
|
||||||
"G402": RuleInfo{"Look for bad TLS connection settings", rules.NewIntermediateTlsCheck},
|
|
||||||
"G403": RuleInfo{"Ensure minimum RSA key length of 2048 bits", rules.NewWeakKeyStrength},
|
|
||||||
"G404": RuleInfo{"Insecure random number source (rand)", rules.NewWeakRandCheck},
|
|
||||||
|
|
||||||
// blacklist
|
|
||||||
"G501": RuleInfo{"Import blacklist: crypto/md5", rules.NewBlacklist_crypto_md5},
|
|
||||||
"G502": RuleInfo{"Import blacklist: crypto/des", rules.NewBlacklist_crypto_des},
|
|
||||||
"G503": RuleInfo{"Import blacklist: crypto/rc4", rules.NewBlacklist_crypto_rc4},
|
|
||||||
"G504": RuleInfo{"Import blacklist: net/http/cgi", rules.NewBlacklist_net_http_cgi},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddRules(analyzer *gas.Analyzer, conf map[string]interface{}) {
|
|
||||||
var all map[string]RuleInfo
|
|
||||||
|
|
||||||
inc := conf["include"].([]string)
|
|
||||||
exc := conf["exclude"].([]string)
|
|
||||||
|
|
||||||
// add included rules
|
|
||||||
if len(inc) == 0 {
|
|
||||||
all = GetFullRuleList()
|
|
||||||
} else {
|
|
||||||
all = map[string]RuleInfo{}
|
|
||||||
tmp := GetFullRuleList()
|
|
||||||
for _, v := range inc {
|
|
||||||
if val, ok := tmp[v]; ok {
|
|
||||||
all[v] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove excluded rules
|
|
||||||
for _, v := range exc {
|
|
||||||
delete(all, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range all {
|
|
||||||
analyzer.AddRule(v.build(conf))
|
|
||||||
}
|
|
||||||
}
|
|
80
config.go
Normal file
80
config.go
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
package gas
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config is used to provide configuration and customization to each of the rules.
|
||||||
|
type Config map[string]interface{}
|
||||||
|
|
||||||
|
// NewConfig initializes a new configuration instance. The configuration data then
|
||||||
|
// needs to be loaded via c.ReadFrom(strings.NewReader("config data"))
|
||||||
|
// or from a *os.File.
|
||||||
|
func NewConfig() Config {
|
||||||
|
return make(Config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadFrom implements the io.ReaderFrom interface. This
|
||||||
|
// should be used with io.Reader to load configuration from
|
||||||
|
//file or from string etc.
|
||||||
|
func (c Config) ReadFrom(r io.Reader) (int64, error) {
|
||||||
|
data, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return int64(len(data)), err
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal(data, c); err != nil {
|
||||||
|
return int64(len(data)), err
|
||||||
|
}
|
||||||
|
return int64(len(data)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTo implements the io.WriteTo interface. This should
|
||||||
|
// be used to save or print out the configuration information.
|
||||||
|
func (c Config) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
data, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return int64(len(data)), err
|
||||||
|
}
|
||||||
|
return io.Copy(w, bytes.NewReader(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
// EnableRule will change the rule to the specified enabled state
|
||||||
|
func (c Config) EnableRule(ruleID string, enabled bool) {
|
||||||
|
if data, found := c["rules"]; found {
|
||||||
|
if rules, ok := data.(map[string]bool); ok {
|
||||||
|
rules[ruleID] = enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enabled returns a list of rules that are enabled
|
||||||
|
func (c Config) Enabled() []string {
|
||||||
|
if data, found := c["rules"]; found {
|
||||||
|
if rules, ok := data.(map[string]bool); ok {
|
||||||
|
enabled := make([]string, len(rules))
|
||||||
|
for ruleID := range rules {
|
||||||
|
enabled = append(enabled, ruleID)
|
||||||
|
}
|
||||||
|
return enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns the configuration section for a given rule
|
||||||
|
func (c Config) Get(ruleID string) (interface{}, error) {
|
||||||
|
section, found := c[ruleID]
|
||||||
|
if !found {
|
||||||
|
return nil, fmt.Errorf("Rule %s not in configuration", ruleID)
|
||||||
|
}
|
||||||
|
return section, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set section for a given rule
|
||||||
|
func (c Config) Set(ruleID string, val interface{}) {
|
||||||
|
c[ruleID] = val
|
||||||
|
}
|
|
@ -15,8 +15,9 @@
|
||||||
package rules
|
package rules
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/GoASTScanner/gas"
|
|
||||||
"go/ast"
|
"go/ast"
|
||||||
|
|
||||||
|
"github.com/GoASTScanner/gas"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UsingBigExp struct {
|
type UsingBigExp struct {
|
||||||
|
@ -31,7 +32,7 @@ func (r *UsingBigExp) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err erro
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
func NewUsingBigExp(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewUsingBigExp(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &UsingBigExp{
|
return &UsingBigExp{
|
||||||
pkg: "*math/big.Int",
|
pkg: "*math/big.Int",
|
||||||
calls: []string{"Exp"},
|
calls: []string{"Exp"},
|
||||||
|
|
|
@ -39,7 +39,7 @@ func (r *BindsToAllNetworkInterfaces) Match(n ast.Node, c *gas.Context) (gi *gas
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBindsToAllNetworkInterfaces(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewBindsToAllNetworkInterfaces(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &BindsToAllNetworkInterfaces{
|
return &BindsToAllNetworkInterfaces{
|
||||||
call: regexp.MustCompile(`^(net|tls)\.Listen$`),
|
call: regexp.MustCompile(`^(net|tls)\.Listen$`),
|
||||||
pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`),
|
pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`),
|
||||||
|
|
|
@ -34,7 +34,7 @@ func (r *BlacklistImport) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlacklist_crypto_md5(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewBlacklist_crypto_md5(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &BlacklistImport{
|
return &BlacklistImport{
|
||||||
MetaData: gas.MetaData{
|
MetaData: gas.MetaData{
|
||||||
Severity: gas.High,
|
Severity: gas.High,
|
||||||
|
@ -45,7 +45,7 @@ func NewBlacklist_crypto_md5(conf map[string]interface{}) (gas.Rule, []ast.Node)
|
||||||
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlacklist_crypto_des(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewBlacklist_crypto_des(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &BlacklistImport{
|
return &BlacklistImport{
|
||||||
MetaData: gas.MetaData{
|
MetaData: gas.MetaData{
|
||||||
Severity: gas.High,
|
Severity: gas.High,
|
||||||
|
@ -56,7 +56,7 @@ func NewBlacklist_crypto_des(conf map[string]interface{}) (gas.Rule, []ast.Node)
|
||||||
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlacklist_crypto_rc4(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewBlacklist_crypto_rc4(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &BlacklistImport{
|
return &BlacklistImport{
|
||||||
MetaData: gas.MetaData{
|
MetaData: gas.MetaData{
|
||||||
Severity: gas.High,
|
Severity: gas.High,
|
||||||
|
@ -67,7 +67,7 @@ func NewBlacklist_crypto_rc4(conf map[string]interface{}) (gas.Rule, []ast.Node)
|
||||||
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBlacklist_net_http_cgi(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewBlacklist_net_http_cgi(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &BlacklistImport{
|
return &BlacklistImport{
|
||||||
MetaData: gas.MetaData{
|
MetaData: gas.MetaData{
|
||||||
Severity: gas.High,
|
Severity: gas.High,
|
||||||
|
|
|
@ -70,7 +70,7 @@ func (r *NoErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNoErrorCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewNoErrorCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
|
|
||||||
// TODO(gm) Come up with sensible defaults here. Or flip it to use a
|
// TODO(gm) Come up with sensible defaults here. Or flip it to use a
|
||||||
// black list instead.
|
// black list instead.
|
||||||
|
|
|
@ -56,7 +56,7 @@ func (r *FilePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFilePerms(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewFilePerms(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
mode := getConfiguredMode(conf, "G302", 0600)
|
mode := getConfiguredMode(conf, "G302", 0600)
|
||||||
return &FilePermissions{
|
return &FilePermissions{
|
||||||
mode: mode,
|
mode: mode,
|
||||||
|
@ -70,7 +70,7 @@ func NewFilePerms(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMkdirPerms(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewMkdirPerms(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
mode := getConfiguredMode(conf, "G301", 0700)
|
mode := getConfiguredMode(conf, "G301", 0700)
|
||||||
return &FilePermissions{
|
return &FilePermissions{
|
||||||
mode: mode,
|
mode: mode,
|
||||||
|
|
|
@ -100,7 +100,7 @@ func (r *Credentials) matchGenDecl(decl *ast.GenDecl, ctx *gas.Context) (*gas.Is
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHardcodedCredentials(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewHardcodedCredentials(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
pattern := `(?i)passwd|pass|password|pwd|secret|token`
|
pattern := `(?i)passwd|pass|password|pwd|secret|token`
|
||||||
entropyThreshold := 80.0
|
entropyThreshold := 80.0
|
||||||
perCharThreshold := 3.0
|
perCharThreshold := 3.0
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (w *WeakRand) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWeakRandCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewWeakRandCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &WeakRand{
|
return &WeakRand{
|
||||||
funcNames: []string{"Read", "Int"},
|
funcNames: []string{"Read", "Int"},
|
||||||
packagePath: "math/rand",
|
packagePath: "math/rand",
|
||||||
|
|
|
@ -37,7 +37,7 @@ func (w *WeakKeyStrength) Match(n ast.Node, c *gas.Context) (*gas.Issue, error)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWeakKeyStrength(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewWeakKeyStrength(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
bits := 2048
|
bits := 2048
|
||||||
return &WeakKeyStrength{
|
return &WeakKeyStrength{
|
||||||
pattern: regexp.MustCompile(`^rsa\.GenerateKey$`),
|
pattern: regexp.MustCompile(`^rsa\.GenerateKey$`),
|
||||||
|
|
87
rules/rulelist.go
Normal file
87
rules/rulelist.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
// (c) Copyright 2016 Hewlett Packard Enterprise Development LP
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package rules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
|
||||||
|
"github.com/GoASTScanner/gas"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RuleDefinition struct {
|
||||||
|
Description string
|
||||||
|
Create func(c gas.Config) (gas.Rule, []ast.Node)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleList map[string]RuleDefinition
|
||||||
|
|
||||||
|
type RuleFilter func(string) bool
|
||||||
|
|
||||||
|
func NewRuleFilter(action bool, ruleIDs ...string) RuleFilter {
|
||||||
|
rulelist := make(map[string]bool)
|
||||||
|
for _, rule := range ruleIDs {
|
||||||
|
rulelist[rule] = true
|
||||||
|
}
|
||||||
|
return func(rule string) bool {
|
||||||
|
if _, found := rulelist[rule]; found {
|
||||||
|
return action
|
||||||
|
}
|
||||||
|
return !action
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the list of rules to use
|
||||||
|
func Generate(filters ...RuleFilter) RuleList {
|
||||||
|
rules := map[string]RuleDefinition{
|
||||||
|
// misc
|
||||||
|
"G101": RuleDefinition{"Look for hardcoded credentials", NewHardcodedCredentials},
|
||||||
|
"G102": RuleDefinition{"Bind to all interfaces", NewBindsToAllNetworkInterfaces},
|
||||||
|
"G103": RuleDefinition{"Audit the use of unsafe block", NewUsingUnsafe},
|
||||||
|
"G104": RuleDefinition{"Audit errors not checked", NewNoErrorCheck},
|
||||||
|
"G105": RuleDefinition{"Audit the use of big.Exp function", NewUsingBigExp},
|
||||||
|
|
||||||
|
// injection
|
||||||
|
"G201": RuleDefinition{"SQL query construction using format string", NewSqlStrFormat},
|
||||||
|
"G202": RuleDefinition{"SQL query construction using string concatenation", NewSqlStrConcat},
|
||||||
|
"G203": RuleDefinition{"Use of unescaped data in HTML templates", NewTemplateCheck},
|
||||||
|
"G204": RuleDefinition{"Audit use of command execution", NewSubproc},
|
||||||
|
|
||||||
|
// filesystem
|
||||||
|
"G301": RuleDefinition{"Poor file permissions used when creating a directory", NewMkdirPerms},
|
||||||
|
"G302": RuleDefinition{"Poor file permisions used when creation file or using chmod", NewFilePerms},
|
||||||
|
"G303": RuleDefinition{"Creating tempfile using a predictable path", NewBadTempFile},
|
||||||
|
|
||||||
|
// crypto
|
||||||
|
"G401": RuleDefinition{"Detect the usage of DES, RC4, or MD5", NewUsesWeakCryptography},
|
||||||
|
"G402": RuleDefinition{"Look for bad TLS connection settings", NewIntermediateTlsCheck},
|
||||||
|
"G403": RuleDefinition{"Ensure minimum RSA key length of 2048 bits", NewWeakKeyStrength},
|
||||||
|
"G404": RuleDefinition{"Insecure random number source (rand)", NewWeakRandCheck},
|
||||||
|
|
||||||
|
// blacklist
|
||||||
|
"G501": RuleDefinition{"Import blacklist: crypto/md5", NewBlacklist_crypto_md5},
|
||||||
|
"G502": RuleDefinition{"Import blacklist: crypto/des", NewBlacklist_crypto_des},
|
||||||
|
"G503": RuleDefinition{"Import blacklist: crypto/rc4", NewBlacklist_crypto_rc4},
|
||||||
|
"G504": RuleDefinition{"Import blacklist: net/http/cgi", NewBlacklist_net_http_cgi},
|
||||||
|
}
|
||||||
|
|
||||||
|
for rule := range rules {
|
||||||
|
for _, filter := range filters {
|
||||||
|
if filter(rule) {
|
||||||
|
delete(rules, rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rules
|
||||||
|
}
|
|
@ -56,7 +56,7 @@ func (s *SqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSqlStrConcat(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewSqlStrConcat(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &SqlStrConcat{
|
return &SqlStrConcat{
|
||||||
SqlStatement: SqlStatement{
|
SqlStatement: SqlStatement{
|
||||||
pattern: regexp.MustCompile(`(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `),
|
pattern: regexp.MustCompile(`(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `),
|
||||||
|
@ -84,7 +84,7 @@ func (s *SqlStrFormat) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err err
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSqlStrFormat(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewSqlStrFormat(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &SqlStrFormat{
|
return &SqlStrFormat{
|
||||||
call: regexp.MustCompile(`^fmt\.Sprintf$`),
|
call: regexp.MustCompile(`^fmt\.Sprintf$`),
|
||||||
SqlStatement: SqlStatement{
|
SqlStatement: SqlStatement{
|
||||||
|
|
|
@ -49,7 +49,7 @@ func (r *Subprocess) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSubproc(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewSubproc(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &Subprocess{
|
return &Subprocess{
|
||||||
pattern: regexp.MustCompile(`^exec\.Command|syscall\.Exec$`),
|
pattern: regexp.MustCompile(`^exec\.Command|syscall\.Exec$`),
|
||||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (t *BadTempFile) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err erro
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBadTempFile(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewBadTempFile(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &BadTempFile{
|
return &BadTempFile{
|
||||||
call: regexp.MustCompile(`ioutil\.WriteFile|os\.Create`),
|
call: regexp.MustCompile(`ioutil\.WriteFile|os\.Create`),
|
||||||
args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`),
|
args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`),
|
||||||
|
|
|
@ -37,7 +37,7 @@ func (t *TemplateCheck) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err er
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTemplateCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewTemplateCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &TemplateCheck{
|
return &TemplateCheck{
|
||||||
call: regexp.MustCompile(`^template\.(HTML|JS|URL)$`),
|
call: regexp.MustCompile(`^template\.(HTML|JS|URL)$`),
|
||||||
MetaData: gas.MetaData{
|
MetaData: gas.MetaData{
|
||||||
|
|
|
@ -121,7 +121,7 @@ func (t *InsecureConfigTLS) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, er
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewModernTlsCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewModernTlsCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
// https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
|
// https://wiki.mozilla.org/Security/Server_Side_TLS#Modern_compatibility
|
||||||
return &InsecureConfigTLS{
|
return &InsecureConfigTLS{
|
||||||
pattern: regexp.MustCompile(`^tls\.Config$`),
|
pattern: regexp.MustCompile(`^tls\.Config$`),
|
||||||
|
@ -136,7 +136,7 @@ func NewModernTlsCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
||||||
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIntermediateTlsCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewIntermediateTlsCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
// https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
|
// https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
|
||||||
return &InsecureConfigTLS{
|
return &InsecureConfigTLS{
|
||||||
pattern: regexp.MustCompile(`^tls\.Config$`),
|
pattern: regexp.MustCompile(`^tls\.Config$`),
|
||||||
|
@ -162,7 +162,7 @@ func NewIntermediateTlsCheck(conf map[string]interface{}) (gas.Rule, []ast.Node)
|
||||||
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
}, []ast.Node{(*ast.CompositeLit)(nil)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCompatTlsCheck(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewCompatTlsCheck(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
// https://wiki.mozilla.org/Security/Server_Side_TLS#Old_compatibility_.28default.29
|
// https://wiki.mozilla.org/Security/Server_Side_TLS#Old_compatibility_.28default.29
|
||||||
return &InsecureConfigTLS{
|
return &InsecureConfigTLS{
|
||||||
pattern: regexp.MustCompile(`^tls\.Config$`),
|
pattern: regexp.MustCompile(`^tls\.Config$`),
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (r *UsingUnsafe) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err erro
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUsingUnsafe(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewUsingUnsafe(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
return &UsingUnsafe{
|
return &UsingUnsafe{
|
||||||
pkg: "unsafe",
|
pkg: "unsafe",
|
||||||
calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"},
|
calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"},
|
||||||
|
|
|
@ -36,7 +36,7 @@ func (r *UsesWeakCryptography) Match(n ast.Node, c *gas.Context) (*gas.Issue, er
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses des.* md5.* or rc4.*
|
// Uses des.* md5.* or rc4.*
|
||||||
func NewUsesWeakCryptography(conf map[string]interface{}) (gas.Rule, []ast.Node) {
|
func NewUsesWeakCryptography(conf gas.Config) (gas.Rule, []ast.Node) {
|
||||||
calls := make(map[string][]string)
|
calls := make(map[string][]string)
|
||||||
calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"}
|
calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"}
|
||||||
calls["crypto/md5"] = []string{"New", "Sum"}
|
calls["crypto/md5"] = []string{"New", "Sum"}
|
||||||
|
|
Loading…
Reference in a new issue