This skill should be used when the user asks about "gosec", "G115", "G404", "integer overflow", "weak random", "crypto/rand", "security lint", "hardcoded credentials", or needs guidance on fixing Go security vulnerabilities. Provides patterns for common security anti-patterns.
Provides Go security patterns to fix gosec lint violations (G115, G404, G101, G107, G304). Use when user asks about integer overflow, weak random, hardcoded credentials, or needs guidance on fixing Go security vulnerabilities.
/plugin marketplace add dkoosis/cc-plugins/plugin install go-security-lint@cc-pluginsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Patterns for fixing common Go security lint violations from gosec.
gosec (Go Security Checker) identifies security anti-patterns:
| Rule | Category | Risk |
|---|---|---|
| G115 | Integer overflow | Data corruption, crashes |
| G404 | Weak random | Predictable tokens |
| G101 | Hardcoded secrets | Credential exposure |
| G107 | Unvalidated URL | SSRF attacks |
| G304 | Path traversal | File access bypass |
| G401 | Weak crypto (MD5/SHA1) | Hash collisions |
Converting between integer types can overflow on different architectures.
func ProcessCount(n int64) {
count := int(n) // G115: int is 32-bit on some systems
buffer := make([]byte, count)
}
import "math"
func ProcessCount(n int64) error {
if n < 0 || n > math.MaxInt {
return fmt.Errorf("count %d out of range", n)
}
count := int(n)
buffer := make([]byte, count)
return nil
}
func ProcessCount(n int64) {
buffer := make([]byte, n) // slice length accepts int64
}
When overflow is impossible due to domain constraints:
// pageSize is always 1-100 from validated input
func ProcessPage(pageSize int64) {
//nolint:gosec // pageSize validated to [1,100] by caller
count := int(pageSize)
}
// Instead of: int(x) where x is int64
// Use: explicit checks or keep type
// For array/slice indices from int64:
if idx < 0 || idx > math.MaxInt {
return ErrIndexOutOfRange
}
arr[int(idx)] // Now safe
// For loop counters:
for i := int64(0); i < n; i++ {
// Keep as int64 throughout
}
math/rand is deterministic - use crypto/rand for security.
import "math/rand"
func GenerateSessionID() string {
return fmt.Sprintf("%x", rand.Int63()) // G404: predictable
}
import (
"crypto/rand"
"encoding/hex"
)
func GenerateSessionID() (string, error) {
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil {
return "", fmt.Errorf("generate session ID: %w", err)
}
return hex.EncodeToString(b), nil
}
Add explicit nolint with justification:
//nolint:gosec // shuffle for UI display order, not security
rand.Shuffle(len(items), func(i, j int) {
items[i], items[j] = items[j], items[i]
})
// Generate random bytes
b := make([]byte, 32)
_, err := rand.Read(b)
// Generate random number in range
n, err := rand.Int(rand.Reader, big.NewInt(100))
// Generate UUID-like token
func GenerateToken() string {
b := make([]byte, 16)
rand.Read(b)
return base64.RawURLEncoding.EncodeToString(b)
}
Never hardcode credentials, API keys, or tokens.
const (
apiKey = "sk-abc123xyz" // G101
dbPass = "secret123" // G101
)
func getAPIKey() string {
key := os.Getenv("API_KEY")
if key == "" {
log.Fatal("API_KEY not set")
}
return key
}
type Config struct {
APIKey string `env:"API_KEY" yaml:"api_key"`
}
func LoadConfig() (*Config, error) {
// Load from environment or config file
}
gosec may flag non-secrets. Use nolint with reason:
//nolint:gosec // example key for documentation
const exampleKey = "sk-example-not-real"
//nolint:gosec // password field name, not actual password
const passwordField = "password"
Unvalidated URLs in HTTP requests enable SSRF.
func Fetch(url string) (*http.Response, error) {
return http.Get(url) // G107: URL from variable
}
var allowedHosts = map[string]bool{
"api.example.com": true,
"cdn.example.com": true,
}
func Fetch(rawURL string) (*http.Response, error) {
u, err := url.Parse(rawURL)
if err != nil {
return nil, fmt.Errorf("invalid URL: %w", err)
}
if !allowedHosts[u.Host] {
return nil, fmt.Errorf("host %s not allowed", u.Host)
}
return http.Get(rawURL)
}
User-controlled paths enable directory traversal.
func ReadFile(name string) ([]byte, error) {
return os.ReadFile(filepath.Join("/data", name)) // G304
}
func ReadFile(name string) ([]byte, error) {
// Clean the path and verify it's within allowed directory
clean := filepath.Clean(name)
if strings.Contains(clean, "..") {
return nil, fmt.Errorf("invalid path: %s", name)
}
full := filepath.Join("/data", clean)
if !strings.HasPrefix(full, "/data/") {
return nil, fmt.Errorf("path escapes data directory")
}
return os.ReadFile(full)
}
| Issue | Detection | Fix |
|---|---|---|
int(x) where x is int64 | G115 | Bounds check or keep type |
math/rand for tokens | G404 | Use crypto/rand |
| Literal secrets | G101 | Environment/config |
http.Get(variable) | G107 | Validate/allowlist URL |
os.ReadFile(variable) | G304 | Sanitize path |
golangci-lint run --enable gosec ./...
go test ./...