SE

security-review

Provides security checklists and best practices for developing secure Go applications.

Install

mkdir -p .claude/skills/security-review-zzh0u && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/12272" && unzip -o skill.zip -d .claude/skills/security-review-zzh0u && rm skill.zip

Installs to .claude/skills/security-review-zzh0u

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Use this skill when adding authentication, handling user input, working with secrets, creating API endpoints, or implementing payment/sensitive features. Provides comprehensive security checklist and patterns.
209 chars✓ has a “when” trigger
Intermediate

Key capabilities

  • Ensure secrets are loaded from environment variables or secret managers
  • Validate all user inputs using struct tags or libraries
  • Restrict file uploads by size, content type, and extension
  • Implement secure authentication and authorization patterns
  • Prevent common Go-specific security vulnerabilities

How it works

The skill provides a checklist and code patterns for Go applications, covering secrets management, input validation, authentication, and other security aspects. It guides the user to implement secure coding practices and avoid common pitfalls.

Inputs & outputs

You give it
Go source code, configuration files, or API endpoint definitions
You get back
Go code adhering to security best practices, with identified vulnerabilities

When to use security-review

  • Review Go auth implementation
  • Secure configuration handling
  • Validate HTTP API inputs

About this skill

Security Review Skill (Go Edition)

This skill ensures Go code follows security best practices and identifies potential vulnerabilities specific to Go applications.

When to Activate

  • Implementing authentication or authorization in Go web applications
  • Handling user input or file uploads in Gin/Go HTTP handlers
  • Creating new API endpoints in Go
  • Working with secrets or credentials in Go configuration
  • Implementing payment or sensitive features in Go
  • Storing or transmitting sensitive data
  • Integrating third-party APIs with Go clients

Security Checklist

1. Secrets Management

❌ NEVER Do This

// Hardcoded secrets in source code
const apiKey = "sk-proj-xxxxx"
const dbPassword = "password123"

// In configuration structs
type Config struct {
    JWTSecret string `json:"jwt_secret"`  // Will be hardcoded in JSON
}

✅ ALWAYS Do This

// Use environment variables or dedicated secret management
import (
    "os"
    "fmt"
)

// Load from environment
jwtSecret := os.Getenv("JWT_SECRET")
if jwtSecret == "" {
    return fmt.Errorf("JWT_SECRET environment variable not set")
}

// Or use configuration struct with validation
type Config struct {
    JWTSecret   string `env:"JWT_SECRET,required"`
    DatabaseURL string `env:"DATABASE_URL,required"`
    APIKey      string `env:"API_KEY"`
}

// Use packages like github.com/caarlos0/env for structured env loading
import "github.com/caarlos0/env/v6"

var cfg Config
if err := env.Parse(&cfg); err != nil {
    log.Fatal("Failed to parse config:", err)
}

Verification Steps

  • No hardcoded API keys, tokens, or passwords in source code
  • All secrets loaded from environment variables or secret managers
  • Configuration files with secrets excluded from git (.env, config/local.yaml)
  • No secrets in git history (check with git log -p -S "password")
  • Production secrets managed by platform (Kubernetes Secrets, AWS Secrets Manager, etc.)
  • Secrets validated at application startup

2. Input Validation

Always Validate User Input

// Use struct tags for validation (Gin framework example)
type CreateUserRequest struct {
    Email    string `json:"email" binding:"required,email"`
    Name     string `json:"name" binding:"required,min=1,max=100"`
    Age      int    `json:"age" binding:"required,min=0,max=150"`
    Password string `json:"password" binding:"required,min=8"`
}

// In Gin handler
func CreateUser(c *gin.Context) {
    var req CreateUserRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": "Invalid input", "details": err.Error()})
        return
    }

    // Proceed with validated request
    user, err := service.CreateUser(req)
    if err != nil {
        c.JSON(500, gin.H{"error": "Internal server error"})
        return
    }

    c.JSON(200, gin.H{"data": user})
}

// Custom validation with go-playground/validator
import "github.com/go-playground/validator/v10"

var validate = validator.New()

func validateUser(req CreateUserRequest) error {
    if err := validate.Struct(req); err != nil {
        return fmt.Errorf("validation failed: %w", err)
    }

    // Custom business logic validation
    if strings.Contains(strings.ToLower(req.Name), "admin") {
        return fmt.Errorf("name cannot contain 'admin'")
    }

    return nil
}

File Upload Validation

import (
    "mime/multipart"
    "path/filepath"
    "strings"
)

func validateFileUpload(fileHeader *multipart.FileHeader) error {
    // Size check (5MB max)
    const maxSize = 5 * 1024 * 1024 // 5MB
    if fileHeader.Size > maxSize {
        return fmt.Errorf("file too large (max 5MB)")
    }

    // Content type check
    contentType := fileHeader.Header.Get("Content-Type")
    allowedTypes := []string{"image/jpeg", "image/png", "image/gif"}
    validType := false
    for _, t := range allowedTypes {
        if contentType == t {
            validType = true
            break
        }
    }
    if !validType {
        return fmt.Errorf("invalid file type: %s", contentType)
    }

    // Extension check (additional safety)
    ext := strings.ToLower(filepath.Ext(fileHeader.Filename))
    allowedExts := []string{".jpg", ".jpeg", ".png", ".gif"}
    validExt := false
    for _, e := range allowedExts {
        if ext == e {
            validExt = true
            break
        }
    }
    if !validExt {
        return fmt.Errorf("invalid file extension: %s", ext)
    }

    // Check filename for path traversal
    if strings.Contains(fileHeader.Filename, "..") || strings.Contains(fileHeader.Filename, "/") {
        return fmt.Errorf("invalid filename")
    }

    return nil
}

