mirror of
https://github.com/securego/gosec.git
synced 2024-12-25 12:05:52 +00:00
Merge pull request #42 from HewlettPackard/code_docs
Adding some inline documentation for godoc
This commit is contained in:
commit
bb42840644
5 changed files with 53 additions and 12 deletions
|
@ -12,6 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package core holds the central scanning logic used by GAS
|
||||
package core
|
||||
|
||||
import (
|
||||
|
@ -26,6 +27,9 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// The Context is populated with data parsed from the source code as it is scanned.
|
||||
// It is passed through to all rule functions as they are called. Rules may use
|
||||
// this data in conjunction withe the encoutered AST node.
|
||||
type Context struct {
|
||||
FileSet *token.FileSet
|
||||
Comments ast.CommentMap
|
||||
|
@ -35,12 +39,17 @@ type Context struct {
|
|||
Config map[string]interface{}
|
||||
}
|
||||
|
||||
// The Rule interface used by all rules supported by GAS.
|
||||
type Rule interface {
|
||||
Match(ast.Node, *Context) (*Issue, error)
|
||||
}
|
||||
|
||||
// A RuleSet maps lists of rules to the type of AST node they should be run on.
|
||||
// The anaylzer will only invoke rules contained in the list associated with the
|
||||
// type of AST node it is currently visiting.
|
||||
type RuleSet map[reflect.Type][]Rule
|
||||
|
||||
// Metrics used when reporting information about a scanning run.
|
||||
type Metrics struct {
|
||||
NumFiles int `json:"files"`
|
||||
NumLines int `json:"lines"`
|
||||
|
@ -48,6 +57,8 @@ type Metrics struct {
|
|||
NumFound int `json:"found"`
|
||||
}
|
||||
|
||||
// The Analyzer object is the main object of GAS. It has methods traverse an AST
|
||||
// and invoke the correct checking rules as on each node as required.
|
||||
type Analyzer struct {
|
||||
ignoreNosec bool
|
||||
ruleset RuleSet
|
||||
|
@ -57,6 +68,7 @@ type Analyzer struct {
|
|||
Stats Metrics `json:"metrics"`
|
||||
}
|
||||
|
||||
// NewAnalyzer buildas a new anaylzer.
|
||||
func NewAnalyzer(conf map[string]interface{}, logger *log.Logger) Analyzer {
|
||||
if logger == nil {
|
||||
logger = log.New(os.Stdout, "[gas]", 0)
|
||||
|
@ -104,6 +116,8 @@ func (gas *Analyzer) process(filename string, source interface{}) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// AddRule adds a rule into a rule set list mapped to the given AST node's type.
|
||||
// The node is only needed for its type and is not otherwise used.
|
||||
func (gas *Analyzer) AddRule(r Rule, n ast.Node) {
|
||||
t := reflect.TypeOf(n)
|
||||
if val, ok := gas.ruleset[t]; ok {
|
||||
|
@ -113,6 +127,8 @@ func (gas *Analyzer) AddRule(r Rule, n ast.Node) {
|
|||
}
|
||||
}
|
||||
|
||||
// Process reads in a source file, convert it to an AST and traverse it.
|
||||
// Rule methods added with AddRule will be invoked as necessary.
|
||||
func (gas *Analyzer) Process(filename string) error {
|
||||
err := gas.process(filename, nil)
|
||||
fun := func(f *token.File) bool {
|
||||
|
@ -123,6 +139,9 @@ func (gas *Analyzer) Process(filename string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// ProcessSource will convert a source code string into an AST and traverse it.
|
||||
// Rule methods added with AddRule will be invoked as necessary. The string is
|
||||
// identified by the filename given but no file IO will be done.
|
||||
func (gas *Analyzer) ProcessSource(filename string, source string) error {
|
||||
err := gas.process(filename, source)
|
||||
fun := func(f *token.File) bool {
|
||||
|
@ -133,7 +152,8 @@ func (gas *Analyzer) ProcessSource(filename string, source string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (gas *Analyzer) Ignore(n ast.Node) bool {
|
||||
// ignore a node (and sub-tree) if it is tagged with a "nosec" comment
|
||||
func (gas *Analyzer) ignore(n ast.Node) bool {
|
||||
if groups, ok := gas.context.Comments[n]; ok && !gas.ignoreNosec {
|
||||
for _, group := range groups {
|
||||
if strings.Contains(group.Text(), "nosec") {
|
||||
|
@ -145,8 +165,10 @@ func (gas *Analyzer) Ignore(n ast.Node) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// Visit runs the GAS visitor logic over an AST created by parsing go code.
|
||||
// Rule methods added with AddRule will be invoked as necessary.
|
||||
func (gas *Analyzer) Visit(n ast.Node) ast.Visitor {
|
||||
if !gas.Ignore(n) {
|
||||
if !gas.ignore(n) {
|
||||
if val, ok := gas.ruleset[reflect.TypeOf(n)]; ok {
|
||||
for _, rule := range val {
|
||||
ret, err := rule.Match(n, &gas.context)
|
||||
|
|
|
@ -37,6 +37,7 @@ func selectName(n ast.Node, s reflect.Type) (string, bool) {
|
|||
return "", false
|
||||
}
|
||||
|
||||
// MatchCall will match an ast.CallNode if its method name obays the given regex.
|
||||
func MatchCall(n ast.Node, r *regexp.Regexp) *ast.CallExpr {
|
||||
t := reflect.TypeOf(&ast.CallExpr{})
|
||||
if name, ok := selectName(n, t); ok && r.MatchString(name) {
|
||||
|
@ -45,6 +46,7 @@ func MatchCall(n ast.Node, r *regexp.Regexp) *ast.CallExpr {
|
|||
return nil
|
||||
}
|
||||
|
||||
// MatcMatchCompLit hCall will match an ast.CompositeLit if its string value obays the given regex.
|
||||
func MatchCompLit(n ast.Node, r *regexp.Regexp) *ast.CompositeLit {
|
||||
t := reflect.TypeOf(&ast.CompositeLit{})
|
||||
if name, ok := selectName(n, t); ok && r.MatchString(name) {
|
||||
|
@ -53,6 +55,7 @@ func MatchCompLit(n ast.Node, r *regexp.Regexp) *ast.CompositeLit {
|
|||
return nil
|
||||
}
|
||||
|
||||
// GetInt will read and return an integer value from an ast.BasicLit
|
||||
func GetInt(n ast.Node) (int64, error) {
|
||||
if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.INT {
|
||||
return strconv.ParseInt(node.Value, 0, 64)
|
||||
|
@ -60,6 +63,7 @@ func GetInt(n ast.Node) (int64, error) {
|
|||
return 0, fmt.Errorf("Unexpected AST node type: %T", n)
|
||||
}
|
||||
|
||||
// GetInt will read and return a float value from an ast.BasicLit
|
||||
func GetFloat(n ast.Node) (float64, error) {
|
||||
if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.FLOAT {
|
||||
return strconv.ParseFloat(node.Value, 64)
|
||||
|
@ -67,6 +71,7 @@ func GetFloat(n ast.Node) (float64, error) {
|
|||
return 0.0, fmt.Errorf("Unexpected AST node type: %T", n)
|
||||
}
|
||||
|
||||
// GetInt will read and return a char value from an ast.BasicLit
|
||||
func GetChar(n ast.Node) (byte, error) {
|
||||
if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.CHAR {
|
||||
return node.Value[0], nil
|
||||
|
@ -74,6 +79,7 @@ func GetChar(n ast.Node) (byte, error) {
|
|||
return 0, fmt.Errorf("Unexpected AST node type: %T", n)
|
||||
}
|
||||
|
||||
// GetInt will read and return a string value from an ast.BasicLit
|
||||
func GetString(n ast.Node) (string, error) {
|
||||
if node, ok := n.(*ast.BasicLit); ok && node.Kind == token.STRING {
|
||||
return strconv.Unquote(node.Value)
|
||||
|
|
|
@ -20,33 +20,39 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
// Score type used by severity and confidence values
|
||||
type Score int
|
||||
|
||||
const (
|
||||
Low Score = iota
|
||||
Medium
|
||||
High
|
||||
Low Score = iota // Low value
|
||||
Medium // Medium value
|
||||
High // High value
|
||||
)
|
||||
|
||||
// An Issue is returnd by a GAS rule if it discovers an issue with the scanned code.
|
||||
type Issue struct {
|
||||
Severity Score `json:"severity"`
|
||||
Confidence Score `json:"confidence"`
|
||||
What string `json:"details"`
|
||||
File string `json:"file"`
|
||||
Code string `json:"code"`
|
||||
Line int `json:"line"`
|
||||
Severity Score `json:"severity"` // issue severity (how problematic it is)
|
||||
Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it)
|
||||
What string `json:"details"` // Human readable explanation
|
||||
File string `json:"file"` // File name we found it in
|
||||
Code string `json:"code"` // Impacted code line
|
||||
Line int `json:"line"` // Line number in file
|
||||
}
|
||||
|
||||
// MetaData is embedded in all GAS rules. The Severity, Confidence and What message
|
||||
// will be passed tbhrough to reported issues.
|
||||
type MetaData struct {
|
||||
Severity Score
|
||||
Confidence Score
|
||||
What string
|
||||
}
|
||||
|
||||
// MarshalJSON is used convert a Score object into a JSON representation
|
||||
func (c Score) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(c.String())
|
||||
}
|
||||
|
||||
// String converts a Score into a string
|
||||
func (c Score) String() string {
|
||||
switch c {
|
||||
case High:
|
||||
|
@ -74,6 +80,7 @@ func codeSnippet(file *os.File, start int64, end int64, n ast.Node) (string, err
|
|||
return string(buf), nil
|
||||
}
|
||||
|
||||
// NewIssue creates a new Issue
|
||||
func NewIssue(ctx *Context, node ast.Node, desc string, severity Score, confidence Score) *Issue {
|
||||
var code string
|
||||
fobj := ctx.FileSet.File(node.Pos())
|
||||
|
|
|
@ -53,6 +53,9 @@ func resolveCallExpr(n *ast.CallExpr, c *Context) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// TryResolve will attempt, given a subtree starting at some ATS node, to resolve
|
||||
// all values contained within to a known constant. It is used to check for any
|
||||
// unkown values in compound expressions.
|
||||
func TryResolve(n ast.Node, c *Context) bool {
|
||||
switch node := n.(type) {
|
||||
case *ast.BasicLit:
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
"reflect"
|
||||
)
|
||||
|
||||
// A selector function. This is like a visitor, but has a richer interface. It
|
||||
// SelectFunc is like an AST visitor, but has a richer interface. It
|
||||
// is called with the current ast.Node being visitied and that nodes depth in
|
||||
// the tree. The function can return true to continue traversing the tree, or
|
||||
// false to end traversal here.
|
||||
|
@ -379,6 +379,9 @@ func Select(s Selector, n ast.Node, bits ...reflect.Type) {
|
|||
depthWalk(n, 0, fun)
|
||||
}
|
||||
|
||||
// SimpleSelect will try to match a path through a sub-tree starting at a given AST node.
|
||||
// The type of each node in the path at a given depth must match its entry in list of
|
||||
// node types given.
|
||||
func SimpleSelect(n ast.Node, bits ...reflect.Type) ast.Node {
|
||||
var found ast.Node
|
||||
fun := func(n ast.Node, d int) bool {
|
||||
|
|
Loading…
Reference in a new issue