Install
Terminal · npx$
npx skills add https://github.com/hoodini/ai-agents-skills --skill owasp-securityWorks with Paperclip
How Owasp Security fits into a Paperclip company.
Owasp Security drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.
S
SaaS FactoryPaired
Pre-configured AI company — 18 agents, 18 skills, one-time purchase.
$27$59
Explore packSource file
SKILL.md536 linesExpandCollapse
---name: owasp-securitydescription: Implement secure coding practices following OWASP Top 10. Use when preventing security vulnerabilities, implementing authentication, securing APIs, or conducting security reviews. Triggers on OWASP, security, XSS, SQL injection, CSRF, authentication security, secure coding, vulnerability.--- # OWASP Top 10 Security Prevent common security vulnerabilities in web applications. ## OWASP Top 10 (2021) | # | Vulnerability | Prevention ||---|---------------|------------|| A01 | Broken Access Control | Proper authorization checks || A02 | Cryptographic Failures | Strong encryption, secure storage || A03 | Injection | Input validation, parameterized queries || A04 | Insecure Design | Threat modeling, secure patterns || A05 | Security Misconfiguration | Hardened configs, no defaults || A06 | Vulnerable Components | Dependency scanning, updates || A07 | Auth Failures | MFA, secure session management || A08 | Data Integrity Failures | Input validation, signed updates || A09 | Logging Failures | Comprehensive audit logs || A10 | SSRF | URL validation, allowlists | ## A01: Broken Access Control ### Prevention Patterns```typescript// ❌ BAD: No authorization checkapp.get('/api/users/:id', async (req, res) => { const user = await db.users.findById(req.params.id); res.json(user);}); // ✅ GOOD: Verify ownershipapp.get('/api/users/:id', authenticate, async (req, res) => { const userId = req.params.id; // Users can only access their own data if (req.user.id !== userId && req.user.role !== 'admin') { return res.status(403).json({ error: 'Forbidden' }); } const user = await db.users.findById(userId); res.json(user);}); // ✅ GOOD: Role-based access control (RBAC)const requireRole = (...roles: string[]) => { return (req: Request, res: Response, next: NextFunction) => { if (!roles.includes(req.user?.role)) { return res.status(403).json({ error: 'Insufficient permissions' }); } next(); };}; app.delete('/api/posts/:id', authenticate, requireRole('admin', 'moderator'), deletePost);``` ### Insecure Direct Object Reference (IDOR)```typescript// ❌ BAD: Predictable IDs exposedGET /api/invoices/1001GET /api/invoices/1002 // Can enumerate others' invoices // ✅ GOOD: Use UUIDs + ownership checkapp.get('/api/invoices/:id', authenticate, async (req, res) => { const invoice = await db.invoices.findOne({ id: req.params.id, userId: req.user.id, // Enforce ownership }); if (!invoice) { return res.status(404).json({ error: 'Not found' }); } res.json(invoice);});``` ## A02: Cryptographic Failures ### Password Hashing```typescriptimport bcrypt from 'bcrypt';import crypto from 'crypto'; // ✅ Hash passwords with bcryptconst SALT_ROUNDS = 12; async function hashPassword(password: string): Promise<string> { return bcrypt.hash(password, SALT_ROUNDS);} async function verifyPassword(password: string, hash: string): Promise<boolean> { return bcrypt.compare(password, hash);} // ✅ Secure token generationfunction generateSecureToken(length = 32): string { return crypto.randomBytes(length).toString('hex');} // ✅ Encrypt sensitive dataconst ALGORITHM = 'aes-256-gcm';const KEY = crypto.scryptSync(process.env.ENCRYPTION_KEY!, 'salt', 32); function encrypt(text: string): { encrypted: string; iv: string; tag: string } { const iv = crypto.randomBytes(16); const cipher = crypto.createCipheriv(ALGORITHM, KEY, iv); let encrypted = cipher.update(text, 'utf8', 'hex'); encrypted += cipher.final('hex'); return { encrypted, iv: iv.toString('hex'), tag: cipher.getAuthTag().toString('hex'), };} function decrypt(encrypted: string, iv: string, tag: string): string { const decipher = crypto.createDecipheriv(ALGORITHM, KEY, Buffer.from(iv, 'hex')); decipher.setAuthTag(Buffer.from(tag, 'hex')); let decrypted = decipher.update(encrypted, 'hex', 'utf8'); decrypted += decipher.final('utf8'); return decrypted;}``` ### Secure Headers```typescriptimport helmet from 'helmet'; app.use(helmet());app.use(helmet.hsts({ maxAge: 31536000, includeSubDomains: true }));app.use(helmet.contentSecurityPolicy({ directives: { defaultSrc: ["'self'"], scriptSrc: ["'self'", "'strict-dynamic'"], styleSrc: ["'self'", "'unsafe-inline'"], imgSrc: ["'self'", 'data:', 'https:'], connectSrc: ["'self'"], fontSrc: ["'self'"], objectSrc: ["'none'"], frameAncestors: ["'none'"], },}));``` ## A03: Injection ### SQL Injection Prevention```typescript// ❌ BAD: String concatenationconst query = `SELECT * FROM users WHERE email = '${email}'`; // ✅ GOOD: Parameterized queries// With Prismaconst user = await prisma.user.findUnique({ where: { email } }); // With raw SQL (parameterized)const user = await db.query('SELECT * FROM users WHERE email = $1', [email]); // With Knexconst user = await knex('users').where({ email }).first();``` ### NoSQL Injection Prevention```typescript// ❌ BAD: Direct user input in queryconst user = await User.findOne({ username: req.body.username });// Attack: { "username": { "$gt": "" } } returns first user // ✅ GOOD: Validate input typeimport { z } from 'zod'; const loginSchema = z.object({ username: z.string().min(3).max(50), password: z.string().min(8),}); app.post('/login', async (req, res) => { const { username, password } = loginSchema.parse(req.body); const user = await User.findOne({ username: String(username) }); // ...});``` ### Command Injection Prevention```typescriptimport { execFile } from 'child_process'; // ❌ BAD: Shell injectionexec(`convert ${userInput} output.png`); // userInput: "; rm -rf /" // ✅ GOOD: Use execFile with array argsexecFile('convert', [userInput, 'output.png'], (error, stdout) => { // Safe - arguments are not shell-interpreted}); // ✅ GOOD: Validate and sanitizeconst allowedFormats = ['png', 'jpg', 'gif'];if (!allowedFormats.includes(format)) { throw new Error('Invalid format');}``` ## A04: Insecure Design ### Rate Limiting```typescriptimport rateLimit from 'express-rate-limit'; // General rate limitconst limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // 100 requests per window standardHeaders: true, legacyHeaders: false,}); // Strict limit for auth endpointsconst authLimiter = rateLimit({ windowMs: 60 * 60 * 1000, // 1 hour max: 5, // 5 failed attempts skipSuccessfulRequests: true,}); app.use('/api/', limiter);app.use('/api/auth/', authLimiter);``` ### Input Validation```typescriptimport { z } from 'zod'; const userSchema = z.object({ email: z.string().email(), password: z.string() .min(8) .regex(/[A-Z]/, 'Must contain uppercase') .regex(/[a-z]/, 'Must contain lowercase') .regex(/[0-9]/, 'Must contain number') .regex(/[^A-Za-z0-9]/, 'Must contain special character'), age: z.number().int().min(13).max(120), role: z.enum(['user', 'admin']).default('user'),}); app.post('/api/users', async (req, res) => { try { const data = userSchema.parse(req.body); // Validated data is safe to use } catch (error) { if (error instanceof z.ZodError) { return res.status(400).json({ errors: error.errors }); } throw error; }});``` ## A05: Security Misconfiguration ### Environment Configuration```typescript// ✅ Never expose stack traces in productionapp.use((err: Error, req: Request, res: Response, next: NextFunction) => { console.error(err.stack); // Log for debugging res.status(500).json({ error: process.env.NODE_ENV === 'production' ? 'Internal server error' : err.message, });}); // ✅ Disable sensitive headersapp.disable('x-powered-by'); // ✅ Secure cookie configurationapp.use(session({ secret: process.env.SESSION_SECRET!, cookie: { secure: process.env.NODE_ENV === 'production', httpOnly: true, sameSite: 'strict', maxAge: 24 * 60 * 60 * 1000, // 24 hours }, resave: false, saveUninitialized: false,}));``` ## A06: Vulnerable Components ### Dependency Scanning```bash# Check for vulnerabilitiesnpm auditnpm audit fix # Use Snyk for deeper scanningnpx snyk testnpx snyk monitor # Keep dependencies updatednpx npm-check-updates -u``` ```json// package.json - Use exact versions or ranges{ "dependencies": { "express": "^4.18.0", // Minor updates OK "lodash": "4.17.21" // Exact version }, "overrides": { "vulnerable-package": "^2.0.0" // Force safe version }}``` ## A07: Authentication Failures ### Secure Session Management```typescriptimport jwt from 'jsonwebtoken'; // ✅ JWT with short expiry + refresh tokensfunction generateTokens(userId: string) { const accessToken = jwt.sign( { userId }, process.env.JWT_SECRET!, { expiresIn: '15m' } // Short-lived ); const refreshToken = jwt.sign( { userId, type: 'refresh' }, process.env.JWT_REFRESH_SECRET!, { expiresIn: '7d' } ); return { accessToken, refreshToken };} // ✅ Secure password resetasync function initiatePasswordReset(email: string) { const user = await db.users.findByEmail(email); if (!user) return; // Don't reveal if email exists const token = crypto.randomBytes(32).toString('hex'); const hashedToken = crypto.createHash('sha256').update(token).digest('hex'); await db.passwordResets.create({ userId: user.id, token: hashedToken, expiresAt: new Date(Date.now() + 60 * 60 * 1000), // 1 hour }); await sendEmail(email, `Reset link: /reset?token=${token}`);}``` ### Multi-Factor Authentication```typescriptimport { authenticator } from 'otplib';import QRCode from 'qrcode'; // Setup TOTPasync function setupMFA(userId: string) { const secret = authenticator.generateSecret(); const otpauth = authenticator.keyuri(userId, 'MyApp', secret); const qrCode = await QRCode.toDataURL(otpauth); await db.users.update(userId, { mfaSecret: encrypt(secret) }); return { qrCode, secret };} // Verify TOTPfunction verifyMFA(token: string, secret: string): boolean { return authenticator.verify({ token, secret });}``` ## A08: XSS Prevention ```typescript// ✅ React auto-escapes by defaultconst UserProfile = ({ user }) => ( <div>{user.name}</div> // Safe - auto-escaped); // ⚠️ Dangerous - avoid if possible<div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} /> // ✅ Sanitize HTML if neededimport DOMPurify from 'dompurify'; const sanitizedHtml = DOMPurify.sanitize(userHtml, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'], ALLOWED_ATTR: ['href'],}); // ✅ Content Security Policyapp.use(helmet.contentSecurityPolicy({ directives: { scriptSrc: ["'self'"], // No inline scripts styleSrc: ["'self'", "'unsafe-inline'"], },}));``` ## A09: Logging & Monitoring ```typescriptimport winston from 'winston'; const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new winston.transports.File({ filename: 'error.log', level: 'error' }), new winston.transports.File({ filename: 'combined.log' }), ],}); // ✅ Log security eventsfunction logSecurityEvent(event: string, details: object) { logger.warn({ type: 'security', event, ...details, timestamp: new Date().toISOString(), });} // UsagelogSecurityEvent('failed_login', { email, ip: req.ip, userAgent: req.headers['user-agent'] });logSecurityEvent('access_denied', { userId, resource, action });logSecurityEvent('suspicious_activity', { userId, pattern: 'rapid_requests' });``` ## A10: SSRF Prevention ```typescriptimport { URL } from 'url'; // ✅ Validate URLs against allowlistconst ALLOWED_HOSTS = ['api.example.com', 'cdn.example.com']; function isAllowedUrl(urlString: string): boolean { try { const url = new URL(urlString); // Block private IPs const privatePatterns = [ /^localhost$/i, /^127\./, /^10\./, /^172\.(1[6-9]|2[0-9]|3[01])\./, /^192\.168\./, /^0\./, /^169\.254\./, // Link-local ]; if (privatePatterns.some(p => p.test(url.hostname))) { return false; } // Check allowlist return ALLOWED_HOSTS.includes(url.hostname); } catch { return false; }} app.post('/api/fetch-url', async (req, res) => { const { url } = req.body; if (!isAllowedUrl(url)) { return res.status(400).json({ error: 'URL not allowed' }); } const response = await fetch(url); // ...});``` ## Security Checklist ```markdown## Pre-Deployment Checklist ### Authentication- [ ] Passwords hashed with bcrypt (cost ≥ 12)- [ ] JWT tokens have short expiry- [ ] Session cookies are httpOnly, secure, sameSite- [ ] Rate limiting on auth endpoints ### Authorization- [ ] All endpoints have auth checks- [ ] RBAC implemented correctly- [ ] No IDOR vulnerabilities ### Input/Output- [ ] All input validated with Zod/Joi- [ ] SQL queries parameterized- [ ] XSS prevented (CSP, escaping)- [ ] File uploads validated and sandboxed ### Infrastructure- [ ] HTTPS enforced- [ ] Security headers configured- [ ] Dependencies audited- [ ] Secrets in environment variables ### Monitoring- [ ] Security events logged- [ ] Error monitoring enabled- [ ] Alerts configured``` ## Resources - **OWASP Top 10**: https://owasp.org/Top10/- **OWASP Cheat Sheets**: https://cheatsheetseries.owasp.org/- **Node.js Security**: https://nodejs.org/en/docs/guides/security/- **Snyk**: https://snyk.io/