mirror of
https://github.com/securego/gosec.git
synced 2024-11-06 03:55:50 +00:00
e3b1d33b95
This re-works the way that CLI options are passed through to the analyzer so that they can act as overrides for config options. If not given on the CLI, options will come from a config file. If no file is used then a default value is chosen. Two lists are also populated with tests to include or exclude. These lists are not used for now but will eventually replace the way we select test to run in a future patch to follow.
215 lines
5.5 KiB
Go
215 lines
5.5 KiB
Go
// (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 (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
gas "github.com/HewlettPackard/gas/core"
|
|
"github.com/HewlettPackard/gas/output"
|
|
)
|
|
|
|
// #nosec flag
|
|
var flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set")
|
|
|
|
// format output
|
|
var flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, csv of text")
|
|
|
|
// output file
|
|
var flagOutput = flag.String("out", "", "Set output file for results")
|
|
|
|
var flagConfig = flag.String("conf", "", "Path to optional config file")
|
|
|
|
var usageText = `
|
|
GAS - Go AST Scanner
|
|
|
|
Gas analyzes Go source code to look for common programming mistakes that
|
|
can lead to security problems.
|
|
|
|
USAGE:
|
|
|
|
# Check a single Go file
|
|
$ gas example.go
|
|
|
|
# Check all files under the current directory and save results in
|
|
# json format.
|
|
$ gas -fmt=json -out=results.json ./...
|
|
|
|
# Run a specific set of rules (by default all rules will be run):
|
|
$ gas -rule=sql -rule=sql ./...
|
|
|
|
`
|
|
|
|
var logger *log.Logger
|
|
|
|
func extendConfList(conf map[string]interface{}, name string, input []string) {
|
|
if val, ok := conf[name]; ok {
|
|
if data, ok := val.(*[]string); ok {
|
|
conf[name] = append(*data, input...)
|
|
} else {
|
|
logger.Fatal("Config item must be a string list: ", name)
|
|
}
|
|
} else {
|
|
conf[name] = []string{}
|
|
}
|
|
}
|
|
|
|
func buildConfig(incRules string, excRules string) map[string]interface{} {
|
|
config := make(map[string]interface{})
|
|
if flagConfig != nil && *flagConfig != "" { // parse config if we have one
|
|
if data, err := ioutil.ReadFile(*flagConfig); err == nil {
|
|
if err := json.Unmarshal(data, &(config)); err != nil {
|
|
logger.Fatal("Could not parse JSON config: ", *flagConfig, ": ", err)
|
|
}
|
|
} else {
|
|
logger.Fatal("Could not read config file: ", *flagConfig)
|
|
}
|
|
}
|
|
|
|
// add in CLI include and exclude data
|
|
extendConfList(config, "include", strings.Split(incRules, ","))
|
|
extendConfList(config, "exclude", strings.Split(excRules, ","))
|
|
|
|
// override ignoreNosec if given on CLI
|
|
if flagIgnoreNoSec != nil {
|
|
config["ignoreNosec"] = *flagIgnoreNoSec
|
|
} else {
|
|
val, ok := config["ignoreNosec"]
|
|
if !ok {
|
|
config["ignoreNosec"] = false
|
|
} else if _, ok := val.(bool); !ok {
|
|
logger.Fatal("Config value must be a bool: 'ignoreNosec'")
|
|
}
|
|
}
|
|
|
|
return config
|
|
}
|
|
|
|
func usage() {
|
|
fmt.Fprintln(os.Stderr, usageText)
|
|
fmt.Fprint(os.Stderr, "OPTIONS:\n\n")
|
|
flag.PrintDefaults()
|
|
}
|
|
|
|
func main() {
|
|
|
|
// Setup usage description
|
|
flag.Usage = usage
|
|
|
|
// Exclude files
|
|
var excluded filelist = []string{"*_test.go"}
|
|
flag.Var(&excluded, "skip", "File pattern to exclude from scan")
|
|
|
|
// Rule configuration
|
|
rules := newRulelist()
|
|
flag.Var(&rules, "rule", "GAS rules enabled when performing a scan")
|
|
|
|
incRules := ""
|
|
flag.StringVar(&incRules, "include", "", "comma sperated list of rules to include")
|
|
|
|
excRules := ""
|
|
flag.StringVar(&excRules, "exclude", "", "comma sperated list of rules to exclude")
|
|
|
|
// Custom commands / utilities to run instead of default analyzer
|
|
tools := newUtils()
|
|
flag.Var(tools, "tool", "GAS utilities to assist with rule development")
|
|
|
|
// Parse command line arguments
|
|
flag.Parse()
|
|
|
|
// Setup logging
|
|
logger = log.New(os.Stderr, "[gas]", log.LstdFlags)
|
|
|
|
// Ensure at least one file was specified
|
|
if flag.NArg() == 0 {
|
|
|
|
fmt.Fprintf(os.Stderr, "\nerror: FILE [FILE...] or './...' expected\n")
|
|
flag.Usage()
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Run utils instead of analysis
|
|
if len(tools.call) > 0 {
|
|
tools.run(flag.Args()...)
|
|
os.Exit(0)
|
|
}
|
|
|
|
// Setup analyzer
|
|
config := buildConfig(incRules, excRules)
|
|
|
|
analyzer := gas.NewAnalyzer(config, logger)
|
|
if !rules.overwritten {
|
|
rules.useDefaults()
|
|
}
|
|
rules.apply(&analyzer)
|
|
|
|
// Traverse directory structure if './...'
|
|
if flag.NArg() == 1 && flag.Arg(0) == "./..." {
|
|
|
|
cwd, err := os.Getwd()
|
|
if err != nil {
|
|
logger.Fatalf("Unable to traverse path %s, reason - %s", flag.Arg(0), err)
|
|
}
|
|
filepath.Walk(cwd, func(path string, info os.FileInfo, err error) error {
|
|
if excluded.Contains(path) && info.IsDir() {
|
|
logger.Printf("Skipping %s\n", path)
|
|
return filepath.SkipDir
|
|
}
|
|
if !info.IsDir() && !excluded.Contains(path) &&
|
|
strings.HasSuffix(path, ".go") {
|
|
err = analyzer.Process(path)
|
|
if err != nil {
|
|
logger.Fatal(err)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
|
|
} else {
|
|
|
|
// Process each file individually
|
|
for _, filename := range flag.Args() {
|
|
if finfo, err := os.Stat(filename); err == nil {
|
|
if !finfo.IsDir() && !excluded.Contains(filename) &&
|
|
strings.HasSuffix(filename, ".go") {
|
|
if err = analyzer.Process(filename); err != nil {
|
|
logger.Fatal(err)
|
|
}
|
|
}
|
|
} else {
|
|
logger.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create output report
|
|
if *flagOutput != "" {
|
|
outfile, err := os.Create(*flagOutput)
|
|
if err != nil {
|
|
logger.Fatalf("Couldn't open: %s for writing. Reason - %s", *flagOutput, err)
|
|
}
|
|
defer outfile.Close()
|
|
output.CreateReport(outfile, *flagFormat, &analyzer)
|
|
} else {
|
|
output.CreateReport(os.Stdout, *flagFormat, &analyzer)
|
|
}
|
|
}
|