UNPKG

@sun-asterisk/sunlint

Version:

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

128 lines (127 loc) 4.36 kB
{ "rule": { "id": "S029", "name": "CSRF Protection Required", "description": "Require CSRF (Cross-Site Request Forgery) protection for state-changing operations. All POST, PUT, DELETE, and PATCH endpoints must implement CSRF token validation to prevent unauthorized requests from malicious sites.", "category": "security", "severity": "error", "languages": ["typescript", "javascript"], "frameworks": ["express", "nestjs", "node"], "version": "1.0.0", "status": "stable", "tags": ["security", "csrf", "xsrf", "authentication", "owasp"], "references": [ "https://owasp.org/www-community/attacks/csrf", "https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html", "https://portswigger.net/web-security/csrf", "https://cwe.mitre.org/data/definitions/352.html" ] }, "configuration": { "checkStateChangingMethods": ["POST", "PUT", "DELETE", "PATCH"], "csrfProtectionPatterns": [ "csurf()", "csrfProtection", "verifyCsrfToken", "checkCsrf", "csrf-token", "_csrf", "req.csrfToken", "csrf.verify", "validateCSRF", "x-csrf-token", "x-xsrf-token" ], "globalMiddlewarePatterns": [ "app.use(csurf())", "app.use(csrfProtection)", "router.use(csurf())" ], "routeHandlerPatterns": [ "app.post", "app.put", "app.delete", "app.patch", "router.post", "router.put", "router.delete", "router.patch" ], "excludePatterns": [ "test", "spec", "mock", "__tests__" ] }, "examples": { "violations": [ { "description": "POST endpoint without CSRF protection", "code": "app.post('/api/transfer', (req, res) => {\n transferMoney(req.body.amount);\n});" }, { "description": "DELETE endpoint without CSRF middleware", "code": "router.delete('/api/user/:id', (req, res) => {\n deleteUser(req.params.id);\n});" }, { "description": "PUT endpoint without token validation", "code": "app.put('/api/profile', (req, res) => {\n updateProfile(req.body);\n});" } ], "fixes": [ { "description": "Apply CSRF protection globally", "code": "const csrf = require('csurf');\nconst csrfProtection = csrf({ cookie: true });\napp.use(csrfProtection);\n\napp.post('/api/transfer', (req, res) => {\n // CSRF token is automatically validated\n transferMoney(req.body.amount);\n});" }, { "description": "Apply CSRF protection per route", "code": "const csrfProtection = csrf({ cookie: true });\n\napp.post('/api/transfer', csrfProtection, (req, res) => {\n transferMoney(req.body.amount);\n});" }, { "description": "Manual CSRF token validation", "code": "app.post('/api/transfer', (req, res) => {\n const token = req.body._csrf || req.headers['x-csrf-token'];\n if (!validateCsrfToken(token, req.session)) {\n return res.status(403).send('Invalid CSRF token');\n }\n transferMoney(req.body.amount);\n});" } ] }, "testing": { "testCases": [ { "name": "post_without_csrf", "type": "violation", "description": "POST endpoint without CSRF protection" }, { "name": "put_without_csrf", "type": "violation", "description": "PUT endpoint without CSRF protection" }, { "name": "delete_without_csrf", "type": "violation", "description": "DELETE endpoint without CSRF protection" }, { "name": "global_csrf_protection", "type": "clean", "description": "Global CSRF middleware applied" }, { "name": "route_specific_csrf", "type": "clean", "description": "Route-specific CSRF middleware" } ] }, "performance": { "complexity": "O(n)", "description": "Linear complexity based on number of route handlers in the source code" }, "owaspMapping": { "category": "A01:2021 – Broken Access Control", "subcategories": [ "A04:2021 – Insecure Design" ], "description": "Validates that all state-changing endpoints have proper CSRF protection to prevent unauthorized cross-site requests" } }