// In Gin handler for file upload
func UploadFile(c *gin.Context) {
    file, err := c.FormFile("file")
    if err != nil {
        c.JSON(400, gin.H{"error": "No file uploaded"})
        return
    }

    if err := validateFileUpload(file); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    // Save the file securely
    safeFilename := generateSafeFilename(file.Filename)
    dst := filepath.Join("uploads", safeFilename)
    if err := c.SaveUploadedFile(file, dst); err != nil {
        c.JSON(500, gin.H{"error": "Failed to save file"})
        return
    }

    c.JSON(200, gin.H{"message": "File uploaded successfully"})
}

Verification Steps

  • All user inputs validated with struct tags or validation libraries
  • File uploads restricted by size, content type, and extension
  • No direct concatenation of user input in queries or commands
  • Whitelist validation (allow known good values) instead of blacklist
  • Error messages generic, no sensitive information exposed
  • Path traversal prevention for file uploads
  • Input length limits enforced to prevent DoS attacks

3. SQL Injection Prevention

❌ NEVER Concatenate SQL

// DANGEROUS - SQL Injection vulnerability
func getUserByEmailUnsafe(email string) (*User, error) {
    query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)
    var user User
    err := db.Raw(query).Scan(&user).Error
    return &user, err
}

// Also dangerous: using Sprintf with query conditions
func searchUsersUnsafe(search string) ([]User, error) {
    condition := ""
    if search != "" {
        condition = fmt.Sprintf("WHERE name LIKE '%%%s%%'", search)
    }
    query := fmt.Sprintf("SELECT * FROM users %s", condition)
    var users []User
    err := db.Raw(query).Scan(&users).Error
    return users, err
}

✅ ALWAYS Use Parameterized Queries

// Safe - GORM parameterized queries
func getUserByEmailSafe(email string) (*User, error) {
    var user User
    err := db.Where("email = ?", email).First(&user).Error
    return &user, err
}

// Safe - GORM with map conditions
func searchUsersSafe(search string) ([]User, error) {
    dbQuery := db.Model(&User{})
    if search != "" {
        dbQuery = dbQuery.Where("name LIKE ?", "%"+search+"%")
    }
    var users []User
    err := dbQuery.Find(&users).Error
    return users, err
}

// Safe - Raw SQL with parameterized queries
func getUserRawSafe(email string) (*User, error) {
    var user User
    err := db.Raw("SELECT * FROM users WHERE email = ?", email).Scan(&user).Error
    return &user, err
}

// Safe - PostgreSQL style numbered parameters
func getUsersByIDs(ids []uint) ([]User, error) {
    var users []User
    query := "SELECT * FROM users WHERE id IN (?)"
    err := db.Raw(query, ids).Scan(&users).Error
    return users, err
}

// Safe - Using GORM's Exec with parameters
func updateUserEmail(userID uint, newEmail string) error {
    result := db.Exec("UPDATE users SET email = ? WHERE id = ?", newEmail, userID)
    return result.Error
}

Verification Steps

  • All database queries use parameterized queries (?, $1, @param placeholders)
  • No string concatenation or fmt.Sprintf for SQL query construction
  • ORM (GORM) used correctly with Where(), Find(), Raw() with parameters
  • User input never directly interpolated into SQL strings
  • Raw SQL queries always use parameter binding
  • SQL query builders used for complex dynamic queries
  • Database driver's parameterized query support utilized

4. Authentication & Authorization

JWT Token Handling and Storage

// ❌ WRONG: Storing tokens in localStorage (vulnerable to XSS in SPA)
// Frontend JavaScript: localStorage.setItem('token', token)
// Go backend returning token in JSON response (insecure for web)
func LoginHandler(c *gin.Context) {
    // ... authenticate user ...
    token, err := generateJWT(user)
    if err != nil {
        c.JSON(500, gin.H{"error": "Failed to generate token"})
        return
    }
    // Insecure: token in JSON response (client stores in localStorage)
    c.JSON(200, gin.H{"token": token})  // ❌ VULNERABLE TO XSS
}

// ✅ CORRECT: httpOnly, Secure, SameSite cookies
func LoginHandlerSecure(c *gin.Context) {
    // ... authenticate user ...
    token, err := generateJWT(user)
    if err != nil {
        c.JSON(500, gin.H{"error": "Failed to generate token"})
        return
    }

    // Set secure httpOnly cookie
    c.SetCookie(
        "token",           // name
        token,             // value
        3600,              // max age in seconds
        "/",               // path
        ".example.com",    // domain (set appropriately)
        true,              // secure (HTTPS only)
        true,              // httpOnly (inaccessible to JavaScript)
    )

    c.JSON(200, gin.H{"message": "Login successful"})
}

// JWT generation and validation using golang-jwt/jwt
import "github.com/golang-jwt/jwt/v4"

type Claims struct {
    UserID uint   `json:"user_id"`
    Role   string `json:"role"`
    jwt.RegisteredClaims
}

func generateJWT(user User) (string, error) {
    claims := &Claims{
        UserID: user.ID,
        Role:   user.Role,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresA

---

*Content truncated.*

When not to use it

  • When hardcoding API keys, tokens, or passwords in source code
  • When configuration structs directly expose secrets in JSON
  • When user inputs are not validated before processing

Limitations

  • The skill focuses on Go-specific security practices.
  • It requires manual verification steps for each security aspect.
  • It does not automatically fix vulnerabilities, but provides guidance.

How it compares

This skill offers Go-specific security guidance and patterns, directly addressing common vulnerabilities in Go applications, unlike general security checklists that may not cover language-specific nuances.

Compared to similar skills

security-review side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
security-review (this skill)06moReviewIntermediate
security-header-generator59moCautionIntermediate
backend-security-coder244moNo flagsIntermediate
api-security-best-practices156moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry