Skip to content

Secure Code Guardian Guide: Usage, Examples, and Best Practices for Beginners

Purpose

This post demonstrates how to use the Secure Code Guardian skill in Claude Code to build security into your development workflow.

Environment

  • Claude Code CLI
  • claude-skills plugin
  • Security development workflow

What is Secure Code Guardian?

Secure Code Guardian is a security review skill that activates automatically when you work with authentication, user input, secrets, API endpoints, or payment features. It provides a comprehensive security checklist and patterns to help you identify vulnerabilities before they reach production.

The skill addresses these security areas:

  • Authentication and authorization
  • Input validation and sanitization
  • SQL injection prevention
  • XSS protection
  • CSRF protection
  • Secret management
  • Rate limiting
  • Error message security

Installation and Setup

First, install the claude-skills plugin if you haven’t already:

Terminal window
npm install -g @jeffallan/claude-skills

Then verify the installation:

Terminal window
claude-skills --version

Secure Code Guardian activates automatically based on context. You don’t need to manually invoke it - it triggers when Claude detects security-sensitive work.

Core Usage Patterns

The skill activates in these scenarios:

Automatic Triggers:

  • Adding authentication features
  • Handling user input (forms, APIs, CLI)
  • Working with secrets (API keys, tokens)
  • Creating API endpoints
  • Implementing payment processing
  • Managing sessions

Manual Invocation: You can also explicitly call the skill:

Use security-review to check this auth implementation

Example 1: Authentication Implementation

When I add authentication to an Express.js application, Secure Code Guardian activates automatically:

auth.ts
import express from 'express'
import bcrypt from 'bcrypt'
const app = express()
app.post('/login', async (req, res) => {
const { username, password } = req.body
// Look up user
const user = await db.users.findOne({ username })
// Verify password
const isValid = await bcrypt.compare(password, user.passwordHash)
if (isValid) {
res.json({ token: generateToken(user) })
} else {
res.status(401).json({ error: 'Invalid credentials' })
}
})

The skill immediately flagged several issues:

  1. Missing rate limiting - Brute force attacks possible
  2. No input validation - Malformed input could crash the server
  3. Generic error messages - Reveals user existence
  4. No account lockout - Unlimited password attempts
  5. Missing HTTPS enforcement - Credentials sent in plaintext

Here’s the secured version:

auth-secured.ts
import express from 'express'
import bcrypt from 'bcrypt'
import rateLimit from 'express-rate-limit'
import { z } from 'zod'
// Rate limiting
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 5 // 5 attempts per window
})
const loginSchema = z.object({
username: z.string().min(3).max(50).regex(/^[a-zA-Z0-9_]+$/),
password: z.string().min(8)
})
app.post('/login', loginLimiter, async (req, res) => {
// Validate input
const validated = loginSchema.parse(req.body)
const { username, password } = validated
// Look up user
const user = await db.users.findOne({ username })
// Generic error message
if (!user) {
return res.status(401).json({
error: 'Invalid username or password'
})
}
// Check account lockout
if (user.lockedUntil && user.lockedUntil > Date.now()) {
return res.status(429).json({
error: 'Account temporarily locked'
})
}
// Verify password
const isValid = await bcrypt.compare(password, user.passwordHash)
if (isValid) {
// Reset failed attempts
await db.users.updateOne(
{ _id: user._id },
{ $set: { failedAttempts: 0 } }
)
res.json({ token: generateToken(user) })
} else {
// Increment failed attempts
const attempts = (user.failedAttempts || 0) + 1
if (attempts >= 5) {
await db.users.updateOne(
{ _id: user._id },
{
$set: {
failedAttempts: attempts,
lockedUntil: Date.now() + 30 * 60 * 1000 // 30 minutes
}
}
)
} else {
await db.users.updateOne(
{ _id: user._id },
{ $set: { failedAttempts: attempts } }
)
}
res.status(401).json({
error: 'Invalid username or password'
})
}
})
// Force HTTPS in production
if (process.env.NODE_ENV === 'production') {
app.use((req, res, next) => {
if (!req.secure) {
return res.redirect('https://' + req.headers.host + req.url)
}
next()
})
}

Example 2: SQL Injection Prevention

