Configures and runs gosec - Go-only SAST covering 40+ rule IDs (G101 hardcoded creds, G104 unhandled errors, G304 path traversal, G401 weak crypto, G601 memory aliasing) via Go AST + SSA taint tracking; `gosec ./...` scan, `#nosec G404 -- justification` suppression, `--fmt sarif|json|junit-xml|html`, golangci-lint integration. Use for a focused Go SAST wired into golangci-lint / CI. Go-only: for Python use bandit-python, for cross-language pattern SAST use semgrep-rules; to merge gosec findings with other scanners into one gate use multi-tool-finding-triage - not this for non-Go code.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Per github.com/securego/gosec:
gosec is the Go-specific SAST. It "performs static code analysis by scanning the Go AST and SSA code representation" and supports "taint analysis tracking data flow from user inputs to dangerous functions" per gs-gh.
The taint analysis distinguishes gosec from regex-based linters - it tracks input flow through method chains, which catches injection patterns linters miss.
semgrep-rules for
cross-language coverage.go install github.com/securego/gosec/v2/cmd/gosec@latest (Step 1).gosec ./..., then narrow noise with -severity=high -confidence=high (Steps 2, 5).// #nosec Gxxx -- Reason: ... and never a bare #nosec (Step 5)..golangci.yml so config is unified (Step 6).-fmt sarif -out gosec.sarif ./...) and upload it for a PR-blocking gate (Steps 4, 7).#nosec lines lacking -- Reason: and re-review them (Step 5).Per gs-gh:
go install github.com/securego/gosec/v2/cmd/gosec@latestFor pinned versions in CI:
go install github.com/securego/gosec/v2/cmd/gosec@v2.20.0Docker:
docker pull securego/gosec
docker run --rm -v "$PWD:/code" securego/gosec ./code/...Per gs-gh:
gosec ./...Common variations:
gosec -severity=high ./... # only HIGH severity
gosec -confidence=high ./... # only HIGH confidence
gosec -exclude=G104 ./... # skip "unhandled errors" rule
gosec -include=G101,G102 ./... # only run specific rulesgosec ships 40+ rule IDs (G101 hardcoded creds, G104 unhandled errors, G304 path traversal, G401 weak crypto, G601 memory aliasing, and more). The full lookup table - every ID and its meaning - lives in references/rule-catalog.md.
Full current list at runtime: gosec -list-rules.
Per gs-gh:
gosec -fmt sarif -out results.sarif ./...
gosec -fmt json -out results.json ./...
gosec -fmt junit-xml -out results.xml ./...
gosec -fmt html -out results.html ./...
gosec -fmt text -out results.txt ./...
gosec -fmt yaml -out results.yaml ./...For multi-scanner triage integration, use JSON.
Per gs-gh the canonical inline suppression syntax:
// #nosec G404 -- justification textFormat: #nosec [RuleList] [-- Justification].
Three suppression layers:
| Mechanism | Example | When to use |
|---|---|---|
Per-line #nosec | // #nosec G101 -- test fixture; not deployed to prod | Single-line exception with justification |
Per-rule list #nosec | // #nosec G104,G115 -- intentional in this fast-path | Multi-rule single-line |
-exclude= flag | gosec -exclude=G104 ./... | Project-wide rule disable (CI flag) |
-confidence= filter | gosec -confidence=high ./... | Triage workflow: only high-confidence first |
Justification template (mandatory in code):
// #nosec G401 -- Reason: legacy MD5 required for vendor-mandated checksum format
// Reviewer: alice@example.com (2026-05-15)
// Expires: 2026-12-15
hash := md5.Sum(data)Bandit-style cadence: every quarter, grep for #nosec patterns
lacking -- Reason: and flag for review.
Most Go teams run gosec via golangci-lint (the universal linter runner) rather than directly:
# .golangci.yml
linters:
enable:
- gosec
linters-settings:
gosec:
excludes:
- G104 # unhandled errors (often noise)
severity: medium
confidence: medium
config:
G306: "0644" # default file perm thresholdgolangci-lint run ./...This is the recommended pattern - golangci-lint handles parallelism,
caching, and unified output across multiple linters.
Standalone:
jobs:
gosec:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
with: { go-version: '1.22' }
- uses: securego/gosec@master
with:
args: -fmt sarif -out gosec.sarif ./...
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: gosec.sarif }Via golangci-lint (preferred):
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/setup-go@v5
with: { go-version: '1.22' }
- uses: golangci/golangci-lint-action@v6
with:
version: latestUnlike Semgrep / CodeQL, gosec doesn't have a custom-rule DSL -
adding new rules requires writing Go code in gosec/rules/ and
contributing upstream OR forking. For most teams, leverage the
40+ built-in rules + suppressions.
A Go microservice needs to clear a security review. Run
gosec -severity=high -confidence=high ./...; gosec reports a G401
on a md5.Sum(data) call and a G104 on an unchecked w.Write
return.
G401 is a false positive here - the MD5 is a vendor-mandated checksum, not a security hash. Suppress it with an audited justification (Step 5):
// #nosec G401 -- Reason: legacy MD5 required for vendor-mandated checksum format
hash := md5.Sum(data)G104 is a real bug - the unhandled w.Write error is fixed by
checking its return value, not suppressed.
Re-run gosec -fmt sarif -out gosec.sarif ./...; the SARIF now
reports zero HIGH findings, and the golangci-lint gate from Step 6
passes in CI.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
#nosec without rule ID | Suppresses ALL rules on that line | // #nosec G401 (specific, Step 5) |
#nosec without -- Justification | No audit trail | Required template (Step 5) |
Skip -confidence= filter | LOW confidence drowns the team | -confidence=high for triage (Step 2) |
| Run gosec separately from golangci-lint | Two linters with different config | golangci-lint integration (Step 6) |
| Exclude G104 globally | Loses entire "unhandled errors" coverage | Per-call // #nosec G104 -- intentional |
codeql-queries catches.gosec -list-rules - current rule catalogsemgrep-rules,
sonarqube-rules,
codeql-queries,
bandit-python - sister scanners