Security of TypeScript Applications: Top 10 Vulnerabilities

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Безопасность TypeScript приложений: топ-10 уязвимостей



TypeScript, as a superset of JavaScript, inherits many of its vulnerabilities while adding specific risks related to the type system and compilation. Even strict typing does not guarantee security — logic errors and unsafe practices can lead to serious consequences. In this article, we will analyze the top 10 vulnerabilities of TypeScript applications, provide code examples, and give recommendations for protection.



1. Cross-Site Scripting (XSS) via dangerouslySetInnerHTML

One of the most common vulnerabilities in React/TypeScript applications is inserting unescaped HTML via dangerouslySetInnerHTML.



// ❌ Vulnerable codefunction UserComment({ comment }: { comment: string }) {  return <div dangerouslySetInnerHTML={{ __html: comment }} />;}

// ✅ Safe optionimport DOMPurify from 'dompurify';

function SafeComment({ comment }: { comment: string }) { const sanitized = DOMPurify.sanitize(comment); return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;}


2. SQL injection via unescaped queries

Using template strings in SQL queries with user input is a direct path to injection.



// ❌ Vulnerable codeasync function getUser(id: string) {  const query = `SELECT * FROM users WHERE id = '${id}'`;  return db.execute(query);}

// ✅ Safe option (using parameterized queries)async function safeGetUser(id: string) { const query = 'SELECT * FROM users WHERE id = $1'; return db.execute(query, [id]);}


3. Prototype Pollution via deep object merging

TypeScript allows easy copying of objects but does not check the prototype chain.



// ❌ Vulnerable codefunction merge(target: any, source: any) {  for (const key in source) {    if (typeof source[key] === 'object') {      target[key] = merge(target[key] || {}, source[key]);    } else {      target[key] = source[key];    }  }  return target;}

// ✅ Safe optionfunction safeMerge(target: Record<string, any>, source: Record<string, any>) { const result = { ...target }; for (const key of Object.keys(source)) { if (key === '__proto__' || key === 'constructor') continue; if (typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = safeMerge(result[key] || {}, source[key]); } else { result[key] = source[key]; } } return result;}


4. NoSQL injection (MongoDB)

When working with MongoDB, passing an object directly from a request can lead to injection of $where or $regex operators.



// ❌ Vulnerable codeapp.post('/login', async (req, res) => {  const { username, password } = req.body;  const user = await User.findOne({ username, password });  // If you pass { "$gt": "" }, you can bypass the check});

// ✅ Safe optionimport { sanitize } from 'mongo-sanitize';

app.post('/login', async (req, res) => { const cleanBody = sanitize(req.body); const user = await User.findOne({ username: cleanBody.username, password: cleanBody.password });});


5. OS commands via child_process

Executing user input via exec without escaping is a critical vulnerability.



// ❌ Vulnerable codeimport { exec } from 'child_process';

function runCommand(input: string) { exec(`echo ${input}`, (err, stdout) => { console.log(stdout); });}

// ✅ Safe optionimport { execFile } from 'child_process';

function safeCommand(input: string) { // Use execFile with an array of arguments execFile('echo', [input], (err, stdout) => { console.log(stdout); });}


6. Path Traversal when working with files

Blogs

Book Recommendations