RULES.md
ssh.InsecureIgnoreHostKey function (AST)strconv.Atoi result to int32/int16 (AST)io.Copy instead of io.CopyN when decompressing (AST)http.Dir('/') as a potential risk (AST)ReadHeaderTimeout not configured as a potential risk (AST)net/http serve function that has no support for setting timeouts (AST)ParseMultipartForm in HTTP handlers can cause memory exhaustion (Taint)filepath.Walk/WalkDir callbacks (SSA)VerifyPeerCertificate when VerifyConnection is unset (SSA)chmod (AST)os.Create (AST)rand) (AST)ssh.PublicKeyCallback leading to auth bypass (SSA)crypto/md5 (AST)crypto/des (AST)crypto/rc4 (AST)net/http/cgi (AST)crypto/sha1 (AST)golang.org/x/crypto/md4 (AST)golang.org/x/crypto/ripemd160 (AST)RangeStmt (Go 1.21 or lower) (AST)text/template (Taint)Note: Implementation types used in this document:
rules/ and evaluated on AST patternsanalyzers/ using the analyzer framework (SSA-backed execution path)taint.NewGosecAnalyzermath/big check and is now used for HTTP request smuggling.Some rules accept configuration in the gosec JSON config file.
Per-rule settings are top-level objects keyed by rule ID (Gxxx).
Configurable rules (alphabetical): G101, G104, G111, G117, G301, G302, G306, G307.
G101 (hardcoded credentials) can be configured with custom patterns and entropy thresholds:
{
"G101": {
"pattern": "(?i)passwd|pass|password|pwd|secret|private_key|token",
"ignore_entropy": false,
"entropy_threshold": "80.0",
"per_char_threshold": "3.0",
"truncate": "32",
"min_entropy_length": "8"
}
}
G104 (unchecked errors) can be configured with function allowlists:
{
"G104": {
"ioutil": ["WriteFile"]
}
}
G111 (HTTP directory serving) can be configured with a custom detection regex.
This replaces the default pattern.
{
"G111": {
"pattern": "http\\.Dir\\(\"\\/\"\\)|http\\.Dir\\('\\/'\\)"
}
}
G117 (secret serialization) can be configured with a custom field-name pattern.
{
"G117": {
"pattern": "(?i)secret|token|password"
}
}
G118 detects three classes of context-propagation failure using SSA-level analysis:
1. Lost cancel function (CWE-400)
Reports when a context.WithCancel, context.WithTimeout, or context.WithDeadline call
returns a cancel function that is never called, potentially leaking resources.
// Flagged: cancel never called
func work(ctx context.Context) {
child, _ := context.WithTimeout(ctx, time.Second)
_ = child
}
// Safe: cancel deferred
func work(ctx context.Context) {
child, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
_ = child
}
The following patterns are all recognised as safe (cancel is considered called):
| Pattern | Description |
|---|---|
defer cancel() | Direct deferred call |
defer func() { cancel() }() | Cancel in a deferred closure |
cancelCopy := cancel; defer cancelCopy() | Alias via variable |
return ctx, cancel | Cancel returned to caller (responsibility transferred) |
s.cancelFn = cancel + method s.cancelFn() | Stored in struct field, called via receiver method |
s.cancel = cancel; defer s.cancel() | Stored in struct field, deferred in same function |
s.cancel = cancel; defer func() { s.cancel() }() | Stored in struct field, called in closure |
| Struct containing field is returned | Caller inherits cancel responsibility |
var cancel CancelFunc in init() + cancel() in another function | Package-level variable assigned in init, called in any function (e.g., signal handlers) |
Example of package-level variable pattern:
// Safe: cancel stored in package-level variable and called in signal handler
var cancel context.CancelFunc
func init() {
ctx, c := context.WithCancel(context.Background())
cancel = c
}
func handleShutdown() {
cancel() // Called from signal handler
}
2. Goroutine uses context.Background/TODO when request context is available (CWE-400)
Reports when a goroutine spawned inside an HTTP handler or a function accepting a
context.Context / *http.Request uses context.Background() or context.TODO()
instead of the request-scoped context.
// Flagged
func handler(w http.ResponseWriter, r *http.Request) {
go func() {
ctx := context.Background() // ignores request context
doWork(ctx)
}()
}
3. Long-running loop without ctx.Done() guard (CWE-400)
Reports an infinite loop that performs blocking I/O (e.g. http.Get, db.Query,
time.Sleep, interface methods such as Read/Write) but never checks ctx.Done(),
making the loop impossible to cancel.
// Flagged
func poll(ctx context.Context) {
for {
http.Get("https://example.com") // blocks, no cancellation path
time.Sleep(time.Second)
}
}
// Safe
func poll(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Second):
http.Get("https://example.com")
}
}
}
Loops with an external exit path (e.g. a break or bounded for i < n) are not flagged.
File and directory permission rules can be configured with stricter maximum permissions:
{
"G301": "0o600",
"G302": "0o600",
"G306": "0o750",
"G307": "0o750"
}