
Typescript Security Review
Have your coding agent scan TypeScript and Node.js code for injection, ReDoS, and auth mistakes before you merge or deploy.
Overview
Typescript-security-review is an agent skill most often used in Ship (also Build, Operate) that reviews TypeScript/Node.js code for injection, command execution, and ReDoS patterns with concrete fixes.
Install
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill typescript-security-reviewWhat is this skill?
- Flags SQL injection from template literals and shows parameterized query fixes
- Covers NoSQL injection on MongoDB login flows with Zod-style input validation
- Recommends execFile over shell exec to block command injection paths
- Documents ReDoS-prone regex patterns common in email and path validators
- Pairs vulnerable snippets with copy-paste remediations for Node/TS stacks
Adoption & trust: 1k installs on skills.sh; 271 GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are shipping Node or TypeScript services but lack a repeatable checklist for injection and unsafe-process bugs the agent keeps reintroducing.
Who is it for?
Solo builders maintaining Express/Nest/Node APIs who want agent-guided security fixes without a full SAST pipeline.
Skip if: Teams that need licensed SAST, dependency CVE dashboards, or non-TypeScript stacks—this skill is pattern docs, not a scanner binary.
When should I use this skill?
Reviewing or writing TypeScript/Node backend code that touches databases, shells, or user-supplied strings.
What do I get? / Deliverables
You get prioritized vulnerability callouts with remediated code patterns you can apply in the same PR or review thread.
- Inline vulnerability findings with before/after code snippets
- PR review comments aligned to injection and ReDoS categories
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Security review belongs on the Ship shelf because it gates release quality, even though you can run it anytime in Build or Operate. Appsec patterns map to the security subphase: injection classes, unsafe exec, and weak validation checks.
Where it fits
Before merging a new search endpoint, have the agent verify queries use bound parameters instead of template literals.
Run a pre-release pass on auth and file-upload handlers for command injection and ReDoS regexes.
After a bug report on login, re-check the route for operator-injection in MongoDB findOne filters.
How it compares
Use as a focused TypeScript appsec checklist in chat, not as a replacement for Semgrep or npm audit in CI.
Common Questions / FAQ
Who is typescript-security-review for?
Indie and solo backend developers using TypeScript or Node who want their agent to spot classic web and API vulnerabilities during coding or review.
When should I use typescript-security-review?
During Build when adding auth or DB queries, in Ship before release, and in Operate when patching reported abuse paths—the skill targets injection and unsafe exec in TS/Node.
Is typescript-security-review safe to install?
It is instructional pattern content for the agent; review the Security Audits panel on this Prism page before trusting any third-party skill package in your environment.
SKILL.md
READMESKILL.md - Typescript Security Review
# Common TypeScript/Node.js Vulnerability Patterns ## Injection Vulnerabilities ### SQL Injection via Template Literals ```typescript // ❌ 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) ```typescript // ❌ 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 ```typescript // ❌ 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) ```typescript // ❌ 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 ```tsx // ❌ Vulnerable <div dangerouslySetInnerHTML={{ __html: userContent }} /> // ✅ Fix: Sanitize with DOMPurify import DOMPurify from 'isomorphic-dompurify'; <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userContent) }} /> ``` ### Server-Side Template Injection ```typescript // ❌ 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 ```typescript // ❌ 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 ```typescript // ❌ 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 ```typescript // ❌ 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 hour ``` ## Path Traversal ```typescript // ❌ 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.