Go has a reputation for producing secure software. The language memory safety (no buffer overflows, no dangling pointers), built-in concurrency primitives, and simple standard library contribute to this reputation. But Go code is still vulnerable to logic bugs, injection attacks, insecure cryptographic usage, and misconfiguration.
gosec (Go Security Checker) is the community standard tool for finding these issues through static analysis. It examines Go source code AST and flags patterns associated with security vulnerabilities.
What gosec Detects
gosec organizes its checks into numbered rules. The most valuable ones include:
G101 - Hardcoded credentials. Detects string literals assigned to variables with names suggesting they are passwords, tokens, or secrets. This catches the most common form of secret leakage in source code.
G104 - Unhandled errors. Go error handling requires explicit checks. When a security-relevant function returns an error that is ignored, the program continues with potentially invalid state. gosec flags unchecked errors from functions like os.Open, http.ListenAndServe, and cryptographic operations.
G201/G202 - SQL injection. Flags string concatenation and fmt.Sprintf used to construct SQL queries. Go database/sql package supports parameterized queries, and these rules enforce their use.
G301/G302/G303/G304 - File permissions. Detects overly permissive file and directory creation. Creating files with mode 0777 or directories with world-write permission is a common misconfiguration that gosec catches reliably.
G401/G501 - Weak cryptography. Flags use of DES, MD5, SHA1, and RC4 for security purposes. These algorithms have known weaknesses and should be replaced with modern alternatives.
G601 - Implicit memory aliasing in for loops. Before Go 1.22, taking the address of a loop variable captured the same variable in every iteration. This is a source of subtle bugs, including security-relevant ones where concurrent goroutines share unintended state.
Running gosec Effectively
Basic usage is straightforward:
gosec ./...
This scans all packages in the current module. For CI/CD integration, use the JSON output format and filter by severity:
gosec -fmt=json -out=results.json -severity=medium ./...
Suppressing False Positives
gosec supports inline suppression with comments:
// #nosec G104 -- error intentionally ignored for logging cleanup
_ = logFile.Close()
Always include the rule number and a justification in the nosec comment. A bare #nosec with no explanation makes it impossible to audit suppressions later.
For project-wide configuration, use a YAML config file:
# .gosec.yaml
global:
# Exclude test files
exclude-test: true
rules:
# Disable specific rules
G104:
enabled: false
Excluding Test Files
Test files often contain patterns that gosec flags -- hardcoded test credentials, intentionally insecure configurations, ignored errors for test cleanup. Exclude test files from scanning to reduce noise:
gosec -exclude-dir=testdata -tests=false ./...
What gosec Misses
Cross-function data flow. gosec analyzes individual function bodies. It cannot track tainted data flowing from an HTTP handler through multiple function calls to a database query. If user input passes through three functions before reaching a SQL query, gosec will not flag it.
Template injection. Go html/template package auto-escapes HTML, but text/template does not. gosec does not currently detect when text/template is used where html/template should be.
Race conditions. While Go race detector (-race flag) catches data races at runtime, gosec does not perform concurrency analysis. Security-relevant race conditions (like TOCTOU in file operations) are not detected.
Business logic. gosec catches coding patterns, not logical flaws. An authorization bypass caused by incorrect permission checking logic is invisible to gosec.
Dependency vulnerabilities. gosec analyzes your source code, not your dependencies. Use govulncheck (the official Go vulnerability checker) alongside gosec to cover dependency risks.
gosec vs. Other Go Security Tools
govulncheck is the official Go vulnerability database checker. It analyzes your dependency tree and reports known vulnerabilities, including reachability analysis that tells you whether your code actually calls the vulnerable function. gosec and govulncheck are complementary -- use both.
Staticcheck is a general-purpose Go linter that catches some security-adjacent issues (like incorrect use of cryptographic APIs) but is not focused on security. It is worth running alongside gosec for broader code quality.
Semgrep offers Go support with more expressive pattern matching than gosec. For teams that want custom security rules, Semgrep provides a powerful DSL for defining patterns. But gosec is simpler to set up and has better Go-specific defaults.
Integration Patterns
Pre-commit hook. Run gosec on changed files before commit. This catches issues early without slowing down the full build:
gosec $(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
CI/CD gate. Run gosec on the full codebase in CI. Fail the build on high-severity findings. Report medium and low severity as warnings.
IDE integration. gosec integrates with VS Code (through the Go extension), GoLand, and other editors. Real-time feedback in the editor is the fastest way to catch issues.
SARIF output. gosec can output SARIF format, which integrates with GitHub Code Scanning, GitLab SAST, and other platforms that consume standardized static analysis results.
How Safeguard.sh Helps
Safeguard.sh covers the dependency security gap that gosec intentionally does not address. While gosec finds insecure patterns in your Go source code, Safeguard.sh monitors your Go module dependencies for known vulnerabilities, tracks new versions for suspicious changes, and generates comprehensive SBOMs. Together, gosec and Safeguard.sh provide coverage across both your own code and the supply chain it depends on.