Comprehensive security review workflow for identifying vulnerabilities
Use this skill when conducting security audits or reviewing code for vulnerabilities.
// ❌ Bad: Plain text password comparison
if (password === user.password) { ... }
// ✅ Good: Secure hash comparison
import bcrypt from 'bcrypt';
if (await bcrypt.compare(password, user.passwordHash)) { ... }
// ❌ Bad: Weak JWT secret
const token = jwt.sign(payload, 'secret123');
// ✅ Good: Strong secret from environment
const token = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '1h',
algorithm: 'HS256',
});
// ❌ Bad: No authorization check
app.delete('/api/posts/:id', async (req, res) => {
await db.posts.delete(req.params.id);
});
// ✅ Good: Verify ownership
app.delete('/api/posts/:id', authenticate, async (req, res) => {
const post = await db.posts.findById(req.params.id);
if (post.authorId !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Forbidden' });
}
await db.posts.delete(req.params.id);
});
// ❌ Bad: No validation
app.post('/api/users', (req, res) => {
db.users.create(req.body);
});
// ✅ Good: Schema validation
import { z } from 'zod';
const CreateUserSchema = z.object({
email: z.string().email().max(255),
name: z.string().min(1).max(100),
age: z.number().int().min(0).max(150).optional(),
});
app.post('/api/users', (req, res) => {
const result = CreateUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(422).json({ errors: result.error.issues });
}
db.users.create(result.data);
});
// ❌ Bad: String interpolation
const user = await db.query(
`SELECT * FROM users WHERE email = '${email}'`
);
// ✅ Good: Parameterized query
const user = await db.query(
'SELECT * FROM users WHERE email = $1',
[email]
);
// ✅ Good: ORM with escaping
const user = await db.users.findOne({
where: { email },
});
// ❌ Bad: Direct HTML injection
element.innerHTML = userInput;
// ✅ Good: Text content only
element.textContent = userInput;
// ✅ Good: Sanitize if HTML needed
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);
// React: Already safe by default
return <div>{userInput}</div>;
// ❌ Bad: dangerouslySetInnerHTML without sanitization
<div dangerouslySetInnerHTML={{ __html: userInput }} />
// ✅ Good: Sanitize first
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
// ✅ Good: CSRF token validation
import csrf from 'csurf';
app.use(csrf({ cookie: true }));
// Include token in forms
app.get('/form', (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
// Token automatically validated on POST
app.post('/submit', (req, res) => {
// CSRF token validated
});
// ❌ Bad: Hardcoded secrets
const API_KEY = 'sk_live_abc123xyz';
const DB_PASSWORD = 'password123';
// ✅ Good: Environment variables
const API_KEY = process.env.API_KEY;
const DB_PASSWORD = process.env.DB_PASSWORD;
// ✅ Good: Validation at startup
if (!process.env.API_KEY) {
throw new Error('API_KEY environment variable is required');
}
import helmet from 'helmet';
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
},
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
},
}));
import rateLimit from 'express-rate-limit';
// Global rate limit
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
message: { error: 'Too many requests' },
});
app.use('/api/', limiter);
// Stricter for auth endpoints
const authLimiter = rateLimit({
windowMs: 60 * 60 * 1000, // 1 hour
max: 5, // 5 attempts per hour
});
app.use('/api/auth/login', authLimiter);
import multer from 'multer';
import path from 'path';
const upload = multer({
limits: {
fileSize: 5 * 1024 * 1024, // 5MB limit
},
fileFilter: (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (!allowedTypes.includes(file.mimetype)) {
return cb(new Error('Invalid file type'));
}
// Check extension too (mimetype can be spoofed)
const ext = path.extname(file.originalname).toLowerCase();
if (!['.jpg', '.jpeg', '.png', '.gif'].includes(ext)) {
return cb(new Error('Invalid file extension'));
}
cb(null, true);
},
});
# Dependency vulnerabilities
npm audit
npm audit fix
# Secret scanning
npx gitleaks detect
# Static analysis
npx eslint --plugin security src/
# SAST scanning
npx semgrep --config auto
| Rank | Vulnerability | Prevention | |------|---------------|------------| | 1 | Broken Access Control | Verify permissions on every request | | 2 | Cryptographic Failures | Use strong algorithms, secure storage | | 3 | Injection | Parameterized queries, input validation | | 4 | Insecure Design | Threat modeling, security requirements | | 5 | Security Misconfiguration | Secure defaults, remove debug features | | 6 | Vulnerable Components | Regular updates, dependency scanning | | 7 | Auth Failures | MFA, secure session management | | 8 | Data Integrity Failures | Verify signatures, validate inputs | | 9 | Logging Failures | Log security events, monitor | | 10 | SSRF | Validate URLs, allowlist destinations |
## Security Review: [Component/Feature]
### Scope
- Files reviewed: X
- Lines of code: Y
### Findings
#### Critical 🔴
1. [Finding description]
- Location: file:line
- Impact: [What could happen]
- Fix: [How to fix]
#### High ⚠️
...
#### Medium 💡
...
### Passed Checks ✅
- [ ] Authentication implemented correctly
- [ ] Authorization on all endpoints
- [ ] Input validation present
- [ ] No SQL injection vectors
- [ ] XSS prevention in place
- [ ] Secrets not in code
- [ ] Dependencies up to date
### Recommendations
1. [Action item]
2. [Action item]
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer