agentsclimarketplace

Golang security

Skill Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack/plugins/devtools-pack/skills/golang-security

When to activate: Go security, JWT, bcrypt, CORS, input validation, SQL injection prevention, secrets management, rate limiting in GoFrom its SKILL.md

Install
npx -y skills add Mattakushi432/Claude-Code-Skills-Custom-DevTools-Pack --skill golang-security

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

5.7 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it

Go Security Patterns

Password Hashing (bcrypt)

import "golang.org/x/crypto/bcrypt"

func HashPassword(password string) (string, error) {
    hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
    return string(hash), err
}

func CheckPassword(password, hash string) bool {
    return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil
}

JWT Authentication

import "github.com/golang-jwt/jwt/v5"

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

func GenerateToken(userID, role, secret string) (string, error) {
    claims := Claims{
        UserID: userID,
        Role:   role,
        RegisteredClaims: jwt.RegisteredClaims{
            ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
            IssuedAt:  jwt.NewNumericDate(time.Now()),
            Issuer:    "myapp",
        },
    }
    return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).
        SignedString([]byte(secret))
}

func ValidateToken(tokenStr, secret string) (*Claims, error) {
    token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(t *jwt.Token) (any, error) {
        if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
            return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
        }
        return []byte(secret), nil
    })
    if err != nil { return nil, err }
    if claims, ok := token.Claims.(*Claims); ok && token.Valid {
        return claims, nil
    }
    return nil, errors.New("invalid token")
}

Input Validation

import "github.com/go-playground/validator/v10"

var validate = validator.New()

type CreateArticleRequest struct {
    Title   string   `json:"title"   validate:"required,min=3,max=200"`
    Body    string   `json:"body"    validate:"required,min=10"`
    Tags    []string `json:"tags"    validate:"max=10,dive,alphanum"`
}

func validateRequest(req any) error {
    if err := validate.Struct(req); err != nil {
        var ve validator.ValidationErrors
        if errors.As(err, &ve) {
            msgs := make([]string, len(ve))
            for i, fe := range ve {
                msgs[i] = fmt.Sprintf("%s: %s", fe.Field(), fe.Tag())
            }
            return fmt.Errorf("validation failed: %s", strings.Join(msgs, ", "))
        }
        return err
    }
    return nil
}

SQL Injection Prevention

// ALWAYS use parameterized queries — never string concatenation
// Bad
db.Exec("SELECT * FROM users WHERE email = '" + email + "'")

// Good — database/sql
row := db.QueryRowContext(ctx, "SELECT id, name FROM users WHERE email = $1", email)

// Good — GORM
db.Where("email = ?", email).First(&user)
db.Where("email = ? AND role = ?", email, role).Find(&users)

Secrets from Environment

import "github.com/caarlos0/env/v11"

type Config struct {
    DBUrl     string `env:"DATABASE_URL,required"`
    JWTSecret string `env:"JWT_SECRET,required"`
    Port      int    `env:"PORT" envDefault:"8080"`
}

func LoadConfig() (Config, error) {
    var cfg Config
    return cfg, env.Parse(&cfg)
}

// Never hardcode secrets; validate at startup
func main() {
    cfg, err := LoadConfig()
    if err != nil { log.Fatalf("config error: %v", err) }
    // ...
}

CORS Configuration

// Gin
r.Use(cors.New(cors.Config{
    AllowOrigins:     []string{"https://app.example.com"},
    AllowMethods:     []string{"GET", "POST", "PUT", "DELETE"},
    AllowHeaders:     []string{"Authorization", "Content-Type"},
    ExposeHeaders:    []string{"X-Request-ID"},
    AllowCredentials: true,
    MaxAge:           12 * time.Hour,
}))

// net/http manually
func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "https://app.example.com")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
        w.Header().Set("Access-Control-Allow-Headers", "Authorization, Content-Type")
        if r.Method == http.MethodOptions {
            w.WriteHeader(http.StatusNoContent)
            return
        }
        next.ServeHTTP(w, r)
    })
}

Rate Limiting

import "golang.org/x/time/rate"

type IPLimiter struct {
    limiters sync.Map
    rate     rate.Limit
    burst    int
}

func (l *IPLimiter) Get(ip string) *rate.Limiter {
    v, _ := l.limiters.LoadOrStore(ip, rate.NewLimiter(l.rate, l.burst))
    return v.(*rate.Limiter)
}

func rateLimitMiddleware(limiter *IPLimiter) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            ip, _, _ := net.SplitHostPort(r.RemoteAddr)
            if !limiter.Get(ip).Allow() {
                http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
                return
            }
            next.ServeHTTP(w, r)
        })
    }
}

Common Anti-Patterns

  • md5/sha1 for passwords — always use bcrypt, scrypt, or argon2id
  • Logging request bodies with credentials — strip Authorization, Cookie, password fields before logging
  • AllowAllOrigins: true in CORS — whitelist specific origins in production
  • Embedding secrets in binary — use environment variables or a secret manager
  • gorilla/sessions with default key — generate a cryptographically random session key at startup

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,851. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.