Skip to content

Commit 09b9143

Browse files
Documentation on adding new rules and analyzers (#1262)
* Create CONTRIBUTING.md Add some docs for developers who want to extend gosec * Address comments from ccojocar * Update CONTRIBUTING.md Co-authored-by: Oleksandr Redko <oleksandr.red+github@gmail.com> * Update CONTRIBUTING.md Co-authored-by: Oleksandr Redko <oleksandr.red+github@gmail.com> * Update README.md Co-authored-by: Oleksandr Redko <oleksandr.red+github@gmail.com> * Update CONTRIBUTING.md Co-authored-by: Oleksandr Redko <oleksandr.red+github@gmail.com> * Update CONTRIBUTING.md Co-authored-by: Oleksandr Redko <oleksandr.red+github@gmail.com> * Linting/formatting pass * Update CONTRIBUTING.md * Update README.md Fix phrasing to be clearer --------- Co-authored-by: Oleksandr Redko <oleksandr.red+github@gmail.com>
1 parent 1bd92a8 commit 09b9143

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed

CONTRIBUTING.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Contributing
2+
3+
## Adding a new rule
4+
5+
New rules can be implemented in two ways:
6+
7+
- as a `gosec.Rule` -- these define an arbitrary function which will be called on every AST node in the analyzed file, and are appropriate for rules that mostly need to reason about a single statement.
8+
- as an Analyzer -- these can operate on the entire program, and receive an [SSA](https://pkg.go.dev/golang.org/x/tools/go/ssa) representation of the package. This type of rule is useful when you need to perform a more complex analysis that requires a great deal of context.
9+
10+
### Adding a gosec.Rule
11+
12+
1. Copy an existing rule file as a starting point-- `./rules/unsafe.go` is a good option, as it implements a very simple rule with no additional supporting logic. Put the copied file in the `./rules/` directory.
13+
2. Change the name of the rule constructor function and of the types in the rule file you've copied so they will be unique.
14+
3. Edit the `Generate` function in `./rules/rulelist.go` to include your rule.
15+
4. Add a RuleID to CWE ID mapping for your rule to the `ruleToCWE` map in `./issue/issue.go`. If you need a CWE that isn't already defined in `./cwe/data.go`, add it to the `idWeaknessess` map in that file.
16+
5. Use `make` to compile `gosec`. The binary will now contain your rule.
17+
18+
To make your rule actually useful, you will likely want to use the support functions defined in `./resolve.go`, `./helpers.go` and `./call_list.go`. There are inline comments explaining the purpose of most of these functions, and you can find usage examples in the existing rule files.
19+
20+
### Adding an Analyzer
21+
22+
1. Create a new go file under `./analyzers/` with the following scaffolding in it:
23+
24+
```go
25+
package analyzers
26+
27+
import (
28+
"fmt"
29+
30+
"golang.org/x/tools/go/analysis"
31+
"golang.org/x/tools/go/analysis/passes/buildssa"
32+
"github.com/securego/gosec/v2/issue"
33+
)
34+
35+
const defaultIssueDescriptionMyAnalyzer = "My new analyzer!"
36+
37+
func newMyAnalyzer(id string, description string) *analysis.Analyzer {
38+
return &analysis.Analyzer{
39+
Name: id,
40+
Doc: description,
41+
Run: runMyAnalyzer,
42+
Requires: []*analysis.Analyzer{buildssa.Analyzer},
43+
}
44+
}
45+
46+
func runMyAnalyzer(pass *analysis.Pass) (interface{}, error) {
47+
ssaResult, err := getSSAResult(pass)
48+
if err != nil {
49+
return nil, fmt.Errorf("building ssa representation: %w", err)
50+
}
51+
var issues []*issue.Issue
52+
fmt.Printf("My Analyzer ran! %+v\n", ssaResult)
53+
54+
return issues, nil
55+
}
56+
```
57+
58+
2. Add the analyzer to `./analyzers/analyzerslist.go` in the `defaultAnalyzers` variable under an entry like `{"G999", "My test analyzer", newMyAnalyzer}`
59+
3. Add a RuleID to CWE ID mapping for your rule to the `ruleToCWE` map in `./issue/issue.go`. If you need a CWE that isn't already defined in `./cwe/data.go`, add it to the `idWeaknessess` map in that file.
60+
4. `make`; then run the `gosec` binary produced. You should see the output from our print statement.
61+
5. You now have a working example analyzer to play with-- look at the other implemented analyzers for ideas on how to make useful rules.
62+
63+
## Developing your rule
64+
65+
There are some utility tools which are useful for analyzing the SSA and AST representation `gosec` works with before writing rules or analyzers.
66+
67+
For instance to dump the SSA, the [ssadump](https://pkg.go.dev/golang.org/x/tools/cmd/ssadump) tool can be used as following:
68+
69+
```bash
70+
ssadump -build F main.go
71+
```
72+
73+
Consult the documentation for ssadump for an overview of available output flags and options.
74+
75+
For outputting the AST and supporting information, there is a utility tool in <https://github.com/securego/gosec/blob/master/cmd/gosecutil/tools.go> which can be compiled and used as standalone.
76+
77+
```bash
78+
gosecutil -tool ast main.go
79+
```
80+
81+
Valid tool arguments for this command are `ast`, `callobj`, `uses`, `types`, `defs`, `comments`, and `imports`.

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,8 @@ $ gosec -fmt=json -out=results.json -stdout -verbose=text *.go
387387

388388
## Development
389389

390+
[CONTRIBUTING.md](https://github.com/securego/gosec/blob/master/CONTRIBUTING.md) contains detailed information about adding new rules to gosec.
391+
390392
### Build
391393

392394
You can build the binary with:

0 commit comments

Comments
 (0)