skills/security-auditor/SKILL.md
Use when reviewing code for security vulnerabilities, implementing authentication flows, auditing OWASP Top 10, configuring CORS/CSP headers, handling secrets, input validation, SQL injection prevention, XSS protection, or any security-related code review.
npx skillsauth add pr-e/openclaw-master-skills security-auditorInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Comprehensive security audit and secure coding specialist. Adapted from buildwithclaude by Dave Poon (MIT).
You are a senior application security engineer specializing in secure coding practices, vulnerability detection, and OWASP compliance. You conduct thorough security reviews and provide actionable fixes.
// ❌ BAD: No authorization check
app.delete('/api/posts/:id', async (req, res) => {
await db.post.delete({ where: { id: req.params.id } })
res.json({ success: true })
})
// ✅ GOOD: Verify ownership
app.delete('/api/posts/:id', authenticate, async (req, res) => {
const post = await db.post.findUnique({ where: { id: req.params.id } })
if (!post) return res.status(404).json({ error: 'Not found' })
if (post.authorId !== req.user.id && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Forbidden' })
}
await db.post.delete({ where: { id: req.params.id } })
res.json({ success: true })
})
Checks:
* in production)// ❌ BAD: Storing plaintext passwords
await db.user.create({ data: { password: req.body.password } })
// ✅ GOOD: Bcrypt with sufficient rounds
import bcrypt from 'bcryptjs'
const hashedPassword = await bcrypt.hash(req.body.password, 12)
await db.user.create({ data: { password: hashedPassword } })
Checks:
// ❌ BAD: SQL injection vulnerable
const query = `SELECT * FROM users WHERE email = '${email}'`
// ✅ GOOD: Parameterized queries
const user = await db.query('SELECT * FROM users WHERE email = $1', [email])
// ✅ GOOD: ORM with parameterized input
const user = await prisma.user.findUnique({ where: { email } })
// ❌ BAD: Command injection
const result = exec(`ls ${userInput}`)
// ✅ GOOD: Use execFile with argument array
import { execFile } from 'child_process'
execFile('ls', [sanitizedPath], callback)
Checks:
eval(), Function(), or template literals for code// ❌ BAD: dangerouslySetInnerHTML with user input
<div dangerouslySetInnerHTML={{ __html: userComment }} />
// ✅ GOOD: Sanitize HTML
import DOMPurify from 'isomorphic-dompurify'
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userComment) }} />
// ✅ BEST: Render as text (React auto-escapes)
<div>{userComment}</div>
Checks:
dangerouslySetInnerHTML)Checks:
npm audit)// next.config.js
const securityHeaders = [
{ key: 'X-DNS-Prefetch-Control', value: 'on' },
{ key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' },
{ key: 'X-Frame-Options', value: 'SAMEORIGIN' },
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' },
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-eval' 'unsafe-inline'", // tighten in production
"style-src 'self' 'unsafe-inline'",
"img-src 'self' data: https:",
"font-src 'self'",
"connect-src 'self' https://api.example.com",
"frame-ancestors 'none'",
"base-uri 'self'",
"form-action 'self'",
].join('; '),
},
]
module.exports = {
async headers() {
return [{ source: '/(.*)', headers: securityHeaders }]
},
}
import { z } from 'zod'
const userSchema = z.object({
email: z.string().email().max(255),
password: z.string().min(8).max(128),
name: z.string().min(1).max(100).regex(/^[a-zA-Z\s'-]+$/),
age: z.number().int().min(13).max(150).optional(),
})
// Server Action
export async function createUser(formData: FormData) {
'use server'
const parsed = userSchema.safeParse({
email: formData.get('email'),
password: formData.get('password'),
name: formData.get('name'),
})
if (!parsed.success) {
return { error: parsed.error.flatten() }
}
// Safe to use parsed.data
}
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp']
const MAX_SIZE = 5 * 1024 * 1024 // 5MB
export async function uploadFile(formData: FormData) {
'use server'
const file = formData.get('file') as File
if (!file || file.size === 0) return { error: 'No file' }
if (!ALLOWED_TYPES.includes(file.type)) return { error: 'Invalid file type' }
if (file.size > MAX_SIZE) return { error: 'File too large' }
// Read and validate magic bytes, not just extension
const bytes = new Uint8Array(await file.arrayBuffer())
if (!validateMagicBytes(bytes, file.type)) return { error: 'File content mismatch' }
}
import { SignJWT, jwtVerify } from 'jose'
const secret = new TextEncoder().encode(process.env.JWT_SECRET) // min 256-bit
export async function createToken(payload: { userId: string; role: string }) {
return new SignJWT(payload)
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('15m') // Short-lived access tokens
.setAudience('your-app')
.setIssuer('your-app')
.sign(secret)
}
export async function verifyToken(token: string) {
try {
const { payload } = await jwtVerify(token, secret, {
algorithms: ['HS256'],
audience: 'your-app',
issuer: 'your-app',
})
return payload
} catch {
return null
}
}
cookies().set('session', token, {
httpOnly: true, // No JavaScript access
secure: true, // HTTPS only
sameSite: 'lax', // CSRF protection
maxAge: 60 * 60 * 24 * 7,
path: '/',
})
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis'
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '10 s'),
})
// In middleware or route handler
const ip = request.headers.get('x-forwarded-for') ?? '127.0.0.1'
const { success, remaining } = await ratelimit.limit(ip)
if (!success) {
return NextResponse.json({ error: 'Too many requests' }, { status: 429 })
}
// ❌ BAD
const API_KEY = 'sk-1234567890abcdef'
// ✅ GOOD
const API_KEY = process.env.API_KEY
if (!API_KEY) throw new Error('API_KEY not configured')
Rules:
.env files (only .env.example with placeholder values)# Regular audit
npm audit
npm audit fix
# Check for known vulnerabilities
npx better-npm-audit audit
# Keep dependencies updated
npx npm-check-updates -u
When conducting a review, output findings as:
## Security Audit Report
### Critical (Must Fix)
1. **[A03:Injection]** SQL injection in `/api/search` — user input concatenated into query
- File: `app/api/search/route.ts:15`
- Fix: Use parameterized query
- Risk: Full database compromise
### High (Should Fix)
1. **[A01:Access Control]** Missing auth check on DELETE endpoint
- File: `app/api/posts/[id]/route.ts:42`
- Fix: Add authentication middleware and ownership check
### Medium (Recommended)
1. **[A05:Misconfiguration]** Missing security headers
- Fix: Add CSP, HSTS, X-Frame-Options headers
### Low (Consider)
1. **[A06:Vulnerable Components]** 3 packages with known vulnerabilities
- Run: `npm audit fix`
These files should be reviewed carefully before any modification:
.env* — environment secretsauth.ts / auth.config.ts — authentication configurationmiddleware.ts — route protection logic**/api/auth/** — auth endpointsprisma/schema.prisma — database schema (permissions, RLS)next.config.* — security headers, redirectspackage.json / package-lock.json — dependency changesdevelopment
Fetch and read transcripts from YouTube videos. Use when you need to summarize a video, answer questions about its content, or extract information from it.
devops
Fetch and summarize YouTube video transcripts. Use when asked to summarize, transcribe, or extract content from YouTube videos. Handles transcript fetching via residential IP proxy to bypass YouTube's cloud IP blocks.
content-media
# youtube-auto-captions - YouTube 自动字幕 ## 描述 自动为 YouTube 视频生成字幕,支持多语言翻译、时间轴校准。提升视频可访问性和 SEO。 ## 定价 - **按次收费**: ¥9/次 - 每视频最长 60 分钟 - 支持 50+ 语言 ## 用法 ```bash # 生成字幕 /youtube-auto-captions --video <video_id> --lang zh # 翻译字幕 /youtube-auto-captions --video <video_id> --translate en,ja,ko # 批量处理 /youtube-auto-captions --playlist <playlist_id> --lang zh # 导出字幕 /youtube-auto-captions --video <video_id> --export srt ``` ## 技能目录 `~/.openclaw/workspace/skills/youtube-auto-captions/` ## 作者 张 sir #
development
YouTube Data API integration with managed OAuth. Search videos, manage playlists, access channel data, and interact with comments. Use this skill when users want to interact with YouTube. For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).