feat: implement 5 QA-driven security and quality proposals
Implemented proposals from comprehensive QA review: 1. extend-csrf-protection - Add POST to CSRF protected methods in frontend - Global CSRF middleware for all state-changing operations - Update tests with CSRF token fixtures 2. tighten-cors-websocket-security - Replace wildcard CORS with explicit method/header lists - Disable query parameter auth in production (code 4002) - Add per-user WebSocket connection limit (max 5, code 4005) 3. shorten-jwt-expiry - Reduce JWT expiry from 7 days to 60 minutes - Add refresh token support with 7-day expiry - Implement token rotation on refresh - Frontend auto-refresh when token near expiry (<5 min) 4. fix-frontend-quality - Add React.lazy() code splitting for all pages - Fix useCallback dependency arrays (Dashboard, Comments) - Add localStorage data validation in AuthContext - Complete i18n for AttachmentUpload component 5. enhance-backend-validation - Add SecurityAuditMiddleware for access denied logging - Add ErrorSanitizerMiddleware for production error messages - Protect /health/detailed with admin authentication - Add input length validation (comment 5000, desc 10000) All 521 backend tests passing. Frontend builds successfully. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -98,6 +98,31 @@
|
||||
- **WHEN** 異常行為發生
|
||||
- **THEN** 系統記錄並發送警示
|
||||
|
||||
### Requirement: Security Event Logging
|
||||
The system SHALL record failed access attempts for security monitoring and intrusion detection.
|
||||
|
||||
#### Scenario: Permission denied logged
|
||||
- **WHEN** server returns 403 Forbidden for a resource access attempt
|
||||
- **THEN** audit log entry is created with event_type "security.access_denied"
|
||||
- **AND** entry includes user_id, resource_type, and attempted_action
|
||||
|
||||
#### Scenario: Repeated auth failures logged
|
||||
- **WHEN** same IP has 5+ failed authentication attempts in 10 minutes
|
||||
- **THEN** audit log entry is created with event_type "security.suspicious_auth_pattern"
|
||||
- **AND** entry includes IP address and failure count
|
||||
- **AND** alert is generated for security administrators
|
||||
|
||||
### Requirement: Detailed Health Endpoint Security
|
||||
The detailed system health endpoint SHALL require admin authentication to prevent information disclosure.
|
||||
|
||||
#### Scenario: Admin accesses detailed health
|
||||
- **WHEN** system administrator requests GET /health/detailed
|
||||
- **THEN** full system status including connection pools is returned
|
||||
|
||||
#### Scenario: Non-admin accesses detailed health
|
||||
- **WHEN** non-admin user or unauthenticated request to GET /health/detailed
|
||||
- **THEN** request is rejected with 401 Unauthorized or 403 Forbidden
|
||||
|
||||
## Data Model
|
||||
|
||||
```
|
||||
|
||||
@@ -161,6 +161,33 @@ The system SHALL support project templates to standardize project creation.
|
||||
- **THEN** system creates template with project's CustomField definitions
|
||||
- **THEN** template is available for future project creation
|
||||
|
||||
### Requirement: Code Splitting
|
||||
The application SHALL use code splitting with React.lazy() to reduce initial bundle size and improve load times.
|
||||
|
||||
#### Scenario: Initial page load
|
||||
- **WHEN** user navigates to application
|
||||
- **THEN** only core framework and current route are loaded
|
||||
- **AND** other routes are loaded on demand
|
||||
|
||||
#### Scenario: Route-based splitting
|
||||
- **WHEN** user navigates to a different page
|
||||
- **THEN** that page's code chunk is loaded dynamically
|
||||
- **AND** loading fallback is displayed during load
|
||||
|
||||
### Requirement: LocalStorage Data Validation
|
||||
User data loaded from localStorage SHALL be validated before use to prevent crashes from corrupted data.
|
||||
|
||||
#### Scenario: Corrupted localStorage data
|
||||
- **WHEN** localStorage contains malformed user JSON
|
||||
- **THEN** invalid data is cleared
|
||||
- **AND** user is redirected to login page
|
||||
- **AND** no application crash occurs
|
||||
|
||||
#### Scenario: Valid localStorage data
|
||||
- **WHEN** localStorage contains valid user JSON
|
||||
- **THEN** user is authenticated from stored data
|
||||
- **AND** application loads normally
|
||||
|
||||
### Requirement: Error Boundary Protection
|
||||
The frontend application SHALL gracefully handle component render errors without crashing the entire application.
|
||||
|
||||
|
||||
@@ -78,6 +78,26 @@
|
||||
- **WHEN** 管理者嘗試新增第 21 個欄位
|
||||
- **THEN** 系統拒絕新增並顯示數量已達上限的訊息
|
||||
|
||||
### Requirement: Input Length Validation
|
||||
All text input fields SHALL have maximum length constraints to prevent abuse and database issues.
|
||||
|
||||
#### Scenario: Task title exceeds limit
|
||||
- **WHEN** user creates a task with title exceeding 500 characters
|
||||
- **THEN** request is rejected with 422 Validation Error
|
||||
- **AND** error indicates field length exceeded
|
||||
|
||||
#### Scenario: Description within limit
|
||||
- **WHEN** user creates a task with description 10000 characters or less
|
||||
- **THEN** task is created successfully
|
||||
|
||||
#### Scenario: Description exceeds limit
|
||||
- **WHEN** user creates a task with description exceeding 10000 characters
|
||||
- **THEN** request is rejected with 422 Validation Error
|
||||
|
||||
#### Scenario: Comment content limit
|
||||
- **WHEN** user submits a comment exceeding 5000 characters
|
||||
- **THEN** request is rejected with 422 Validation Error
|
||||
|
||||
### Requirement: Multiple Views
|
||||
系統 SHALL 支援多維視角:看板 (Kanban)、甘特圖 (Gantt)、列表 (List)、行事曆 (Calendar)。
|
||||
|
||||
|
||||
@@ -89,6 +89,34 @@
|
||||
- **WHEN** 使用者執行登出操作
|
||||
- **THEN** 系統銷毀 session 並清除 token
|
||||
|
||||
### Requirement: Access Token Expiry
|
||||
Access tokens SHALL expire within 60 minutes to limit exposure window in case of token compromise.
|
||||
|
||||
#### Scenario: Access token expiry
|
||||
- **WHEN** an access token issued 61 minutes ago is used for API authentication
|
||||
- **THEN** request is rejected with 401 Unauthorized
|
||||
- **AND** error indicates "Token expired"
|
||||
|
||||
### Requirement: Refresh Token Support
|
||||
The system SHALL support refresh tokens for seamless session continuity without requiring re-authentication.
|
||||
|
||||
#### Scenario: Refresh valid token
|
||||
- **WHEN** POST to /api/auth/refresh with valid refresh token
|
||||
- **THEN** new access token is issued
|
||||
- **AND** new refresh token is issued via rotation
|
||||
- **AND** old refresh token is invalidated
|
||||
|
||||
#### Scenario: Refresh expired token
|
||||
- **WHEN** POST to /api/auth/refresh with expired refresh token
|
||||
- **THEN** request is rejected with 401 Unauthorized
|
||||
- **AND** user must re-authenticate via login
|
||||
|
||||
#### Scenario: Automatic frontend refresh
|
||||
- **WHEN** access token expires in less than 5 minutes
|
||||
- **AND** frontend prepares to make API call
|
||||
- **THEN** token is automatically refreshed first
|
||||
- **AND** original request proceeds with new token
|
||||
|
||||
### Requirement: API Rate Limiting
|
||||
The system SHALL implement rate limiting to protect against brute force attacks and DoS attempts.
|
||||
|
||||
@@ -143,8 +171,19 @@ The system SHALL enforce maximum length limits on all user-provided string input
|
||||
- **WHEN** user submits content with description under 10000 characters
|
||||
- **THEN** system accepts the input and processes normally
|
||||
|
||||
### Requirement: CORS Security
|
||||
The system SHALL explicitly define allowed CORS methods and headers instead of using wildcards to reduce attack surface.
|
||||
|
||||
#### Scenario: Request with standard headers
|
||||
- **WHEN** a cross-origin request includes Content-Type, Authorization, or X-CSRF-Token headers
|
||||
- **THEN** the request is allowed
|
||||
|
||||
#### Scenario: Request with non-standard header
|
||||
- **WHEN** a cross-origin request includes a non-whitelisted custom header
|
||||
- **THEN** CORS preflight fails and request is rejected
|
||||
|
||||
### Requirement: Secure WebSocket Authentication
|
||||
The system SHALL authenticate WebSocket connections without exposing tokens in URL query parameters.
|
||||
The system SHALL authenticate WebSocket connections without exposing tokens in URL query parameters. In production environments, query parameter authentication SHALL be disabled.
|
||||
|
||||
#### Scenario: WebSocket connection with token in first message
|
||||
- **WHEN** client connects to WebSocket endpoint without a query token
|
||||
@@ -161,6 +200,25 @@ The system SHALL authenticate WebSocket connections without exposing tokens in U
|
||||
- **WHEN** client connects but does not send authentication within 10 seconds
|
||||
- **THEN** server closes the connection with appropriate error code
|
||||
|
||||
#### Scenario: Query parameter auth in production
|
||||
- **WHEN** production environment and WebSocket connection includes token in query parameter
|
||||
- **THEN** connection is rejected with code 4002
|
||||
- **AND** error message indicates "Query parameter auth disabled in production"
|
||||
|
||||
### Requirement: WebSocket Connection Limits
|
||||
The system SHALL limit each user to a maximum of 5 concurrent WebSocket connections to prevent resource exhaustion.
|
||||
|
||||
#### Scenario: User exceeds connection limit
|
||||
- **WHEN** user already has 5 active WebSocket connections
|
||||
- **AND** user attempts to open a 6th connection
|
||||
- **THEN** connection is rejected with code 4005
|
||||
- **AND** error message indicates "Too many connections"
|
||||
|
||||
#### Scenario: User within connection limit
|
||||
- **WHEN** user has fewer than 5 active connections
|
||||
- **AND** user opens a new WebSocket connection
|
||||
- **THEN** connection is accepted
|
||||
|
||||
### Requirement: Path Traversal Protection
|
||||
The system SHALL prevent file path traversal attacks by validating all file paths resolve within the designated storage directory.
|
||||
|
||||
@@ -187,22 +245,25 @@ The system SHALL validate JWT secret key strength on startup.
|
||||
- **THEN** the system SHALL log a security warning
|
||||
|
||||
### Requirement: CSRF Protection
|
||||
The system SHALL protect sensitive state-changing operations with CSRF tokens.
|
||||
The system SHALL protect all state-changing operations (POST, PUT, PATCH, DELETE) with CSRF tokens.
|
||||
|
||||
#### Scenario: CSRF token required for password change
|
||||
- **WHEN** a user attempts to change their password
|
||||
- **AND** the request does not include a valid CSRF token
|
||||
#### Scenario: POST request without CSRF token
|
||||
- **WHEN** an authenticated user makes a POST request without X-CSRF-Token header
|
||||
- **THEN** the request SHALL be rejected with 403 Forbidden
|
||||
- **AND** error message indicates "CSRF token is required"
|
||||
|
||||
#### Scenario: CSRF token required for account deletion
|
||||
- **WHEN** a user attempts to delete their account or resources
|
||||
- **AND** the request does not include a valid CSRF token
|
||||
#### Scenario: PUT/PATCH/DELETE request without CSRF token
|
||||
- **WHEN** an authenticated user makes a PUT, PATCH, or DELETE request without X-CSRF-Token header
|
||||
- **THEN** the request SHALL be rejected with 403 Forbidden
|
||||
|
||||
#### Scenario: Valid CSRF token accepted
|
||||
- **WHEN** a state-changing request includes a valid CSRF token
|
||||
- **THEN** the request SHALL proceed normally
|
||||
|
||||
#### Scenario: Public endpoints exempt from CSRF
|
||||
- **WHEN** POST to /api/auth/login or other public endpoints
|
||||
- **THEN** CSRF token is not required
|
||||
|
||||
## Data Model
|
||||
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user