Install
openclaw skills install go-security-auditPerform a security audit of a Go codebase. Targets SSH servers, BBS systems, API services, and CLI tools. Finds race conditions, goroutine leaks, missing error handling, command injection, and auth bypasses. Produces a prioritized finding list with file:line citations and minimal diff patches. Use when asked to audit, review, or security-test a Go repository.
openclaw skills install go-security-auditgo.mod for dependencies, cmd/ for entry points, internal/ for core logicsync.Mutex, sync.RWMutex, channels, goroutinesexec.Command, os.StartProcessCheck every shared mutable state:
TOCTOU patterns — check/act gaps:
// BAD: unlock between check and use
r.mu.Lock()
entry, ok := r.m[id]
r.mu.Unlock() // ← gap here
if !ok { return }
entry.Close() // ← entry could be gone
// GOOD: defer unlock or keep locked through the operation
r.mu.Lock()
defer r.mu.Unlock()
entry, ok := r.m[id]
if !ok { return }
delete(r.m, id)
entry.Close()
Map concurrent access:
map written in one goroutine and read in another without mutex → data racesync.Map or protect with sync.RWMutexChannel patterns:
close() called on a channel that may already be closed → panicFind silent failures:
// Flag patterns:
result, _ = someFunc() // error discarded
res.LastInsertId() // return value ignored
time.Parse(layout, val) // two-return ignored with _
Every _ on the error position should be justified or flagged.
SQL patterns:
// Check LastInsertId separately:
id, err := res.LastInsertId()
if err != nil {
return 0, fmt.Errorf("get insert id: %w", err)
}
Check every exec.Command / exec.CommandContext call:
// Risky: user-controlled input split with strings.Fields
parts := strings.Fields(os.Getenv("USER_CMD"))
cmd := exec.CommandContext(ctx, parts[0], parts[1:]...)
// Safe: validate no shell metacharacters, or use explicit args
if strings.ContainsAny(cmdline, "|;&$`(){}") {
return fmt.Errorf("invalid command")
}
context.WithTimeout used for all external calls?// File descriptors — check every os.Open has defer Close()
f, err := os.Open(path)
// missing defer f.Close() → leak
// Goroutine leaks — goroutines started without a stop mechanism
go func() {
for { select { case <-ch: ... } } // ← what closes ch?
}()
// DB rows — rows.Close() deferred after rows.Next() loop
rows, _ := db.Query(...)
defer rows.Close() // must be present
## Finding [N]: [Title] — [Critical/High/Medium/Low]
**File:** path/file.go:LINE
**Impact:** [what can go wrong]
**Root cause:** [exact code snippet]
**Fix:**
\`\`\`go
// corrected code
\`\`\`
Prioritize by: Critical (data loss/auth bypass) → High (crash/leak) → Medium (silent failure) → Low (hardening)