
Typescript Security Review
- 1.6k installs
- 311 repo stars
- Updated June 22, 2026
- giuseppe-trisciuoglio/developer-kit
typescript-security-review is an agent skill that provides security review capability for typescript/node.js applications, validates code against xss, injection, csrf, jwt/oauth2 flaws, dependency cves, and secrets expos
About
typescript-security-review is an agent skill from giuseppe-trisciuoglio/developer-kit that provides security review capability for typescript/node.js applications, validates code against xss, injection, csrf, jwt/oauth2 flaws, dependency cves, and secrets exposure. use when performing secur. # TypeScript Security Review ## Overview Security review for TypeScript/Node.js applications. Evaluates code against OWASP Top 10, framework-specific patterns, and production-readiness criteria. Findings are classified by severity (Critical, High, Medium, Low) with remediation examples. Delegates to the `typescript-security-expert` agent for deep Developers invoke typescript-security-review during build/backend work for backend & apis tasks. The skill documents triggers, prerequisites, and step-by-step workflows grounded in SKILL.md. Compatible with Claude Code, Cursor, and Codex agent runtimes that load marketplace skills. Review the Security Audits panel on this listing before installing in production environments.
- TypeScript Security Review
- Performing security audits on TypeScript/Node.js codebases
- Reviewing authentication and authorization implementations (JWT, OAuth2, Passport.js)
- Checking for common vulnerabilities (XSS, injection, CSRF, path traversal)
- Validating input validation and sanitization logic
Typescript Security Review by the numbers
- 1,609 all-time installs (skills.sh)
- +65 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #296 of 4,386 Backend & APIs skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
typescript-security-review capabilities & compatibility
- Capabilities
- typescript security review · performing security audits on typescript/node.js · reviewing authentication and authorization imple · checking for common vulnerabilities (xss, inject · validating input validation and sanitization log
- Use cases
- orchestration
What typescript-security-review says it does
- Performing security audits on TypeScript/Node.js codebases
- Reviewing authentication and authorization implementations (JWT, OAuth2, Passport.js)
- Checking for common vulnerabilities (XSS, injection, CSRF, path traversal)
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill typescript-security-reviewAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.6k |
|---|---|
| repo stars | ★ 311 |
| Security audit | 2 / 3 scanners passed |
| Last updated | June 22, 2026 |
| Repository | giuseppe-trisciuoglio/developer-kit ↗ |
What it does
Provides security review capability for TypeScript/Node.js applications, validates code against XSS, injection, CSRF, JWT/OAuth2 flaws, dependency CVEs, and secrets exposure. Use when performing secur
Who is it for?
Developers working on backend & apis during build tasks.
Skip if: Tasks outside Backend & APIs scope described in SKILL.md.
When should I use this skill?
Provides security review capability for TypeScript/Node.js applications, validates code against XSS, injection, CSRF, JWT/OAuth2 flaws, dependency CVEs, and secrets exposure. Use when performing secur
What you get
Completed backend & apis workflow aligned with SKILL.md steps.
- vulnerability findings
- patched code patterns
- security remediation notes
Files
TypeScript Security Review
Overview
Security review for TypeScript/Node.js applications. Evaluates code against OWASP Top 10, framework-specific patterns, and production-readiness criteria. Findings are classified by severity (Critical, High, Medium, Low) with remediation examples. Delegates to the typescript-security-expert agent for deep analysis.
When to Use
- Performing security audits on TypeScript/Node.js codebases
- Reviewing authentication and authorization implementations (JWT, OAuth2, Passport.js)
- Checking for common vulnerabilities (XSS, injection, CSRF, path traversal)
- Validating input validation and sanitization logic
- Reviewing dependency security (npm audit, known CVEs)
- Checking secrets management and environment variable handling
- Assessing API security (rate limiting, CORS, security headers)
- Reviewing Express, NestJS, or Next.js security configurations
- Before deploying to production or after significant code changes
- Compliance checks (GDPR, HIPAA, SOC2 data handling requirements)
Instructions
1. Identify Scope: Determine which files and modules are under review. Prioritize authentication, authorization, data handling, API endpoints, and configuration files. Use grep to find security-sensitive patterns (eval, exec, innerHTML, password handling, JWT operations).
Checkpoint: Verify at least 3 security-sensitive files/modules identified before proceeding.
2. Check Authentication & Authorization: Review JWT implementation (signing algorithm, expiration, refresh tokens), OAuth2/OIDC integration, session management, password hashing (bcrypt/argon2), and multi-factor authentication. Verify protected routes enforce authentication.
Checkpoint: Use grep to confirm all route handlers have auth guards or middleware applied.
3. Scan for Injection Vulnerabilities: Check for SQL/NoSQL injection in database queries, command injection in exec/spawn, template injection, and LDAP injection. Verify parameterized queries and input validation.
Checkpoint: Use grep to confirm all database queries use parameterization — no string concatenation with user input.
4. Review Input Validation: Check API inputs validated with Zod, Joi, or class-validator. Verify schema completeness — proper type constraints, length limits, format validation. Check for validation bypass paths.
Checkpoint: Verify all public API endpoints have corresponding validation schemas.
5. Assess XSS Prevention: Review React components for dangerouslySetInnerHTML usage, check Content Security Policy headers, verify HTML sanitization for user-generated content. See references/xss-prevention.md for detailed patterns.
Checkpoint: Use grep to confirm any dangerouslySetInnerHTML usage has sanitization via DOMPurify or equivalent.
6. Check Secrets Management: Scan for hardcoded credentials, API keys, secrets in source code. Verify .env files are gitignored, secrets accessed through proper management services.
Checkpoint: Run grep -r "password\|secret\|api.*key\|token" --include="*.ts" to identify potential secrets in code.
7. Review Dependency Security: Run npm audit or check package-lock.json for known vulnerabilities. Identify outdated dependencies with CVEs. Check for unnecessary dependencies.
Checkpoint: Verify npm audit results are reviewed and critical vulnerabilities addressed.
8. Evaluate Security Headers & Configuration: Check helmet.js or manual security header configuration. Review CORS policy, rate limiting, HTTPS enforcement, cookie security flags (HttpOnly, Secure, SameSite), and CSP. See references/security-headers.md for configuration examples.
Checkpoint: Use grep to confirm helmet or equivalent security headers are applied globally.
9. Produce Security Report: Generate structured report with severity-classified findings, remediation guidance with code examples, and security posture summary.
Feedback Loop: If Critical or High vulnerabilities found, re-scan related modules for similar patterns before finalizing. Use grep to identify if the same vulnerability pattern exists elsewhere.
Examples
JWT Security Review
// ❌ 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
}
// ✅ Secure: Proper JWT configuration
import jwt from 'jsonwebtoken';
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;
}SQL Injection Prevention
// ❌ 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);
}See references/xss-prevention.md for XSS patterns and references/security-headers.md for security headers configuration.
Review Output Format
Structure all security review findings as follows:
1. Security Posture Summary
Overall security assessment score (1-10) with key observations and risk level.
2. Critical Vulnerabilities (Immediate Action)
Issues that can be exploited to compromise the system, steal data, or cause unauthorized access.
3. High Priority (Address Within 30 Days)
Security misconfigurations, missing protections, or vulnerabilities requiring near-term remediation.
4. Medium Priority (Address Within 90 Days)
Issues that reduce security posture but have mitigating factors or limited exploitability.
5. Low Priority (Next Cycle)
Security improvements, hardening recommendations, and defense-in-depth enhancements.
6. Positive Security Observations
Well-implemented security patterns and practices to acknowledge.
7. Remediation Roadmap
Prioritized action items with code examples for the most critical fixes.
Best Practices
- Validate all inputs at the API boundary — never trust client-side validation alone
- Use parameterized queries or ORMs — never concatenate user input into queries
- Store secrets in environment variables or secret managers — never in source code
- Apply the principle of least privilege for database accounts, API keys, and IAM roles
- Enable security headers (helmet.js) and restrict CORS to known origins
- Implement rate limiting on all public-facing endpoints
- Hash passwords with bcrypt or argon2 — never use MD5/SHA for passwords
- Set cookie flags:
HttpOnly,Secure,SameSite=Strict - Use
npm auditin CI pipelines to catch dependency vulnerabilities - Log security events (failed logins, permission denials) without logging sensitive data
Constraints and Warnings
- Security review is not a substitute for professional penetration testing
- Focus on code-level vulnerabilities — infrastructure security is out of scope
- Respect the project's framework — provide framework-specific remediation guidance
- Do not log, print, or expose discovered secrets — report their location only
- Dependency vulnerabilities should be assessed for actual exploitability, not just presence
- Security recommendations must be practical — consider implementation effort vs risk reduction
References
See the references/ directory for detailed security documentation:
references/owasp-typescript.md— OWASP Top 10 mapped to TypeScript/Node.js patternsreferences/common-vulnerabilities.md— Common vulnerability patterns and remediationreferences/dependency-security.md— Dependency scanning and supply chain securityreferences/xss-prevention.md— XSS prevention patterns for React and server-sidereferences/security-headers.md— Security headers and CORS configuration examplesreferences/input-validation.md— Input validation patterns with Zod and class-validator
Common TypeScript/Node.js Vulnerability Patterns
Injection Vulnerabilities
SQL Injection via Template Literals
// ❌ Vulnerable
async function searchUsers(name: string) {
return db.query(`SELECT * FROM users WHERE name LIKE '%${name}%'`);
}
// ✅ Fix: Parameterized query
async function searchUsers(name: string) {
return db.query('SELECT * FROM users WHERE name LIKE $1', [`%${name}%`]);
}NoSQL Injection (MongoDB)
// ❌ Vulnerable: User input as query operator
app.post('/login', async (req, res) => {
const user = await User.findOne({
email: req.body.email,
password: req.body.password, // Could be { $ne: '' }
});
});
// ✅ Fix: Validate and cast input types
app.post('/login', async (req, res) => {
const { email, password } = loginSchema.parse(req.body); // Zod ensures strings
const user = await User.findOne({ email });
if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
return res.status(401).json({ error: 'Invalid credentials' });
}
});Command Injection
// ❌ Vulnerable: Shell command with user input
import { exec } from 'child_process';
exec(`convert ${filename} output.png`);
// ✅ Fix: Use execFile (no shell interpretation)
import { execFile } from 'child_process';
execFile('convert', [filename, 'output.png']);Regex Denial of Service (ReDoS)
// ❌ Vulnerable: Catastrophic backtracking
const emailRegex = /^([a-zA-Z0-9]+)*@([a-zA-Z0-9]+)*\.([a-zA-Z]+)$/;
// ✅ Fix: Use non-backtracking patterns or Zod
import { z } from 'zod';
const emailSchema = z.string().email();Cross-Site Scripting (XSS)
React dangerouslySetInnerHTML
// ❌ Vulnerable
<div dangerouslySetInnerHTML={{ __html: userContent }} />
// ✅ Fix: Sanitize with DOMPurify
import DOMPurify from 'isomorphic-dompurify';
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userContent) }} />Server-Side Template Injection
// ❌ Vulnerable: String interpolation in HTML response
app.get('/greeting', (req, res) => {
res.send(`<h1>Hello ${req.query.name}</h1>`);
});
// ✅ Fix: Escape HTML entities
import { escape } from 'html-escaper';
app.get('/greeting', (req, res) => {
res.send(`<h1>Hello ${escape(req.query.name as string)}</h1>`);
});Authentication Vulnerabilities
Timing Attack on Comparison
// ❌ Vulnerable: Early return leaks timing information
function verifyApiKey(provided: string, stored: string): boolean {
return provided === stored; // Timing attack possible
}
// ✅ Fix: Constant-time comparison
import { timingSafeEqual } from 'crypto';
function verifyApiKey(provided: string, stored: string): boolean {
const a = Buffer.from(provided);
const b = Buffer.from(stored);
if (a.length !== b.length) return false;
return timingSafeEqual(a, b);
}JWT Algorithm Confusion
// ❌ Vulnerable: Accepts any algorithm
jwt.verify(token, publicKey); // Attacker can use 'none' or switch HS/RS
// ✅ Fix: Restrict algorithms
jwt.verify(token, publicKey, {
algorithms: ['RS256'], // Only accept expected algorithm
});Insecure Password Reset
// ❌ Vulnerable: Predictable token
const resetToken = Math.random().toString(36);
// ✅ Fix: Cryptographically secure token with expiration
import { randomBytes } from 'crypto';
const resetToken = randomBytes(32).toString('hex');
const tokenHash = createHash('sha256').update(resetToken).digest('hex');
const expires = new Date(Date.now() + 3600_000); // 1 hourPath Traversal
// ❌ Vulnerable: User controls file path
app.get('/files', (req, res) => {
const filePath = path.join('/uploads', req.query.name as string);
res.sendFile(filePath);
});
// Attack: ?name=../../etc/passwd
// ✅ Fix: Validate path stays within allowed directory
import path from 'path';
app.get('/files', (req, res) => {
const basePath = path.resolve('/uploads');
const filePath = path.resolve(basePath, req.query.name as string);
if (!filePath.startsWith(basePath)) {
return res.status(400).json({ error: 'Invalid file path' });
}
res.sendFile(filePath);
});Prototype Pollution
// ❌ Vulnerable: Deep merge with user input
function deepMerge(target: any, source: any) {
for (const key in source) {
if (typeof source[key] === 'object') {
target[key] = deepMerge(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
// Attack: { "__proto__": { "isAdmin": true } }
// ✅ Fix: Block prototype keys
function safeMerge(target: Record<string, unknown>, source: Record<string, unknown>) {
for (const key of Object.keys(source)) {
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
continue;
}
if (typeof source[key] === 'object' && source[key] !== null) {
target[key] = safeMerge(
(target[key] as Record<string, unknown>) || {},
source[key] as Record<string, unknown>,
);
} else {
target[key] = source[key];
}
}
return target;
}Information Disclosure
Error Messages
// ❌ Vulnerable: Exposes internals
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
res.status(500).json({
error: err.message,
stack: err.stack, // Full stack trace
query: req.query, // Request details
});
});
// ✅ Fix: Generic error response
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
logger.error('Unhandled error', { error: err.message, stack: err.stack });
res.status(500).json({
error: 'Internal server error',
requestId: req.id,
});
});Enumeration via Timing or Response
// ❌ Vulnerable: Different responses for existing vs non-existing users
app.post('/login', async (req, res) => {
const user = await findUser(req.body.email);
if (!user) return res.status(404).json({ error: 'User not found' });
if (!await bcrypt.compare(req.body.password, user.hash)) {
return res.status(401).json({ error: 'Wrong password' });
}
});
// ✅ Fix: Same response for all failures
app.post('/login', async (req, res) => {
const user = await findUser(req.body.email);
const isValid = user && await bcrypt.compare(req.body.password, user.hash);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
});Denial of Service
Event Loop Blocking
// ❌ Vulnerable: Blocking the event loop
app.get('/hash', (req, res) => {
const hash = crypto.pbkdf2Sync(req.query.data, 'salt', 100000, 64, 'sha512');
res.json({ hash: hash.toString('hex') });
});
// ✅ Fix: Async operation
app.get('/hash', async (req, res) => {
const hash = await new Promise((resolve, reject) => {
crypto.pbkdf2(req.query.data, 'salt', 100000, 64, 'sha512', (err, key) => {
if (err) reject(err);
else resolve(key.toString('hex'));
});
});
res.json({ hash });
});Unbounded Input
// ❌ Vulnerable: No limit on request body size
app.use(express.json());
// ✅ Fix: Limit body size
app.use(express.json({ limit: '10kb' }));Dependency Security and Supply Chain Guide
npm Audit
Running Security Audits
# Check for known vulnerabilities
npm audit
# Get machine-readable output
npm audit --json
# Fix automatically where possible
npm audit fix
# Fix with major version bumps (review carefully)
npm audit fix --force
# Only show critical/high severity
npm audit --audit-level=highCI Pipeline Integration
# GitHub Actions example
- name: Security audit
run: |
npm audit --audit-level=high
if [ $? -ne 0 ]; then
echo "::error::npm audit found high/critical vulnerabilities"
exit 1
fiDependency Management Best Practices
Lock Files
Always commit package-lock.json to ensure reproducible builds and prevent supply chain attacks.
# Install from lock file only (CI environments)
npm ci
# Never use in CI:
# npm install ← Can resolve different versionsMinimal Dependencies
Every dependency is an attack surface. Review before adding:
# Check package details before installing
npm info <package> | grep -E 'description|homepage|repository|maintainers'
# Check download stats and maintenance
npx npm-check-updates --doctorQuestions Before Adding a Dependency
1. Is this actively maintained? (check last commit date, open issues) 2. How many transitive dependencies does it add? (npm ls <package>) 3. Can I implement this with the standard library instead? 4. Does the maintainer have 2FA enabled? (check npm profile) 5. Is there a more popular/trusted alternative?
Automated Dependency Scanning
GitHub Dependabot
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
reviewers:
- "security-team"
labels:
- "dependencies"
- "security"Socket.dev
Detects supply chain attacks that npm audit misses:
- Typosquatting (similar package names)
- Install scripts that execute code during
npm install - Obfuscated code
- Network access during install
- Excessive permissions
Snyk
Provides deeper vulnerability analysis and fix PRs:
# Install and authenticate
npm install -g snyk
snyk auth
# Test for vulnerabilities
snyk test
# Monitor project continuously
snyk monitorSupply Chain Attack Vectors
Typosquatting
Attackers publish packages with names similar to popular ones.
# ❌ Easy mistake
npm install expres # Not "express"
npm install lodahs # Not "lodash"
# ✅ Double-check package names
npm info express | head -5Install Script Attacks
Malicious packages run code during npm install.
# Check for install scripts before installing
npm show <package> scripts
# Audit install scripts in your dependencies
npx can-i-ignore-scriptsDependency Confusion
Attackers publish public packages with the same name as internal packages. When a package name is unclaimed on the public registry, anyone can publish malicious code under that name — never reference unclaimed package names in documentation or install commands.
Prevention:
# Example: configure .npmrc to resolve scoped packages from a private registry
# (using Microsoft's real public registry as illustration only)
registry=https://registry.npmjs.org/
@microsoft:registry=https://npm.pkg.github.com/Always verify that any package name referenced in docs or scripts is either claimed by your organization or points to an explicit local path (e.g. ./script-name).
Reviewing New Dependencies
Security Checklist for New Packages
- [ ] Popularity: >1000 weekly downloads or well-known organization
- [ ] Maintenance: Updated within last 6 months
- [ ] License: Compatible with your project (MIT, Apache-2.0, BSD)
- [ ] Dependencies: Minimal transitive dependencies
- [ ] Install scripts: No suspicious preinstall/postinstall scripts
- [ ] Source code: Repository matches published package
- [ ] Maintainers: Multiple maintainers, 2FA enabled
- [ ] TypeScript: Has type definitions (DefinitelyTyped or built-in)
- [ ] CVE history: Check for past vulnerabilities on Snyk/NVD
Evaluating Package Health
# Check package size and dependencies
npx packagephobia <package>
# Check bundle size impact
npx bundlephobia <package>
# List all transitive dependencies
npm ls <package> --all
# Check for known vulnerabilities
npx audit-ci --config audit-ci.jsonResponding to Vulnerabilities
Severity Assessment
| Severity | Response Time | Action |
|---|---|---|
| Critical | Immediate | Patch or remove dependency |
| High | Within 1 week | Upgrade to fixed version |
| Medium | Within 1 month | Plan upgrade in next sprint |
| Low | Next quarter | Include in regular maintenance |
When a Fix is Not Available
1. Check if vulnerability is exploitable in your usage context 2. Apply workarounds (disable affected feature, add input validation) 3. Fork and patch the dependency if critical 4. Replace with an alternative package 5. Accept risk with documented justification (last resort)
// Document accepted risk in code
// SECURITY: CVE-2024-XXXXX in package-x v1.2.3
// Risk accepted: We don't use the affected XML parser feature
// Review date: 2024-06-01
// Ticket: JIRA-1234Environment Variable Security
Validation at Startup
import { z } from 'zod';
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
API_KEY: z.string().min(16),
PORT: z.coerce.number().default(3000),
});
const env = envSchema.parse(process.env);
export default env;.env Files
# .gitignore — ALWAYS ignore env files
.env
.env.local
.env.*.local
# .env.example — template without secrets (DO commit this)
NODE_ENV=development
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
JWT_SECRET=<generate-with-openssl-rand-hex-32>Prevent Client Exposure
// Next.js: Only NEXT_PUBLIC_ vars are exposed to client
// ❌ Exposed to client bundle
NEXT_PUBLIC_API_KEY=secret // Don't put secrets here!
// ✅ Server-only (not in client bundle)
DATABASE_URL=postgresql://...
JWT_SECRET=...Input Validation Patterns
Zod Schema Validation
import { z } from 'zod';
// Comprehensive user creation schema
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'),
});
// API endpoint with validation
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);
});NestJS Validation with class-validator
import { IsEmail, IsString, MinLength, Matches, IsEnum } from 'class-validator';
export class CreateUserDto {
@IsString()
@MinLength(1)
@MaxLength(100)
name: string;
@IsEmail()
@MaxLength(254)
email: string;
@IsString()
@MinLength(12)
@Matches(/[A-Z]/, { message: 'Must contain uppercase letter' })
@Matches(/[a-z]/, { message: 'Must contain lowercase letter' })
@Matches(/[0-9]/, { message: 'Must contain a number' })
password: string;
@IsEnum(['user', 'editor'])
role?: 'user' | 'editor';
}Validation Error Handling
// Consistent error response format
interface ValidationError {
field: string;
message: string;
}
function handleValidation(errors: ZodError): ValidationError[] {
return errors.errors.map(err => ({
field: err.path.join('.'),
message: err.message,
}));
}
// Return structured 400 response
res.status(400).json({
error: 'Validation failed',
details: handleValidation(result.error),
});OWASP Top 10 for TypeScript/Node.js
A01: Broken Access Control
Risk
Users acting outside their intended permissions. The most common web application vulnerability.
TypeScript/Node.js Patterns
// ❌ Vulnerable: No authorization check
app.get('/api/users/:id/profile', async (req, res) => {
const profile = await db.user.findUnique({ where: { id: req.params.id } });
res.json(profile); // Any user can access any profile
});
// ✅ Secure: Proper authorization
app.get('/api/users/:id/profile', authenticate, async (req, res) => {
if (req.user.id !== req.params.id && req.user.role !== 'admin') {
return res.status(403).json({ error: 'Forbidden' });
}
const profile = await db.user.findUnique({ where: { id: req.params.id } });
res.json(profile);
});Review Checklist
- [ ] All endpoints enforce authentication
- [ ] Authorization checks verify resource ownership
- [ ] Admin endpoints restricted by role
- [ ] Direct object references validated against user permissions
- [ ] CORS restrictive (not
origin: '*') - [ ] JWT tokens validated on every request
A02: Cryptographic Failures
Risk
Exposure of sensitive data due to weak or missing encryption.
TypeScript/Node.js Patterns
// ❌ Vulnerable: MD5 for passwords, no salt
import { createHash } from 'crypto';
const hash = createHash('md5').update(password).digest('hex');
// ✅ Secure: bcrypt with proper cost factor
import bcrypt from 'bcrypt';
const hash = await bcrypt.hash(password, 12);
const isValid = await bcrypt.compare(input, hash);Review Checklist
- [ ] Passwords hashed with bcrypt or argon2 (cost factor ≥ 10)
- [ ] Sensitive data encrypted at rest (AES-256)
- [ ] TLS/HTTPS enforced for all communications
- [ ] No sensitive data in JWT payloads
- [ ] Strong random values from
crypto.randomBytes - [ ] API keys not in source code or client bundles
A03: Injection
Risk
Untrusted data sent to an interpreter as part of a command or query.
TypeScript/Node.js Patterns
// ❌ SQL Injection
const result = await db.query(`SELECT * FROM users WHERE name = '${name}'`);
// ✅ Parameterized query
const result = await db.query('SELECT * FROM users WHERE name = $1', [name]);
// ❌ Command Injection
const { exec } = require('child_process');
exec(`ls ${userInput}`); // Shell injection
// ✅ Safe subprocess
const { execFile } = require('child_process');
execFile('ls', [userInput]); // No shell interpretationReview Checklist
- [ ] All SQL queries use parameterized statements or ORM
- [ ] No
eval(),new Function(), orvm.runInNewContext()with user input - [ ]
child_process.execnot used with user input (useexecFileorspawn) - [ ] Template literals not used for SQL, shell commands, or LDAP queries
- [ ] NoSQL queries don't accept operator objects from user input
A04: Insecure Design
Review Checklist
- [ ] Business logic has rate limiting for abuse-prone operations
- [ ] Multi-step operations validate state at each step
- [ ] Error messages don't reveal system internals
- [ ] Sensitive operations require re-authentication
A05: Security Misconfiguration
TypeScript/Node.js Patterns
// ❌ Misconfigured: Debug enabled, permissive CORS
app.use(cors());
app.use(errorHandler({ showStack: true }));
// ✅ Secure configuration
app.use(cors({
origin: process.env.ALLOWED_ORIGINS?.split(','),
credentials: true,
}));
app.use(helmet());
if (process.env.NODE_ENV === 'production') {
app.set('trust proxy', 1);
}Review Checklist
- [ ] Debug mode disabled in production
- [ ] Security headers configured (helmet.js)
- [ ] Error responses don't include stack traces in production
- [ ] Default accounts/passwords removed
- [ ] Unnecessary features disabled
- [ ]
NODE_ENV=productionset in production
A06: Vulnerable and Outdated Components
Review Checklist
- [ ]
npm auditreports no critical/high vulnerabilities - [ ] Dependencies regularly updated
- [ ] Lock file (
package-lock.json) committed - [ ] No unnecessary dependencies
- [ ] Dependabot or Renovate configured
A07: Identification and Authentication Failures
TypeScript/Node.js Patterns
// ❌ Weak session management
app.use(session({
secret: 'secret',
cookie: {},
}));
// ✅ Secure session configuration
app.use(session({
secret: process.env.SESSION_SECRET!,
resave: false,
saveUninitialized: false,
cookie: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 15 * 60 * 1000, // 15 minutes
},
}));Review Checklist
- [ ] Passwords require minimum 12 characters with complexity
- [ ] Account lockout after failed attempts
- [ ] Session tokens rotated after login
- [ ] JWT expiration set (short-lived: 15 min)
- [ ] Refresh tokens stored securely (HttpOnly cookie)
- [ ] Multi-factor authentication for sensitive operations
A08: Software and Data Integrity Failures
Review Checklist
- [ ] CI/CD pipeline integrity verified
- [ ] Dependencies verified against known good hashes
- [ ] Subresource integrity (SRI) for external scripts
- [ ] Serialization/deserialization validated (no
JSON.parseof untrusted data without schema validation)
A09: Security Logging and Monitoring Failures
TypeScript/Node.js Patterns
// ✅ Security event logging
logger.warn('Authentication failed', {
ip: req.ip,
email: req.body.email, // Log identifier, NOT password
userAgent: req.headers['user-agent'],
timestamp: new Date().toISOString(),
});Review Checklist
- [ ] Failed login attempts logged with IP and user agent
- [ ] Authorization failures logged
- [ ] Sensitive data NOT included in logs (passwords, tokens, PII)
- [ ] Log injection prevented (sanitize user input in logs)
- [ ] Monitoring alerts for anomalous patterns
A10: Server-Side Request Forgery (SSRF)
TypeScript/Node.js Patterns
// ❌ SSRF vulnerable
app.get('/fetch', async (req, res) => {
const response = await fetch(req.query.url as string); // User controls URL
res.json(await response.json());
});
// ✅ SSRF prevention
const ALLOWED_DOMAINS = ['api.example.com', 'cdn.example.com'];
app.get('/fetch', async (req, res) => {
const url = new URL(req.query.url as string);
if (!ALLOWED_DOMAINS.includes(url.hostname)) {
return res.status(400).json({ error: 'Domain not allowed' });
}
if (url.protocol !== 'https:') {
return res.status(400).json({ error: 'HTTPS required' });
}
const response = await fetch(url.toString());
res.json(await response.json());
});Review Checklist
- [ ] Server-side HTTP requests don't use user-controlled URLs
- [ ] URL allowlists for external requests
- [ ] Internal network addresses blocked (127.0.0.1, 10.x, 172.16.x, 192.168.x)
- [ ] DNS rebinding prevention
Security Headers and Configuration
Express Security Configuration
Vulnerable: Missing security headers and permissive CORS
const app = express();
app.use(cors()); // Allows all originsSecure: 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,
}));NestJS Security Module
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
@Module({
imports: [
HelmetModule,
ThrottlerModule.forRoot([{
ttl: 60000,
limit: 10,
}]),
],
})
export class SecurityModule {}Cookie Security Flags
// Always set these flags on sensitive cookies
res.cookie('sessionId', sessionId, {
httpOnly: true, // Prevents JavaScript access
secure: true, // HTTPS only
sameSite: 'strict', // CSRF protection
maxAge: 3600000, // 1 hour expiration
path: '/',
});XSS Prevention Patterns
React Components
Vulnerable: dangerouslySetInnerHTML without sanitization
function Comment({ content }: { content: string }) {
return <div dangerouslySetInnerHTML={{ __html: content }} />;
}Secure: DOMPurify sanitization
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: Markdown renderer instead of raw HTML
import ReactMarkdown from 'react-markdown';
function Comment({ content }: { content: string }) {
return <ReactMarkdown>{content}</ReactMarkdown>;
}Server-Side Template Engines
// Vulnerable: Template injection
const template = `Hello ${userInput}`; // User input directly in template
// Secure: Use template engines with auto-escaping
import Handlebars from 'handlebars';
const safeTemplate = Handlebars.compile('Hello {{name}}');
const result = safeTemplate({ name: userInput }); // Auto-escapedRelated skills
Forks & variants (1)
Typescript Security Review has 1 known copy in the catalog totaling 2 installs. They canonicalize to this original listing.
- giuseppe-trisciuoglio - 2 installs
How it compares
Use typescript-security-review for Node.js/TypeScript code-level injection and ReDoS fixes, not for cloud IAM audits or mobile ASO listing checks.
FAQ
What does typescript-security-review do?
Provides security review capability for TypeScript/Node.js applications, validates code against XSS, injection, CSRF, JWT/OAuth2 flaws, dependency CVEs, and secrets exposure. Use when performing secur
When should I use typescript-security-review?
During build backend work for backend & apis.
Is typescript-security-review safe to install?
Review the Security Audits panel on this listing before production use.