Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when implementing authentication/authorization, securing user input, or preventing OWASP Top 10 vulnerabilities — including custom security implementations such as hashing passwords with bcrypt/argon2, sanitizing SQL queries with parameterized statements, configuring CORS/CSP headers, validating input with Zod, and setting up JWT tokens. Invoke for authentication, authorization, input validation, encryption, OWASP Top 10 prevention, secure session management, and security hardening. For pre-
.claude/skills/jeffallan-secure-code-guardian/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-03 | ✗→✓ | ▲ Improved | 88% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 55% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 83% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 40% | 0% |
After each implementation step, verify:
' OR 1=1--) are rejected; confirm XSS payloads (<script>alert(1)</script>) are escaped or rejected.curl -I, Mozilla Observatory) that security headers are present and CORS origin allowlist is correct.Load detailed guidance based on context:
| Topic | Reference | Load When | |-------|-----------|-----------| | OWASP | references/owasp-prevention.md | OWASP Top 10 patterns | | Authentication | references/authentication.md | Password hashing, JWT | | Input Validation | references/input-validation.md | Zod, SQL injection | | XSS/CSRF | references/xss-csrf.md | XSS prevention, CSRF | | Headers | references/security-headers.md | Helmet, rate limiting |
typescriptimport bcrypt from 'bcrypt'; const SALT_ROUNDS = 12; // minimum 10; 12 balances security and performance export async function hashPassword(plaintext: string): Promise<string> { return bcrypt.hash(plaintext, SALT_ROUNDS); } export async function verifyPassword(plaintext: string, hash: string): Promise<boolean> { return bcrypt.compare(plaintext, hash); }
typescript// NEVER: `SELECT * FROM users WHERE email = '${email}'` // ALWAYS: use positional parameters import { Pool } from 'pg'; const pool = new Pool(); export async function getUserByEmail(email: string) { const { rows } = await pool.query( 'SELECT id, email, role FROM users WHERE email = $1', [email] // value passed separately — never interpolated ); return rows[0] ?? null; }
typescriptimport { z } from 'zod'; const LoginSchema = z.object({ email: z.string().email().max(254), password: z.string().min(8).max(128), }); export function validateLoginInput(raw: unknown) { const result = LoginSchema.safeParse(raw); if (!result.success) { // Return generic error — never echo raw input back throw new Error('Invalid credentials format'); } return result.data; }
typescriptimport jwt from 'jsonwebtoken'; const JWT_SECRET = process.env.JWT_SECRET!; // never hardcode export function verifyToken(token: string): jwt.JwtPayload { // Throws if expired, tampered, or wrong algorithm const payload = jwt.verify(token, JWT_SECRET, { algorithms: ['HS256'], // explicitly allowlist algorithm issuer: 'your-app', audience: 'your-app', }); if (typeof payload === 'string') throw new Error('Invalid token payload'); return payload; }
typescriptimport express from 'express'; import rateLimit from 'express-rate-limit'; import helmet from 'helmet'; const app = express(); app.use(helmet()); // sets CSP, HSTS, X-Frame-Options, etc. app.use(express.json({ limit: '10kb' })); // limit payload size const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 10, // 10 attempts per window per IP standardHeaders: true, legacyHeaders: false, }); app.post('/api/login', authLimiter, async (req, res) => { // 1. Validate input const { email, password } = validateLoginInput(req.body); // 2. Authenticate — parameterized query, constant-time compare const user = await getUserByEmail(email); if (!user || !(await verifyPassword(password, user.passwordHash))) { // Generic message — do not reveal whether email exists return res.status(401).json({ error: 'Invalid credentials' }); } // 3. Authorize — issue scoped, short-lived token const token = jwt.sign( { sub: user.id, role: user.role }, JWT_SECRET, { algorithm: 'HS256', expiresIn: '15m', issuer: 'your-app', audience: 'your-app' } ); // 4. Secure response — token in httpOnly cookie, not body res.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'strict' }); return res.json({ message: 'Authenticated' }); });
When implementing security features, provide:
OWASP Top 10, bcrypt/argon2, JWT, OAuth 2.0, OIDC, CSP, CORS, rate limiting, input validation, output encoding, encryption (AES, RSA), TLS, security headers
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 15,637 | 13,848 | -11% | 1 | 1 | 0% | 3,448 | 4,374 | +27% | 0 | 0 | — |
case-02 | pass→pass | 16,061 | 9,021 | -44% | 1 | 1 | 0% | 3,075 | 3,224 | +5% | 0 | 0 | — |
case-03 | fail→pass | 10,510 | 14,117 | +34% | 1 | 1 | 0% | 2,238 | 4,206 | +88% | 0 | 0 | — |
case-04 | pass→pass | 6,003 | 7,120 | +19% | 1 | 1 | 0% | 1,141 | 2,806 | +146% | 0 | 0 | — |
case-05 | fail→pass | 16,632 | 12,760 | -23% | 1 | 1 | 0% | 3,330 | 3,950 | +19% | 0 | 0 | — |
case-06 | fail→fail | 10,186 | 10,763 | +6% | 1 | 1 | 0% | 2,011 | 3,507 | +74% | 0 | 0 | — |
case-07 | fail→pass | 13,047 | 13,739 | +5% | 1 | 1 | 0% | 2,767 | 4,279 | +55% | 0 | 0 | — |
case-08 | fail→pass | 9,858 | 10,431 | +6% | 1 | 1 | 0% | 1,985 | 3,637 | +83% | 0 | 0 | — |
case-09 | pass→pass | 16,975 | 14,718 | -13% | 1 | 1 | 0% | 3,324 | 4,588 | +38% | 0 | 0 | — |
case-10 | pass→pass | 17,238 | 14,815 | -14% | 1 | 1 | 0% | 3,432 | 4,563 | +33% | 0 | 0 | — |
case-11 | pass→fail | 16,511 | 15,822 | -4% | 1 | 1 | 0% | 3,254 | 4,859 | +49% | 0 | 0 | — |
case-12 | pass→pass | 20,745 | 17,592 | -15% | 1 | 1 | 0% | 3,886 | 5,132 | +32% | 0 | 0 | — |
case-13 | pass→pass | 17,115 | 16,369 | -4% | 1 | 1 | 0% | 3,715 | 5,041 | +36% | 0 | 0 | — |
case-14 | pass→pass | 14,537 | 13,159 | -9% | 1 | 1 | 0% | 2,505 | 3,869 | +54% | 0 | 0 | — |
case-15 | pass→pass | 13,425 | 13,712 | +2% | 1 | 1 | 0% | 2,715 | 4,253 | +57% | 0 | 0 | — |
case-16 | pass→pass | 9,079 | 7,659 | -16% | 1 | 1 | 0% | 1,825 | 3,048 | +67% | 0 | 0 | — |
case-17 | pass→pass | 10,220 | 9,586 | -6% | 1 | 1 | 0% | 1,867 | 3,403 | +82% | 0 | 0 | — |
case-18 | pass→pass | 17,374 | 15,427 | -11% | 1 | 1 | 0% | 3,293 | 4,533 | +38% | 0 | 0 | — |
case-19 | fail→pass | 15,202 | 15,652 | +3% | 1 | 1 | 0% | 3,236 | 4,530 | +40% | 0 | 0 | — |
case-20 | pass→pass | 15,045 | 16,143 | +7% | 1 | 1 | 0% | 2,819 | 5,027 | +78% | 0 | 0 | — |
case-21 | pass→pass | 18,824 | 17,537 | -7% | 1 | 1 | 0% | 4,347 | 5,561 | +28% | 0 | 0 | — |
case-22 | pass→pass | 18,765 | 18,510 | -1% | 1 | 1 | 0% | 3,231 | 4,832 | +50% | 0 | 0 | — |
case-23 | pass→pass | 9,454 | 14,380 | +52% | 1 | 1 | 0% | 1,916 | 4,293 | +124% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +17 percentage points is the difference between those two pass rates over the 23 comparable cases. 1 case got worse with the skill loaded, and it is included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.