ACTIVATE when conducting security assessments, code review for vulnerabilities, or CVSS scoring in TypeScript/Node.js/NestJS projects. ACTIVATE for 'security audit', 'vulnerability', 'injection', 'XSS', 'OWASP'. Covers: SQL injection prevention (Drizzle), XSS prevention, JWT security, password hashing, command/path injection, secrets management, CSRF, security checklist. DO NOT use for: general code quality, performance optimization, authentication flow design.
From typescriptnpx claudepluginhub fabiensalles/claude-marketplace --plugin typescriptThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Implements structured self-debugging workflow for AI agent failures: capture errors, diagnose patterns like loops or context overflow, apply contained recoveries, and generate introspection reports.
Security audit patterns for TypeScript/Node.js/NestJS projects.
Always use Drizzle parameterized queries. Never interpolate user input into SQL.
// ❌ AVOID - String interpolation in SQL
const result = await db.execute(
`SELECT * FROM tenants WHERE email = '${email}'`
);
// ✅ CORRECT - Drizzle parameterized query
const result = await db
.select()
.from(tenants)
.where(eq(tenants.email, email));
// ✅ CORRECT - Raw SQL with placeholder
import { sql } from 'drizzle-orm';
const result = await db.execute(
sql`SELECT * FROM tenants WHERE email = ${email}`
);
// ✅ React/JSX auto-escapes by default — safe
return <p>{userInput}</p>;
// ❌ AVOID - Bypasses auto-escaping
return <div dangerouslySetInnerHTML={{ __html: userInput }} />;
// ✅ CORRECT - If HTML rendering needed, sanitize first
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userInput);
return <div dangerouslySetInnerHTML={{ __html: clean }} />;
// ❌ AVOID - No expiration, weak secret
const token = jwt.sign(payload, 'secret');
// ✅ CORRECT - Strong secret, expiration, algorithm specified
const token = jwt.sign(payload, process.env.JWT_SECRET, {
expiresIn: '1h',
algorithm: 'HS256',
});
// ✅ CORRECT - Validate with explicit algorithms
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
});
import * as bcrypt from 'bcrypt';
// ✅ CORRECT - bcrypt with sufficient rounds
const SALT_ROUNDS = 12;
const hash = await bcrypt.hash(password, SALT_ROUNDS);
const isValid = await bcrypt.compare(password, hash);
// ❌ AVOID - MD5, SHA-1, SHA-256 without salt
import { createHash } from 'crypto';
const hash = createHash('sha256').update(password).digest('hex');
// ❌ AVOID - Shell injection via child_process.exec
// exec(`convert ${userInput} output.png`);
// exec() spawns a shell, allowing injection via metacharacters
// ✅ CORRECT - execFile prevents shell injection (no shell involved)
import { execFile } from 'child_process';
execFile('convert', [userInput, 'output.png'], (error, stdout) => {
// Arguments are passed as array, not interpolated into a shell command
});
import { resolve, normalize } from 'path';
// ❌ AVOID - User input directly in path
const filePath = `./uploads/${req.params.filename}`;
// ✅ CORRECT - Normalize and verify containment
const UPLOAD_DIR = resolve('./uploads');
const requested = resolve(UPLOAD_DIR, req.params.filename);
if (!requested.startsWith(UPLOAD_DIR)) {
throw new ForbiddenException('Path traversal detected');
}
// ❌ AVOID - Secrets hardcoded
const API_KEY = 'sk-1234567890abcdef';
// ✅ CORRECT - From environment, validated at startup
import { z } from 'zod';
const EnvSchema = z.object({
JWT_SECRET: z.string().min(32),
DATABASE_URL: z.string().url(),
API_KEY: z.string().min(16),
});
const env = EnvSchema.parse(process.env);
// ✅ NestJS - Use CSRF middleware for state-changing endpoints
// For API-only (JWT auth): CSRF is not needed (no cookies)
// For session-based auth: enable CSRF tokens
// ✅ SameSite cookies
response.cookie('session', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
});
# Dependency audit
pnpm audit
# ESLint security rules
pnpm add -D eslint-plugin-security
# Snyk scanning
npx snyk test
dangerouslySetInnerHTML without DOMPurifyexecFile instead of exec for system commandshttpOnly, secure, sameSite: 'strict' on cookiespnpm audit in CI pipeline| Vulnerability | Mitigation |
|---|---|
| SQL Injection | Drizzle queries, sql template tag |
| XSS | JSX auto-escaping, DOMPurify if needed |
| JWT flaws | Expiration, strong secret, explicit algorithm |
| Weak passwords | bcrypt 12+ rounds |
| Command injection | execFile (no shell) |
| Path traversal | resolve + startsWith check |
| Secrets in code | Env vars + Zod validation |
| CSRF | SameSite cookies, CSRF tokens for sessions |