When I query a database with user input, Secure Code Guardian checks for SQL injection vulnerabilities:

vulnerable.ts
// DON'T: Direct string concatenation
async function getUser(userId: string) {
const query = `SELECT * FROM users WHERE id = '${userId}'`
return db.query(query)
}

The skill immediately identified this as critical. Here’s the fix:

secure.ts
// DO: Parameterized queries
async function getUser(userId: string) {
const query = 'SELECT * FROM users WHERE id = $1'
return db.query(query, [userId])
}

Or using an ORM:

"orm.ts
// DO: ORM with built-in protection
async function getUser(userId: string) {
return prisma.user.findUnique({
where: { id: userId }
})
}

Example 3: Secret Management

Hardcoded secrets are a major security risk. When I wrote this:

bad-secrets.ts
// DON'T: Hardcoded secrets
const apiKey = "sk-proj-xxxxxxxxxxxxx"
const dbPassword = "MySuperSecretPassword123!"

Secure Code Guardian flagged every hardcoded secret and guided me to:

good-secrets.ts
// DO: Environment variables
const apiKey = process.env.OPENAI_API_KEY
const dbPassword = process.env.DB_PASSWORD
if (!apiKey || !dbPassword) {
throw new Error('Required environment variables not configured')
}

Then create a .env file (added to .gitignore):

.env
OPENAI_API_KEY=sk-proj-xxxxxxxxxxxxx
DB_PASSWORD=MySuperSecretPassword123!

Best Practices

DO

Use parameterized queries:

// Safe
db.query('SELECT * FROM users WHERE id = $1', [userId])
// Not safe
db.query(`SELECT * FROM users WHERE id = '${userId}'`)

Validate all input:

import { z } from 'zod'
const schema = z.object({
email: z.string().email(),
age: z.number().int().min(0).max(150)
})
const validated = schema.parse userInput)

Use HTTPS in production:

if (process.env.NODE_ENV === 'production') {
app.use((req, res, next) => {
if (!req.secure) {
return res.redirect('https://' + req.headers.host + req.url)
}
next()
})
}

Implement rate limiting:

import rateLimit from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 100
})
app.use('/api/', limiter)

Sanitize HTML output:

import DOMPurify from 'dompurify'
const clean = DOMPurify.sanitize(userInput)

DON’T

Don’t expose errors:

// DON'T: Leak implementation details
res.status(500).json({
error: `Database connection failed: ${err.message}`
})
// DO: Generic messages
res.status(500).json({
error: 'An error occurred. Please try again later.'
})
// Log the actual error server-side
console.error('Database error:', err)

Don’t store plain text passwords:

// DON'T
user.password = password
// DO
const hash = await bcrypt.hash(password, 10)
user.passwordHash = hash

Don’t trust client-side validation:

// Client-side validation improves UX but isn't secure
// Always validate on the server
const validated = serverSchema.parse(userInput)

Don’t use weak crypto:

// DON'T: MD5 is broken
const hash = md5(password)
// DO: bcrypt, Argon2, or scrypt
const hash = await bcrypt.hash(password, 10)

Common Security Checklist

Before committing code, verify:

  • No hardcoded secrets (API keys, passwords, tokens)
  • All user inputs validated
  • SQL injection prevention (parameterized queries)
  • XSS prevention (sanitized HTML)
  • CSRF protection enabled
  • Authentication/authorization verified
  • Rate limiting on all endpoints
  • Error messages don’t leak sensitive data
  • HTTPS enforced in production
  • Dependencies are up to date

Integration with Other Skills

Secure Code Guardian works alongside other claude-skills:

  • tdd-workflow: Write security tests first
  • code-reviewer: General code quality after security review
  • springboot-security: Java Spring Security patterns
  • backend-patterns: API security design

Summary

In this post, I showed how to use Secure Code Guardian to build security into your development workflow. The key points are:

  • The skill activates automatically for security-sensitive code
  • It covers authentication, input validation, SQL injection, XSS, CSRF, secrets, rate limiting, and error handling
  • Always validate input, use parameterized queries, and avoid hardcoded secrets
  • Security testing should be part of your TDD workflow
  • Use the security checklist before committing

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments