Loading...
Loading...
Provides comprehensive security review capability for TypeScript and Node.js applications, validates code against XSS, injection, CSRF, JWT/OAuth2 flaws, dependency CVEs, and secrets exposure. Use when performing security audits, before deployment, reviewing authentication/authorization implementations, or ensuring OWASP compliance for Express, NestJS, and Next.js. Triggers on "security review", "check for security issues", "TypeScript security audit".
npx skill4agent add giuseppe-trisciuoglio/developer-kit typescript-security-reviewtypescript-security-expertgrepevalexecinnerHTMLexecspawndangerouslySetInnerHTML.envnpm auditpackage-lock.json// ❌ Critical: Weak JWT configuration
import jwt from 'jsonwebtoken';
const SECRET = 'mysecret123'; // Hardcoded weak secret
function generateToken(user: User) {
return jwt.sign({ id: user.id, role: user.role }, SECRET);
// Missing expiration, weak secret, no algorithm specification
}
function verifyToken(token: string) {
return jwt.verify(token, SECRET); // No algorithm restriction
}
// ✅ Secure: Proper JWT configuration
import jwt from 'jsonwebtoken';
import { randomBytes } from 'crypto';
const JWT_SECRET = process.env.JWT_SECRET;
if (!JWT_SECRET || JWT_SECRET.length < 32) {
throw new Error('JWT_SECRET must be set and at least 32 characters');
}
function generateToken(user: User): string {
return jwt.sign(
{ sub: user.id }, // Minimal claims, no sensitive data
JWT_SECRET,
{
algorithm: 'HS256',
expiresIn: '15m',
issuer: 'my-app',
audience: 'my-app-client',
}
);
}
function verifyToken(token: string): JwtPayload {
return jwt.verify(token, JWT_SECRET, {
algorithms: ['HS256'], // Restrict accepted algorithms
issuer: 'my-app',
audience: 'my-app-client',
}) as JwtPayload;
}// ❌ Critical: SQL injection vulnerability
async function findUser(email: string) {
const result = await db.query(
`SELECT * FROM users WHERE email = '${email}'`
);
return result.rows[0];
}
// ✅ Secure: Parameterized query
async function findUser(email: string) {
const result = await db.query(
'SELECT id, name, email FROM users WHERE email = $1',
[email]
);
return result.rows[0];
}
// ✅ Secure: ORM with type-safe queries (Drizzle example)
async function findUser(email: string) {
return db.select({
id: users.id,
name: users.name,
email: users.email,
})
.from(users)
.where(eq(users.email, email))
.limit(1);
}// ❌ High: Missing input validation
app.post('/api/users', async (req, res) => {
const user = await createUser(req.body);
res.json(user);
});
// ✅ Secure: Comprehensive input validation with Zod
import { z } from 'zod';
const createUserSchema = z.object({
name: z.string().min(1).max(100).trim(),
email: z.string().email().max(254).toLowerCase(),
password: z.string()
.min(12, 'Password must be at least 12 characters')
.regex(/[A-Z]/, 'Must contain uppercase letter')
.regex(/[a-z]/, 'Must contain lowercase letter')
.regex(/[0-9]/, 'Must contain a number'),
role: z.enum(['user', 'editor']).default('user'),
});
app.post('/api/users', async (req, res) => {
const result = createUserSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ errors: result.error.flatten() });
}
const user = await createUser(result.data);
res.status(201).json(user);
});// ❌ High: XSS vulnerability through dangerouslySetInnerHTML
function Comment({ content }: { content: string }) {
return <div dangerouslySetInnerHTML={{ __html: content }} />;
}
// ✅ Secure: Sanitize HTML before rendering
import DOMPurify from 'isomorphic-dompurify';
function Comment({ content }: { content: string }) {
const sanitized = DOMPurify.sanitize(content, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],
ALLOWED_ATTR: ['href', 'target', 'rel'],
});
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
}
// ✅ Better: Use a markdown renderer instead of raw HTML
import ReactMarkdown from 'react-markdown';
function Comment({ content }: { content: string }) {
return <ReactMarkdown>{content}</ReactMarkdown>;
}// ❌ Medium: Missing security headers and permissive CORS
const app = express();
app.use(cors()); // Allows all origins
// ✅ Secure: Comprehensive security configuration
import helmet from 'helmet';
import cors from 'cors';
import rateLimit from 'express-rate-limit';
const app = express();
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", 'data:', 'https:'],
},
},
hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
}));
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(',') ?? [],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
}));
app.use(rateLimit({
windowMs: 15 * 60 * 1000,
max: 100,
standardHeaders: true,
legacyHeaders: false,
}));HttpOnlySecureSameSite=Strictnpm auditreferences/references/owasp-typescript.mdreferences/common-vulnerabilities.mdreferences/dependency-security.md