UNPKG

@sun-asterisk/sunlint

Version:

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

176 lines (175 loc) 6.18 kB
{ "rule": { "id": "S041", "name": "Session Tokens must be invalidated after logout or expiration", "description": "Session tokens must be properly invalidated after logout or expiration to prevent session hijacking and unauthorized access. This includes clearing session data, invalidating JWT tokens, and ensuring proper session cleanup.", "category": "security", "severity": "error", "languages": ["typescript", "javascript"], "frameworks": ["express", "nestjs", "node"], "version": "1.0.0", "status": "stable", "tags": ["security", "session", "token", "logout", "invalidation", "owasp"], "references": [ "https://owasp.org/www-community/controls/Session_Management_Cheat_Sheet", "https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html", "https://owasp.org/www-community/attacks/Session_hijacking_attack", "https://portswigger.net/web-security/authentication/password-based/lab-session-fixation" ] }, "configuration": { "enableLogoutDetection": true, "enableSessionCleanupDetection": true, "enableTokenInvalidationDetection": true, "enableJWTHandlingDetection": true, "checkLogoutMethods": [ "logout", "signOut", "logOut", "destroy", "clear", "invalidate", "revoke", "expire" ], "checkSessionMethods": [ "removeItem", "clear", "destroy", "delete" ], "checkExpressSessionMethods": [ "destroy", "regenerate", "reload", "save" ], "checkJWTMethods": [ "sign", "verify", "decode", "invalidate", "blacklist" ], "checkTokenMethods": [ "removeToken", "clearToken", "invalidateToken", "revokeToken", "deleteToken", "destroyToken" ], "sessionCleanupPatterns": [ "req.session.destroy", "req.session = null", "req.session = {}", "session.destroy", "session.clear", "session.remove", ".removeItem(", ".clear(", ".delete(" ], "tokenInvalidationPatterns": [ "blacklist", "revoke", "invalidate", "expire", "remove.*token", "delete.*token", "clear.*token", "destroy.*token" ], "logoutHandlerPatterns": [ "function.*logout", "const.*logout.*=", "let.*logout.*=", "var.*logout.*=", "logout.*:.*function", "logout.*:.*(" ], "sessionStoragePatterns": [ "sessionStorage", "localStorage", "req.session", "session[" ] }, "examples": { "violations": [ { "description": "Logout method without session cleanup", "code": "app.post('/logout', (req, res) => {\n // Missing session cleanup\n res.json({ message: 'Logged out' });\n});" }, { "description": "Logout handler without token invalidation", "code": "function logout(req, res) {\n // Missing token invalidation\n res.redirect('/login');\n}" }, { "description": "JWT token signing during logout", "code": "app.post('/logout', (req, res) => {\n const token = jwt.sign({ userId: req.user.id }, secret);\n res.json({ token });\n});" }, { "description": "Session method without proper cleanup", "code": "app.post('/logout', (req, res) => {\n req.session.userId = null; // Should use destroy()\n res.json({ message: 'Logged out' });\n});" } ], "fixes": [ { "description": "Proper session cleanup in logout", "code": "app.post('/logout', (req, res) => {\n req.session.destroy((err) => {\n if (err) {\n return res.status(500).json({ error: 'Logout failed' });\n }\n res.clearCookie('sessionId');\n res.json({ message: 'Logged out successfully' });\n });\n});" }, { "description": "JWT token blacklisting during logout", "code": "app.post('/logout', (req, res) => {\n const token = req.headers.authorization?.split(' ')[1];\n if (token) {\n // Add token to blacklist\n blacklist.add(token);\n }\n res.json({ message: 'Logged out successfully' });\n});" }, { "description": "Complete session and token cleanup", "code": "app.post('/logout', async (req, res) => {\n try {\n // Invalidate JWT token\n const token = req.headers.authorization?.split(' ')[1];\n if (token) {\n await tokenService.blacklist(token);\n }\n \n // Clear session\n req.session.destroy();\n \n // Clear cookies\n res.clearCookie('sessionId');\n res.clearCookie('token');\n \n res.json({ message: 'Logged out successfully' });\n } catch (error) {\n res.status(500).json({ error: 'Logout failed' });\n }\n});" } ] }, "testing": { "testCases": [ { "name": "logout_without_session_cleanup", "type": "violation", "description": "Logout method without proper session cleanup" }, { "name": "logout_handler_without_token_invalidation", "type": "violation", "description": "Logout handler without token invalidation" }, { "name": "jwt_sign_during_logout", "type": "violation", "description": "JWT token signing during logout process" }, { "name": "session_method_without_cleanup", "type": "violation", "description": "Session method without proper cleanup" }, { "name": "secure_logout_with_session_cleanup", "type": "clean", "description": "Logout with proper session cleanup" }, { "name": "secure_logout_with_token_invalidation", "type": "clean", "description": "Logout with proper token invalidation" }, { "name": "complete_logout_implementation", "type": "clean", "description": "Complete logout implementation with all security measures" } ] }, "performance": { "complexity": "O(n)", "description": "Linear complexity based on number of function calls and expressions in the source code" } }