What it does
gosec loads Go packages using the standard Go tooling, which means it gets
real type information rather than a best-effort parse. It then walks the
abstract syntax tree applying a numbered rule set: G101 for hardcoded
credentials, G201 and G202 for SQL string construction, G204 for subprocess
invocation with variable arguments, G304 for file paths taken from a
variable, G401 and G501 for deprecated hash algorithms, G402 for TLS settings
that disable verification, and so on through file permissions, unhandled
errors, integer conversion overflow and use of math/rand where a
cryptographic source is required.
Some rules make use of Go's SSA form to do limited value tracking, for
instance to determine whether a value passed to a dangerous call is a
constant or derived from something variable. That distinction is what keeps
the command execution and SQL rules from firing on every call site. Findings
carry a rule identifier, severity, confidence and CWE mapping, and can be
suppressed with a #nosec comment optionally scoped to specific rule IDs.
Output formats include JSON, SARIF, JUnit and a plain text summary.
Where it fits
gosec runs as a CLI in CI and locally, frequently as one of the linters enabled inside golangci-lint rather than as a separate binary, which is the path of least resistance for most Go teams. Developers own it. It needs the module to resolve and build well enough for package loading, so vendored or partially broken checkouts can cause confusing failures. A common setup gates on high severity and high confidence while collecting the rest.
Strengths
- Uses genuine Go type information, so rules are less prone to naive string matching mistakes than a regex-based scanner.
- Rule identifiers are stable and well documented, which makes suppressions and organizational policy easy to express precisely.
- Fast enough for pre-commit and pull request use on realistic Go repositories.
- Integrates cleanly with golangci-lint, so it can be adopted without adding another CI step.
Limitations
- Analysis is largely per-function pattern matching. It does not follow tainted values across package boundaries, so many real injection paths go unreported.
- Noisy rules in practice: unhandled errors, file permissions and integer conversion checks fire frequently on idiomatic code and are commonly disabled outright.
- Go only, and unaware of web frameworks, so it cannot distinguish an externally reachable handler from internal plumbing.
Who it suits
Sensible baseline for any Go codebase, especially as part of an existing golangci-lint configuration. It is not sufficient on its own for a service handling untrusted input at scale, where you want a taint-aware engine such as a query-based or commercial scanner in addition.
Used gosec? Recommend it under your own name and title.
Recommend this tool