Claude Code Plugins

Community-maintained marketplace

Feedback

Go Security Patterns

@dkoosis/cc-plugins
0
0

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.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name Go Security Patterns
description 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.

Go Security Patterns

Patterns for fixing common Go security lint violations from gosec.

Overview

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

Pattern 1: Safe Integer Conversion (G115)

Converting between integer types can overflow on different architectures.

Problem

func ProcessCount(n int64) {
    count := int(n)  // G115: int is 32-bit on some systems
    buffer := make([]byte, count)
}

Solution 1: Bounds Check

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
}

Solution 2: Keep Original Type

func ProcessCount(n int64) {
    buffer := make([]byte, n)  // slice length accepts int64
}

Solution 3: Document Constraint

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)
}

Architecture-Safe Patterns

// 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
}

Pattern 2: Cryptographic Random (G404)

math/rand is deterministic - use crypto/rand for security.

Problem

import "math/rand"

func GenerateSessionID() string {
    return fmt.Sprintf("%x", rand.Int63())  // G404: predictable
}

Solution

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
}

When math/rand is Acceptable

  • Shuffling for display (not security)
  • Test data generation
  • Games/simulations
  • Performance-critical non-security code

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]
})

Crypto/Rand Patterns

// 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)
}

Pattern 3: Secrets Management (G101)

Never hardcode credentials, API keys, or tokens.

Problem

const (
    apiKey = "sk-abc123xyz"  // G101
    dbPass = "secret123"     // G101
)

Solution: Environment Variables

func getAPIKey() string {
    key := os.Getenv("API_KEY")
    if key == "" {
        log.Fatal("API_KEY not set")
    }
    return key
}

Solution: Config Files

type Config struct {
    APIKey string `env:"API_KEY" yaml:"api_key"`
}

func LoadConfig() (*Config, error) {
    // Load from environment or config file
}

False Positives

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"

Pattern 4: URL Validation (G107)

Unvalidated URLs in HTTP requests enable SSRF.

Problem

func Fetch(url string) (*http.Response, error) {
    return http.Get(url)  // G107: URL from variable
}

Solution: Allowlist

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)
}

Pattern 5: Path Sanitization (G304)

User-controlled paths enable directory traversal.

Problem

func ReadFile(name string) ([]byte, error) {
    return os.ReadFile(filepath.Join("/data", name))  // G304
}

Solution

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)
}

Quick Reference

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

Verification

golangci-lint run --enable gosec ./...
go test ./...