This guide helps you migrate existing insecure JavaScript code to secure patterns step-by-step.
- Before You Start
- Input Validation Migration
- XSS Prevention Migration
- Eval Alternatives Migration
- Testing After Migration
- Performance Considerations
- Backup your codebase
- Set up version control (git)
- Install Node.js 14+ for testing
- Review OWASP Top 10 vulnerabilities
Prioritize migration based on:
- Critical: User authentication, payment processing, admin functions
- High: User profile management, data display, file uploads
- Medium: Read-only pages, static content with user input
- Low: Internal tools, logged actions only
Search your codebase for:
// ❌ Insecure patterns to find
function processUser(data) {
return data; // No validation
}
app.post('/api/user', (req, res) => {
const userData = req.body; // Direct use without validation
});// ✅ Secure replacement
const { validateInput } = require('./security-patterns/input-validation');
function processUser(data) {
const validation = validateInput(data.username, {
type: 'string',
minLength: 3,
maxLength: 50,
pattern: /^[a-zA-Z0-9_]+$/
});
if (!validation.isValid) {
throw new Error(`Invalid username: ${validation.errors.join(', ')}`);
}
return validation.sanitized;
}
app.post('/api/user', (req, res) => {
try {
const safeUsername = processUser(req.body);
// Proceed with safe data
res.json({ success: true });
} catch (error) {
res.status(400).json({ error: error.message });
}
});// Before: No type checking
function calculateTotal(price, quantity) {
return price * quantity;
}
// After: Strict type validation
function calculateTotal(price, quantity) {
if (typeof price !== 'number' || typeof quantity !== 'number') {
throw new TypeError('Price and quantity must be numbers');
}
if (price < 0 || quantity < 0) {
throw new RangeError('Values must be positive');
}
if (!Number.isFinite(price) || !Number.isFinite(quantity)) {
throw new Error('Values must be finite numbers');
}
return price * quantity;
}// Email validation
const emailValidation = validateInput(email, {
type: 'email',
maxLength: 254
});
// URL validation
const urlValidation = validateInput(url, {
type: 'url',
allowedProtocols: ['https']
});
// Phone validation
const phoneValidation = validateInput(phone, {
type: 'string',
pattern: /^\+?[1-9]\d{1,14}$/
});
// Date validation
const dateValidation = validateInput(dateStr, {
type: 'date',
minDate: '2020-01-01',
maxDate: '2030-12-31'
});# Search for dangerous patterns
grep -r "innerHTML" src/
grep -r "document.write" src/
grep -r "eval" src/// ❌ Before: Direct innerHTML
element.innerHTML = userContent;
// ✅ After: Sanitized content
const { sanitizeHTML } = require('./security-patterns/xss-prevention');
element.innerHTML = sanitizeHTML(userContent, {
allowBasicFormatting: true,
allowLinks: true
});// ❌ Before: String concatenation
function renderComment(comment) {
return `
<div class="comment">
<h3>${comment.author}</h3>
<p>${comment.text}</p>
<a href="${comment.website}">Website</a>
</div>
`;
}
// ✅ After: Escaped and validated
const { escapeHTML, validateURL } = require('./security-patterns/xss-prevention');
function renderComment(comment) {
const urlValidation = validateURL(comment.website);
const safeURL = urlValidation.isValid ? urlValidation.sanitizedURL : '#';
return `
<div class="comment">
<h3>${escapeHTML(comment.author)}</h3>
<p>${escapeHTML(comment.text)}</p>
<a href="${safeURL}">Website</a>
</div>
`;
}// ❌ Before: Inline event handlers
button.setAttribute('onclick', userProvidedCode);
// ✅ After: addEventListener
button.addEventListener('click', () => {
// Controlled code execution
handleUserAction();
});// Express.js example
const { CSP } = require('./security-patterns/xss-prevention');
app.use((req, res, next) => {
CSP.setHeader(res, {
allowInlineStyles: false,
allowInlineScripts: false,
allowEval: false,
trustedDomains: ['https://cdn.example.com']
});
next();
});// ❌ Before: Dangerous eval
function calculate(expression) {
return eval(expression);
}
// ✅ After: Secure calculator
const { safeCalculator } = require('./security-patterns/eval-alternatives');
function calculate(expression) {
const result = safeCalculator(expression);
if (result.error) {
throw new Error(result.error);
}
return result.result;
}// ❌ Before: eval-based property access
function getNestedValue(obj, path) {
return eval(`obj.${path}`);
}
// ✅ After: Safe property access
const { safeJSONPath } = require('./security-patterns/eval-alternatives');
function getNestedValue(obj, path) {
const result = safeJSONPath(obj, path);
if (!result.success) {
return undefined;
}
return result.value;
}// ❌ Before: eval in templates
function processTemplate(template, data) {
return template.replace(/\{\{(.+?)\}\}/g, (match, code) => {
return eval(code);
});
}
// ✅ After: Safe template
const { safeTemplate } = require('./security-patterns/eval-alternatives');
function processTemplate(template, data) {
return safeTemplate(template, data);
}// ❌ Before: String-based timeout
setTimeout("doSomething()", 1000);
// ✅ After: Function-based timeout
setTimeout(() => doSomething(), 1000);// ❌ Before: eval-based config
function parseConfig(configString) {
return eval(`(${configString})`);
}
// ✅ After: Safe JSON parsing
const { safeJSONParse } = require('./security-patterns/xss-prevention');
function parseConfig(configString) {
const result = safeJSONParse(configString);
if (!result.success) {
throw new Error(`Config parse error: ${result.error}`);
}
return result.data;
}// test/security-regression.js
const assert = require('assert');
const { sanitizeHTML, validateInput, secureEval } = require('../index');
describe('Security Regression Tests', () => {
it('should block script injection', () => {
const result = sanitizeHTML('<script>alert("XSS")</script>');
assert(!result.includes('<script>'), 'Script tags should be removed');
});
it('should reject invalid input', () => {
const result = validateInput('<script>', { type: 'string', maxLength: 100 });
assert(!result.isValid, 'Should reject malicious input');
});
it('should prevent code injection in eval', () => {
const result = secureEval('alert("XSS")');
assert(!result.success, 'Should block dangerous code');
});
});- Test with malicious payloads from OWASP XSS cheat sheet
- Verify error messages don't leak sensitive information
- Test with various character encodings (UTF-8, UTF-16)
- Check performance with large inputs (DoS prevention)
- Validate CORS and CSP headers are set correctly
- Test on multiple browsers (Chrome, Firefox, Safari, Edge)
Use tools like:
- OWASP ZAP: Automated security scanner
- Burp Suite: Manual testing and fuzzing
- npm audit: Check dependencies for vulnerabilities
# Run security audit
npm audit
npm audit fix
# Check for known vulnerabilities
npx snyk testconst { performance } = require('perf_hooks');
// Benchmark insecure version
const start1 = performance.now();
for (let i = 0; i < 10000; i++) {
insecureFunction(testData);
}
const insecureTime = performance.now() - start1;
// Benchmark secure version
const start2 = performance.now();
for (let i = 0; i < 10000; i++) {
secureFunction(testData);
}
const secureTime = performance.now() - start2;
console.log(`Overhead: ${((secureTime - insecureTime) / insecureTime * 100).toFixed(2)}%`);| Pattern | Overhead | Acceptable For |
|---|---|---|
| Input Validation | 0.1-0.5ms | All use cases |
| HTML Sanitization | 0.2-1ms | User-generated content |
| Secure Eval | 0.05-0.2ms | Configuration, calculators |
| URL Validation | 0.1-0.3ms | Link processing |
- Cache validation results for repeated inputs
- Validate once at entry points rather than everywhere
- Use lazy validation for non-critical paths
- Batch operations when processing multiple items
// Example: Cached validation
const validationCache = new Map();
function validateWithCache(input, rules) {
const cacheKey = `${input}_${JSON.stringify(rules)}`;
if (validationCache.has(cacheKey)) {
return validationCache.get(cacheKey);
}
const result = validateInput(input, rules);
validationCache.set(cacheKey, result);
return result;
}// ❌ Too aggressive
function displayUsername(name) {
return sanitizeHTML(name); // Removes all formatting
}
// ✅ Appropriate sanitization
function displayUsername(name) {
return escapeHTML(name); // Just escapes entities
}// ❌ Only client-side validation
// Client: validates email format
// Server: trusts client input ← VULNERABLE
// ✅ Always validate server-side
// Client: validates for UX
// Server: validates for security ← SECURETest with:
- Empty strings
- Null/undefined
- Very long inputs (> 10MB)
- Unicode characters
- Control characters
- Encoded payloads
If issues arise after migration:
- Keep old code in git (don't delete, comment out)
- Deploy to staging first
- Use feature flags for gradual rollout
- Monitor error rates closely
- Have rollback script ready
// Feature flag example
const USE_SECURE_VALIDATION = process.env.SECURE_MODE === 'true';
function processInput(data) {
if (USE_SECURE_VALIDATION) {
return secureValidate(data);
} else {
return legacyValidate(data);
}
}- Issues: https://github.com/Tryboy869/js-security-patterns/issues
- Email: nexusstudio100@gmail.com
- OWASP Resources: https://owasp.org/www-project-web-security-testing-guide/
- All user inputs validated
- innerHTML replaced with sanitized alternatives
- eval() removed or replaced with secure alternatives
- Event handlers use addEventListener
- CSP headers implemented
- Automated security tests passing
- Performance benchmarks acceptable
- Code review completed
- Staging deployment successful
- Production monitoring in place
Last updated: September 30, 2025
Maintained by: Nexus Studio