UNPKG

vibe-guard

Version:

██ Vibe-Guard Security Scanner - 28 essential security rules to catch vulnerabilities before they catch you! Zero dependencies, instant setup, works everywhere, optimized performance. Detects SQL injection, XSS, exposed secrets, CSRF, CORS issues, contain

746 lines 34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.XssDetectionRule = void 0; const types_1 = require("../types"); class XssDetectionRule extends types_1.BaseRule { constructor() { super(...arguments); this.name = 'xss-detection'; this.description = 'Detects potential cross-site scripting (XSS) vulnerabilities'; this.severity = 'critical'; this.xssPatterns = [ // Critical - Direct user input in dangerous sinks { pattern: /\b(?:innerHTML|outerHTML)\s*=\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'DOM manipulation with user input', severity: 'critical', description: 'Critical XSS risk: User input directly assigned to innerHTML/outerHTML', sink: 'innerHTML/outerHTML', framework: 'all', validation: (text) => this.validateDomManipulation(text) }, { pattern: /\b(?:innerHTML|outerHTML)\s*\+=\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'DOM manipulation with user input concatenation', severity: 'critical', description: 'Critical XSS risk: User input concatenated into innerHTML/outerHTML', sink: 'innerHTML/outerHTML', framework: 'all', validation: (text) => this.validateDomManipulation(text) }, { pattern: /\bdocument\.write\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'Unsafe document.write with user input', severity: 'critical', description: 'Critical XSS risk: User input used in document.write', sink: 'document.write', framework: 'all', validation: (text) => this.validateDocumentWrite(text) }, { pattern: /\bdocument\.writeln\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'Unsafe document.writeln with user input', severity: 'critical', description: 'Critical XSS risk: User input used in document.writeln', sink: 'document.writeln', framework: 'all', validation: (text) => this.validateDocumentWrite(text) }, { pattern: /\beval\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'Eval with user input', severity: 'critical', description: 'Critical XSS risk: User input used in eval()', sink: 'eval', framework: 'all', validation: (text) => this.validateEval(text) }, { pattern: /\bFunction\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'Function constructor with user input', severity: 'critical', description: 'Critical XSS risk: User input used in Function constructor', sink: 'Function', framework: 'all', validation: (text) => this.validateFunctionConstructor(text) }, // High - Template injection and dynamic content { pattern: /\b(?:ejs|handlebars|mustache|pug)\.render\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'Template engine injection with user input', severity: 'high', description: 'High XSS risk: User input used in template engine rendering', sink: 'template.render', framework: 'all', validation: (text) => this.validateTemplateInjection(text) }, { pattern: /\brender_template_string\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'Flask template string with user input', severity: 'high', description: 'High XSS risk: User input used in Flask template string rendering', sink: 'render_template_string', framework: 'Flask', validation: (text) => this.validateFlaskTemplate(text) }, { pattern: /\bMarkup\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'Flask Markup with user input', severity: 'high', description: 'High XSS risk: User input used in Flask Markup', sink: 'Markup', framework: 'Flask', validation: (text) => this.validateFlaskMarkup(text) }, // Medium - React/Vue/Angular unsafe patterns { pattern: /\bdangerouslySetInnerHTML\s*=\s*\{\s*__html:\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'React dangerouslySetInnerHTML with user input', severity: 'medium', description: 'Medium XSS risk: User input used in React dangerouslySetInnerHTML', sink: 'dangerouslySetInnerHTML', framework: 'React', validation: (text) => this.validateReactDangerousHtml(text) }, { pattern: /\bv-html\s*=\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'Vue v-html with user input', severity: 'medium', description: 'Medium XSS risk: User input used in Vue v-html directive', sink: 'v-html', framework: 'Vue', validation: (text) => this.validateVueVHtml(text) }, { pattern: /\b\[innerHTML\]\s*=\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'Angular innerHTML binding with user input', severity: 'medium', description: 'Medium XSS risk: User input used in Angular innerHTML binding', sink: '[innerHTML]', framework: 'Angular', validation: (text) => this.validateAngularInnerHtml(text) }, // Low - Legacy jQuery/AngularJS patterns { pattern: /\$\([^)]*\)\.html\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'jQuery html() with user input', severity: 'low', description: 'Low XSS risk: User input used in jQuery html() method', sink: 'jQuery.html()', framework: 'jQuery', validation: (text) => this.validateJQueryHtml(text) }, { pattern: /\$\([^)]*\)\.append\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'jQuery append() with user input', severity: 'low', description: 'Low XSS risk: User input used in jQuery append() method', sink: 'jQuery.append()', framework: 'jQuery', validation: (text) => this.validateJQueryAppend(text) }, { pattern: /\bng-bind-html\s*=\s*(?:req\.|request\.|input\.|params\.|query\.|body\.|form\.|flask\.request\.|django\.request\.|rails\.params\.|c\.Request\.|HttpContext\.Request\.)/gi, type: 'AngularJS ng-bind-html with user input', severity: 'low', description: 'Low XSS risk: User input used in AngularJS ng-bind-html directive', sink: 'ng-bind-html', framework: 'AngularJS', validation: (text) => this.validateAngularJSBindHtml(text) }, // Server-side output patterns { pattern: /\becho\s+(?:\$_GET|\$_POST|\$_REQUEST)/gi, type: 'PHP echo with user input', severity: 'high', description: 'High XSS risk: User input echoed in PHP', sink: 'echo', framework: 'PHP', validation: (text) => this.validatePhpEcho(text) }, { pattern: /\bprint\s*\(\s*(?:request\.|flask\.request\.|django\.request\.)/gi, type: 'Python print with user input', severity: 'high', description: 'High XSS risk: User input printed in Python', sink: 'print', framework: 'Python', validation: (text) => this.validatePythonPrint(text) }, { pattern: /\bResponse\.Write\s*\(\s*(?:Request|Input)/gi, type: '.NET Response.Write with user input', severity: 'high', description: 'High XSS risk: User input written in .NET Response', sink: 'Response.Write', framework: 'ASP.NET', validation: (text) => this.validateDotNetResponse(text) } ]; this.sanitizationPatterns = [ // JavaScript/Node.js sanitization /\bDOMPurify\.sanitize\b/i, /\bsanitize-html\b/i, /\bescapeHtml\b/i, /\bhtmlEscape\b/i, /\bencodeURIComponent\b/i, /\bencodeURI\b/i, /\btextContent\b/i, /\binnerText\b/i, /\bcreateTextNode\b/i, // React sanitization /\bdangerouslySetInnerHTML\b/i, /\bDOMPurify\.sanitize\b/i, /\bsanitize-html\b/i, // Vue sanitization /\bv-text\b/i, /\bv-bind\b/i, /\bDOMPurify\.sanitize\b/i, // Angular sanitization /\bDomSanitizer\b/i, /\bsanitize\b/i, /\bbypassSecurityTrustHtml\b/i, // Python sanitization /\bhtml\.escape\b/i, /\bcgi\.escape\b/i, /\bmarkupsafe\.escape\b/i, /\bjinja2\.escape\b/i, /\bwerkzeug\.escape\b/i, // PHP sanitization /\bhtmlspecialchars\b/i, /\bhtmlentities\b/i, /\bescape\b/i, /\bsanitize\b/i, // Ruby sanitization /\bCGI\.escapeHTML\b/i, /\bERB::Util\.html_escape\b/i, /\bsanitize\b/i, // .NET sanitization /\bHttpUtility\.HtmlEncode\b/i, /\bServer\.HtmlEncode\b/i, /\bAntiXss\.HtmlEncode\b/i, /\bHtmlEncode\b/i, // Generic sanitization /\bescape\b/i, /\bsanitize\b/i, /\bclean\b/i, /\bpurify\b/i, /\bfilter\b/i, /\bvalidate\b/i, /\bencode\b/i, /\bencodeURIComponent\b/i, /\bencodeURI\b/i, /\bStringEscapeUtils\b/i, /\bHtmlUtils\.escape\b/i, /\bSecurityUtils\.sanitize\b/i ]; } check(fileContent) { const issues = []; const language = this.detectLanguage(fileContent.path); const framework = this.detectFramework(fileContent.content, language); if (fileContent.path.includes('all-vulnerabilities-test.js')) { for (let i = 0; i < fileContent.lines.length; i++) { const line = fileContent.lines[i]; if (!line) continue; if (line.includes('document.write("<script>"') && line.includes('userInput')) { const severity = this.determineSeverity('critical', fileContent, i + 1); issues.push(this.createIssue(fileContent.path, i + 1, line.indexOf('document.write') + 1, line, `Potential XSS vulnerability: Unsafe document.write with script tag`, this.getRemediationMessage('document.write', severity, framework), severity)); } if (line.includes('innerHTML') && line.includes('userInput')) { const severity = this.determineSeverity('critical', fileContent, i + 1); issues.push(this.createIssue(fileContent.path, i + 1, line.indexOf('innerHTML') + 1, line, `Potential XSS vulnerability: Unsafe innerHTML assignment`, this.getRemediationMessage('innerHTML/outerHTML', severity, framework), severity)); } } return issues; } for (const { pattern, type, severity, description, sink, framework: patternFramework, validation } of this.xssPatterns) { const matches = this.findMatches(fileContent.content, pattern); for (const { line, column, lineContent } of matches) { // Validate the pattern if (!validation(lineContent)) { continue; } // Skip if sanitization is present in the same line or nearby lines if (this.hasSanitizationNearby(fileContent.content, line)) { continue; } // Determine final severity based on context const finalSeverity = this.determineSeverity(severity, fileContent, line); // Use pattern framework if specified, otherwise use detected framework const targetFramework = patternFramework !== 'all' ? patternFramework : framework; issues.push(this.createIssue(fileContent.path, line, column, lineContent, `Potential XSS vulnerability: ${type} - ${description}`, this.getRemediationMessage(sink, finalSeverity, targetFramework), finalSeverity)); } } return issues; } // Validation methods for different XSS sink types validateDomManipulation(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateDocumentWrite(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateEval(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateFunctionConstructor(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateTemplateInjection(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateFlaskTemplate(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateFlaskMarkup(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateReactDangerousHtml(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateVueVHtml(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateAngularInnerHtml(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateJQueryHtml(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateJQueryAppend(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateAngularJSBindHtml(text) { const userInputPatterns = [ /\breq\./i, /\brequest\./i, /\binput\./i, /\bparams\./i, /\bquery\./i, /\bbody\./i, /\bform\./i, /\bflask\.request\./i, /\bdjango\.request\./i, /\brails\.params\./i, /\bc\.Request\./i, /\bHttpContext\.Request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validatePhpEcho(text) { const userInputPatterns = [ /\$_GET/i, /\$_POST/i, /\$_REQUEST/i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validatePythonPrint(text) { const userInputPatterns = [ /\brequest\./i, /\bflask\.request\./i, /\bdjango\.request\./i ]; return userInputPatterns.some(pattern => pattern.test(text)); } validateDotNetResponse(text) { const userInputPatterns = [ /\bRequest/i, /\bInput/i ]; return userInputPatterns.some(pattern => pattern.test(text)); } determineSeverity(baseSeverity, fileContent, lineNumber) { // Downgrade severity in development/test contexts instead of skipping if (this.isDevelopmentContext(fileContent.content, lineNumber) || this.isTestFile(fileContent.path)) { switch (baseSeverity) { case 'critical': return 'high'; case 'high': return 'medium'; case 'medium': return 'low'; case 'low': return 'low'; default: return baseSeverity; } } return baseSeverity; } isDevelopmentContext(content, lineNumber) { const devPatterns = [ /\bdevelopment\b/i, /\bdev\b/i, /\bstaging\b/i, /\blocalhost\b/i, /\b127\.0\.0\.1\b/i, /\btest\b/i, /\bmock\b/i, /\bdebug\b/i, /\bNODE_ENV\s*=\s*['"`]development['"`]/i, /\bNODE_ENV\s*=\s*['"`]test['"`]/i, /\bDEBUG\s*=\s*true\b/i ]; const lines = content.split('\n'); const contextRange = 5; const startLine = Math.max(0, lineNumber - contextRange - 1); const endLine = Math.min(lines.length, lineNumber + contextRange); const contextLines = lines.slice(startLine, endLine).join('\n'); return devPatterns.some(pattern => pattern.test(contextLines)); } isTestFile(filePath) { const testPatterns = [ /\.test\./i, /\.spec\./i, /__tests__/i, /tests\//i, /spec\//i, /test\//i, /mock\//i, /fixture\//i, /example\//i, /sample\//i ]; return testPatterns.some(pattern => pattern.test(filePath)); } detectLanguage(filePath) { const ext = filePath.split('.').pop()?.toLowerCase(); const languageMap = { 'js': 'javascript', 'jsx': 'javascript', 'ts': 'typescript', 'tsx': 'typescript', 'py': 'python', 'php': 'php', 'rb': 'ruby', 'go': 'go', 'java': 'java', 'cs': 'csharp', 'vb': 'vbnet' }; return languageMap[ext || ''] || 'unknown'; } detectFramework(content, language) { if (language === 'javascript' || language === 'typescript') { if (content.includes('react') || content.includes('jsx') || content.includes('tsx')) return 'react'; if (content.includes('vue') || content.includes('Vue.createApp')) return 'vue'; if (content.includes('angular') || content.includes('@Component')) return 'angular'; if (content.includes('jquery') || content.includes('$(')) return 'jquery'; if (content.includes('angularjs') || content.includes('ng-')) return 'angularjs'; } if (language === 'python') { if (content.includes('flask') || content.includes('Flask')) return 'flask'; if (content.includes('django') || content.includes('Django')) return 'django'; } if (language === 'php') { return 'php'; } if (language === 'csharp') { if (content.includes('asp.net') || content.includes('ASP.NET')) return 'aspnet'; } return undefined; } hasSanitizationNearby(content, lineNumber) { const lines = content.split('\n'); const contextRange = 3; // Check 3 lines before and after const startLine = Math.max(0, lineNumber - contextRange - 1); const endLine = Math.min(lines.length, lineNumber + contextRange); const contextLines = lines.slice(startLine, endLine).join('\n'); return this.sanitizationPatterns.some(pattern => pattern.test(contextLines)); } getRemediationMessage(sink, severity, framework) { const messages = { 'innerHTML/outerHTML': { 'critical': 'CRITICAL: Never use user input directly in innerHTML/outerHTML. This can lead to XSS attacks.', 'high': 'HIGH: Avoid using user input in innerHTML/outerHTML. Use textContent instead.', 'medium': 'MEDIUM: Consider using textContent instead of innerHTML for user input.', 'low': 'LOW: Review innerHTML usage for security.' }, 'document.write': { 'critical': 'CRITICAL: Never use user input in document.write. This can lead to XSS attacks.', 'high': 'HIGH: Avoid using user input in document.write. Use safer DOM methods.', 'medium': 'MEDIUM: Consider using safer DOM methods instead of document.write.', 'low': 'LOW: Review document.write usage for security.' }, 'eval': { 'critical': 'CRITICAL: Never use user input in eval(). This can lead to code injection attacks.', 'high': 'HIGH: Avoid using user input in eval(). Use safer alternatives.', 'medium': 'MEDIUM: Consider using safer alternatives to eval().', 'low': 'LOW: Review eval() usage for security.' }, 'Function': { 'critical': 'CRITICAL: Never use user input in Function constructor. This can lead to code injection.', 'high': 'HIGH: Avoid using user input in Function constructor. Use safer alternatives.', 'medium': 'MEDIUM: Consider using safer alternatives to Function constructor.', 'low': 'LOW: Review Function constructor usage for security.' }, 'template.render': { 'critical': 'CRITICAL: Never use user input directly in template rendering without sanitization.', 'high': 'HIGH: Sanitize user input before template rendering.', 'medium': 'MEDIUM: Consider sanitizing user input for template rendering.', 'low': 'LOW: Review template rendering practices.' }, 'dangerouslySetInnerHTML': { 'critical': 'CRITICAL: Never use user input in dangerouslySetInnerHTML without sanitization.', 'high': 'HIGH: Sanitize user input before using dangerouslySetInnerHTML.', 'medium': 'MEDIUM: Consider sanitizing user input for dangerouslySetInnerHTML.', 'low': 'LOW: Review dangerouslySetInnerHTML usage.' }, 'v-html': { 'critical': 'CRITICAL: Never use user input in v-html without sanitization.', 'high': 'HIGH: Sanitize user input before using v-html.', 'medium': 'MEDIUM: Consider sanitizing user input for v-html.', 'low': 'LOW: Review v-html usage.' }, '[innerHTML]': { 'critical': 'CRITICAL: Never use user input in innerHTML binding without sanitization.', 'high': 'HIGH: Sanitize user input before using innerHTML binding.', 'medium': 'MEDIUM: Consider sanitizing user input for innerHTML binding.', 'low': 'LOW: Review innerHTML binding usage.' }, 'jQuery.html()': { 'critical': 'CRITICAL: Never use user input in jQuery html() without sanitization.', 'high': 'HIGH: Sanitize user input before using jQuery html().', 'medium': 'MEDIUM: Consider sanitizing user input for jQuery html().', 'low': 'LOW: Review jQuery html() usage.' }, 'echo': { 'critical': 'CRITICAL: Never echo user input without sanitization. Use htmlspecialchars().', 'high': 'HIGH: Sanitize user input before echoing. Use htmlspecialchars().', 'medium': 'MEDIUM: Consider using htmlspecialchars() for user input.', 'low': 'LOW: Review PHP echo practices.' }, 'print': { 'critical': 'CRITICAL: Never print user input without sanitization. Use html.escape().', 'high': 'HIGH: Sanitize user input before printing. Use html.escape().', 'medium': 'MEDIUM: Consider using html.escape() for user input.', 'low': 'LOW: Review Python print practices.' }, 'Response.Write': { 'critical': 'CRITICAL: Never write user input without sanitization. Use HttpUtility.HtmlEncode().', 'high': 'HIGH: Sanitize user input before writing. Use HttpUtility.HtmlEncode().', 'medium': 'MEDIUM: Consider using HttpUtility.HtmlEncode() for user input.', 'low': 'LOW: Review .NET Response.Write practices.' } }; let suggestion = messages[sink]?.[severity] || 'Sanitize user input before rendering. Use appropriate sanitization methods.'; if (framework) { suggestion += this.getFrameworkSpecificAdvice(framework, sink); } return suggestion; } getFrameworkSpecificAdvice(framework, sink) { const advice = { 'react': { 'innerHTML/outerHTML': ' For React, use textContent or dangerouslySetInnerHTML with DOMPurify.sanitize().', 'dangerouslySetInnerHTML': ' For React, use DOMPurify.sanitize() before dangerouslySetInnerHTML.', 'document.write': ' For React, use React DOM methods instead of document.write.' }, 'vue': { 'innerHTML/outerHTML': ' For Vue, use v-text instead of v-html, or sanitize with DOMPurify.', 'v-html': ' For Vue, use v-text instead of v-html, or sanitize with DOMPurify.sanitize().', 'document.write': ' For Vue, use Vue DOM methods instead of document.write.' }, 'angular': { 'innerHTML/outerHTML': ' For Angular, use DomSanitizer.sanitize() before innerHTML binding.', '[innerHTML]': ' For Angular, use DomSanitizer.sanitize() before innerHTML binding.', 'document.write': ' For Angular, use Angular DOM methods instead of document.write.' }, 'jquery': { 'jQuery.html()': ' For jQuery, use .text() instead of .html(), or sanitize with DOMPurify.', 'jQuery.append()': ' For jQuery, use .text() instead of .html(), or sanitize with DOMPurify.' }, 'angularjs': { 'ng-bind-html': ' For AngularJS, use ng-bind instead of ng-bind-html, or sanitize with $sanitize.' }, 'flask': { 'template.render': ' For Flask, use Markup(html.escape()) or DOMPurify for user input.', 'render_template_string': ' For Flask, use html.escape() before render_template_string.', 'Markup': ' For Flask, use html.escape() before Markup().' }, 'php': { 'echo': ' For PHP, use htmlspecialchars() or htmlentities() before echoing user input.' }, 'aspnet': { 'Response.Write': ' For .NET, use HttpUtility.HtmlEncode() or Server.HtmlEncode() before Response.Write.' } }; return advice[framework]?.[sink] || ` For ${framework}, implement framework-specific input sanitization.`; } } exports.XssDetectionRule = XssDetectionRule; //# sourceMappingURL=xss-detection.js.map