- Local workflow
- Contributing: adding rules and analyzers
- AI-generated rule workflow (Copilot)
- AI-generated bug fix workflow (Copilot)
- AI-supported Go version update workflow (Copilot)
- Rule development utilities
- SARIF types generation
- Performance regression guard
- Generate TLS rule data
- Release
- Docker image
- Go version:
1.25+(seego.mod) - Build:
make - Run all checks used in CI (format, vet, security scan, vulnerability scan, tests):
make test - Run linter only:
make golangci
gosec supports three implementation styles:
- AST rules (
gosec.Rule) for node-level checks inrules/ - SSA analyzers (
analysis.Analyzer) for whole-program context inanalyzers/ - Taint analyzers for source-to-sink data-flow checks in
analyzers/viataint.NewGosecAnalyzer
- Create a new file in
rules/(for example, userules/unsafe.goas a simple template). - Implement your rule constructor and
Matchlogic. - Register the rule in
rules/rulelist.go. - Add rule-to-CWE mapping in
issue/issue.go(and add CWE data incwe/data.goonly if needed). - Add tests and samples:
- sample code in
testutils/ - rule tests in
rules/or integration tests inanalyzer_test.go
- sample code in
- Create a new file in
analyzers/. - Define the analyzer and require
buildssa.Analyzer. - Read SSA input using
ssautil.GetSSAResult(pass). - Return findings as
[]*issue.Issue. - Register in
analyzers/analyzerslist.go. - Add rule-to-CWE mapping in
issue/issue.go. - Add tests and sample code in
analyzers/andtestutils/.
Minimal skeleton:
package analyzers
import (
"fmt"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/buildssa"
"github.com/securego/gosec/v2/internal/ssautil"
"github.com/securego/gosec/v2/issue"
)
func newMyAnalyzer(id, description string) *analysis.Analyzer {
return &analysis.Analyzer{
Name: id,
Doc: description,
Run: runMyAnalyzer,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
}
}
func runMyAnalyzer(pass *analysis.Pass) (interface{}, error) {
ssaResult, err := ssautil.GetSSAResult(pass)
if err != nil {
return nil, fmt.Errorf("getting SSA result: %w", err)
}
_ = ssaResult
var issues []*issue.Issue
return issues, nil
}gosec taint analyzers track data flow from untrusted sources to dangerous sinks. Current taint rules include SQL injection, command injection, path traversal, SSRF, XSS, log injection, and SMTP injection.
- Create a new analyzer file in
analyzers/(for exampleanalyzers/newvuln.go) with both:- the taint
Config(sources, sinks, optional sanitizers) - the analyzer constructor that returns
taint.NewGosecAnalyzer(...)
- the taint
package analyzers
import (
"golang.org/x/tools/go/analysis"
"github.com/securego/gosec/v2/taint"
)
func NewVulnerability() taint.Config {
return taint.Config{
Sources: []taint.Source{
{Package: "net/http", Name: "Request", Pointer: true},
{Package: "os", Name: "Args", IsFunc: true},
},
Sinks: []taint.Sink{
{Package: "dangerous/package", Method: "DangerousFunc"},
},
}
}
func newNewVulnAnalyzer(id string, description string) *analysis.Analyzer {
config := NewVulnerability()
rule := NewVulnerabilityRule
rule.ID = id
rule.Description = description
return taint.NewGosecAnalyzer(&rule, &config)
}- Register the analyzer in
analyzers/analyzerslist.go:
var defaultAnalyzers = []AnalyzerDefinition{
// ... existing analyzers ...
{"G7XX", "Description of vulnerability", newNewVulnAnalyzer},
}-
Add sample programs in
testutils/g7xx_samples.go. -
Add the analyzer test in
analyzers/analyzers_test.go:
It("should detect your new vulnerability", func() {
runner("G7XX", testutils.SampleCodeG7XX)
})Each taint analyzer keeps its configuration function in the same file as the analyzer. Reference implementations:
analyzers/sqlinjection.go(G701)analyzers/commandinjection.go(G702)analyzers/pathtraversal.go(G703)
Sources define where untrusted data starts:
Package: import path (for example"net/http")Name: type or function name (for example"Request","Getenv")Pointer: settruefor pointer types (for example*http.Request)IsFunc: settruewhen the source is a function that returns tainted data
Sinks define where tainted data must not reach:
PackageReceiver: method receiver type, empty for package functionsMethodPointer: whether receiver is a pointerCheckArgs: optional argument indexes to inspect; if omitted, all args are inspected
Example:
// For *sql.DB.Query, Args[1] is the query string.
{Package: "database/sql", Receiver: "DB", Method: "Query", Pointer: true, CheckArgs: []int{1}}
// Skip writer arg in fmt.Fprintf and check the rest.
{Package: "fmt", Method: "Fprintf", CheckArgs: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}Sanitizers break taint flow after validation/escaping:
PackageReceiverMethodPointer
If data passes through a configured sanitizer, it is treated as safe for subsequent sinks.
| Source Type | Package | Type/Method | Pointer | IsFunc |
|---|---|---|---|---|
| HTTP Request | net/http |
Request |
true |
false |
| Command Line Args | os |
Args |
false |
true |
| Environment Variables | os |
Getenv |
false |
true |
| File Content | bufio |
Reader |
true |
false |
This repository includes a reusable Copilot skill and prompt for creating new gosec rules from an issue description.
- Skill file:
.github/skills/gosec-new-rule/SKILL.md - Prompt file:
.github/prompts/create-gosec-rule.prompt.md
- In VS Code Copilot Chat, run
/promptand select Create Gosec Rule. - Fill in the issue fields (
Summary, repro steps, versions, environment, expected, actual). - Submit the prompt.
- First response should only propose:
- rule ID
- implementation approach (SSA / taint / AST)
- relevance for Go
1.25and1.26 - confirmation request
- Reply with explicit confirmation (for example:
Confirmed. Proceed with implementation.).
Send this in Copilot Chat:
Use the skill "Create New Gosec Rule" from .github/skills/gosec-new-rule/SKILL.md.
Then paste the same issue template fields and confirm after the proposal step.
- Ensure the workspace root is this repository.
- Confirm the file exists at
.github/prompts/create-gosec-rule.prompt.md. - Reload VS Code window and start a new chat session.
- As fallback, open the prompt file and send its content directly in chat.
This repository also includes a Copilot skill and prompt for fixing bugs described in GitHub issues.
- Skill file:
.github/skills/gosec-fix-issue/SKILL.md - Prompt file:
.github/prompts/fix-gosec-bug-from-issue.prompt.md
- In VS Code Copilot Chat, run
/promptand select Fix Gosec Bug From Issue. - Fill in at least the
GitHub issue URLfield (other fields are optional but useful). - Submit the prompt.
- First response should only include:
- reproduction status on
master(or clear blocker) - root cause analysis
- detailed fix plan
- confirmation request
- reproduction status on
- Reply with explicit confirmation (for example:
Confirmed. Proceed with fix.).
Send this in Copilot Chat:
Use the skill "Fix Gosec Bug From Issue" from .github/skills/gosec-fix-issue/SKILL.md.
Then provide the GitHub issue URL and confirm after the analysis and plan step.
After confirmation, the workflow should:
- keep the fix small and isolated to the problem
- use idiomatic Go and good design
- add positive and negative tests
- add or update
testutils/code samples when appropriate for reproducing/validating the issue - validate with build, tests,
golangci-lint, and agosecCLI run against a sample
This repository includes a Copilot skill and prompt to update supported Go versions to the latest patch versions of the two newest major Go series.
- Skill file:
.github/skills/gosec-update-go-versions/SKILL.md - Prompt file:
.github/prompts/update-supported-go-versions.prompt.md
- In VS Code Copilot Chat, run
/promptand select Update Supported Go Versions. - Submit the prompt (no additional fields required).
- The workflow should:
- read
https://go.dev/doc/devel/release - detect latest two supported Go series and latest patch for each
- update all active repository locations where supported Go versions are configured or documented
- run validation checks
- create branch, commit, push, and open a PR
- read
Send this in Copilot Chat:
Use the skill "Update Supported Go Versions" from .github/skills/gosec-update-go-versions/SKILL.md.
The result should include:
- detected versions (
previous_patch,latest_patch,previous_minor,latest_minor) - grouped file update summary
- test command result
- branch, commit SHA, PR title, and PR URL
Use these tools while building or debugging rules:
- Dump SSA with
ssadump:
ssadump -build F main.go- Inspect AST/types/defs/imports with
gosecutil:
gosecutil -tool ast main.goValid -tool values: ast, callobj, uses, types, defs, comments, imports.
Install schema-generate:
go install github.com/a-h/generate/cmd/schema-generate@latestGenerate types:
schema-generate -i sarif-schema-2.1.0.json -o path/to/types.goMost MarshalJSON/UnmarshalJSON helpers can be removed after generation, except PropertyBag where inlined additional properties are useful.
CI includes a taint benchmark guard based on BenchmarkTaintPackageAnalyzers_SharedCache.
- Baseline and thresholds:
.github/benchmarks/taint_benchmark_baseline.env - Guard script:
tools/check_taint_benchmark.sh
Run locally:
bash tools/check_taint_benchmark.shUpdate baseline after intentional changes:
BENCH_COUNT=10 bash tools/check_taint_benchmark.sh --update-baselineIf you update the baseline, commit both the benchmark-related code and the baseline file.
The TLS rule data is generated from Mozilla recommendations.
From the repository root:
go generate ./...If go generate fails with exec: "tlsconfig": executable file not found in $PATH, install the local generator and add $(go env GOPATH)/bin to PATH:
export PATH="$(go env GOPATH)/bin:$PATH"
go install ./cmd/tlsconfig
go generate ./...This updates rules/tls_config.go.
If you need to install the generator binary outside this repository:
go install github.com/securego/gosec/v2/cmd/tlsconfig@latestTag and push:
git tag v1.0.0 -m "Release version v1.0.0"
git push origin v1.0.0The release workflow builds binaries and Docker images, then signs artifacts.
Verify signatures:
cosign verify --key cosign.pub ghcr.io/securego/gosec:<TAG>
cosign verify-blob --key cosign.pub --signature gosec_<VERSION>_darwin_amd64.tar.gz.sig gosec_<VERSION>_darwin_amd64.tar.gzBuild locally:
make imageRun against a local project:
docker run --rm -it -w /<PROJECT>/ -v <YOUR_PROJECT_PATH>/<PROJECT>:/<PROJECT> ghcr.io/securego/gosec:latest /<PROJECT>/...Set -w so module dependencies resolve from the mounted project root.