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:
beabigegg
2026-01-12 23:19:05 +08:00
parent df50d5e7f8
commit 35c90fe76b
48 changed files with 2132 additions and 403 deletions

View File

@@ -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
```

View File

@@ -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.

View File

@@ -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)。

View File

@@ -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
```