UNPKG

@sun-asterisk/sunlint

Version:

☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards

230 lines (229 loc) 6.67 kB
{ "rule": { "id": "S022", "name": "Escape data properly based on output context", "description": "Ensure data is properly escaped or sanitized based on the output context (HTML, JavaScript, CSS, URL) to prevent Cross-Site Scripting (XSS) attacks. Different contexts require different escaping methods.", "category": "security", "severity": "error", "languages": ["typescript", "javascript"], "frameworks": ["express", "nestjs", "react", "vue", "angular", "node"], "version": "1.0.0", "status": "stable", "tags": ["security", "xss", "escaping", "sanitization", "owasp", "injection"], "references": [ "https://owasp.org/www-community/attacks/xss/", "https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html", "https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html", "https://portswigger.net/web-security/cross-site-scripting", "https://cwe.mitre.org/data/definitions/79.html" ] }, "configuration": { "contexts": { "html": { "dangerousMethods": [ "innerHTML", "outerHTML", "insertAdjacentHTML", "document.write", "document.writeln", "v-html", "dangerouslySetInnerHTML" ], "requireEscaping": true, "severity": "error" }, "javascript": { "dangerousMethods": [ "eval", "Function", "setTimeout", "setInterval", "execScript" ], "requireEscaping": true, "severity": "error" }, "url": { "dangerousMethods": [ "location.href", "window.location", "location.assign", "location.replace", "window.open" ], "requireValidation": true, "severity": "warning" }, "attribute": { "dangerousAttributes": [ "onclick", "onload", "onerror", "onmouseover", "href", "src" ], "requireEscaping": true, "severity": "error" } }, "userInputSources": [ "req.body", "req.query", "req.params", "request.body", "request.query", "request.params", "localStorage", "sessionStorage", "window.location", "location.search", "location.hash", "URLSearchParams", "document.cookie", "window.name", "postMessage" ], "safeEscapingFunctions": [ "escape", "escapeHtml", "sanitize", "DOMPurify.sanitize", "textContent", "innerText", "setAttribute", "encodeURIComponent", "encodeURI", "validator.escape", "xss.filterXSS" ], "safeFrameworkMethods": { "react": [ "textContent", "children", "{variable}" ], "vue": [ "{{ variable }}", "v-text" ], "angular": [ "{{ variable }}", "[textContent]" ] } }, "examples": { "violations": [ { "description": "Unescaped user input in innerHTML", "code": "element.innerHTML = userInput;", "context": "html" }, { "description": "User input in eval without validation", "code": "eval(req.query.code);", "context": "javascript" }, { "description": "Unvalidated URL from user input", "code": "window.location = req.query.redirect;", "context": "url" }, { "description": "React dangerouslySetInnerHTML without sanitization", "code": "<div dangerouslySetInnerHTML={{__html: userInput}} />", "context": "html" }, { "description": "Vue v-html directive with user input", "code": "<div v-html=\"userInput\"></div>", "context": "html" }, { "description": "Dynamic event handler with user input", "code": "element.setAttribute('onclick', userInput);", "context": "attribute" } ], "fixes": [ { "description": "Use textContent instead of innerHTML", "code": "element.textContent = userInput;", "context": "html" }, { "description": "Sanitize HTML with DOMPurify", "code": "element.innerHTML = DOMPurify.sanitize(userInput);", "context": "html" }, { "description": "Never use eval with user input", "code": "// Avoid eval entirely, use JSON.parse or safe alternatives", "context": "javascript" }, { "description": "Validate and whitelist URLs", "code": "const allowedHosts = ['example.com'];\nconst url = new URL(req.query.redirect);\nif (allowedHosts.includes(url.hostname)) {\n window.location = url.href;\n}", "context": "url" }, { "description": "React with proper sanitization", "code": "<div dangerouslySetInnerHTML={{__html: DOMPurify.sanitize(userInput)}} />", "context": "html" }, { "description": "Vue using v-text for safe output", "code": "<div v-text=\"userInput\"></div>", "context": "html" } ] }, "testing": { "testCases": [ { "name": "innerHTML_with_user_input", "type": "violation", "description": "Using innerHTML with unsanitized user input" }, { "name": "eval_with_user_input", "type": "violation", "description": "Using eval with user input" }, { "name": "location_with_user_input", "type": "violation", "description": "Setting location with unvalidated user input" }, { "name": "textContent_safe_output", "type": "clean", "description": "Using textContent for safe output" }, { "name": "dompurify_sanitization", "type": "clean", "description": "Using DOMPurify to sanitize HTML" }, { "name": "url_validation", "type": "clean", "description": "Validating URLs before redirect" } ] }, "performance": { "complexity": "O(n)", "description": "Linear complexity based on number of DOM manipulation and output operations in the source code" }, "owaspMapping": { "category": "A03:2021 – Injection", "subcategories": [ "A07:2021 – Identification and Authentication Failures" ], "cweId": "CWE-79", "description": "Validates that all output is properly escaped or sanitized based on context to prevent XSS attacks" } }