# Implementation Guide for Security & Performance Improvements This guide provides step-by-step instructions for implementing the critical fixes identified in the audit. --- ## 1. SEO: Sitemap Generation ### Implementation Steps 1. **Add Sitemap Controller Routes** to `internal/routes/routes.go`: ```go // In SetupRootRoutes function func SetupRootRoutes(r *gin.Engine, db *gorm.DB) { sitemapCtrl := &controllers.SitemapController{DB: db} // Sitemap routes r.GET("/sitemap.xml", sitemapCtrl.GetSitemap) r.GET("/robots.txt", sitemapCtrl.GetRobotsTxt) // ... existing routes } ``` 2. **Update Settings Model** to include `EnableIndexing` field if not present: ```go // In internal/models/settings.go type Settings struct { // ... existing fields EnableIndexing bool `json:"enable_indexing" gorm:"default:true"` } ``` 3. **Test the sitemap**: - Visit `http://localhost:8080/sitemap.xml` - Visit `http://localhost:8080/robots.txt` - Verify XML structure is valid --- ## 2. Security: CSRF Protection ### Implementation Steps 1. **Add CSRF Token Endpoint** to routes: ```go // In internal/routes/routes.go import "fotbal-club/internal/middleware" func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) { // Public CSRF token endpoint api.GET("/csrf-token", middleware.GetCSRFToken) // Apply CSRF protection to state-changing routes protected := api.Group("") protected.Use(middleware.CSRFProtection()) { // All POST, PUT, PATCH, DELETE routes here protected.POST("/articles", baseCtrl.CreateArticle) protected.PUT("/articles/:id", baseCtrl.UpdateArticle) // ... etc } } ``` 2. **Update Frontend API Service** to fetch and include CSRF token: ```typescript // In frontend/src/services/api.ts let csrfToken: string | null = null; // Fetch CSRF token on app init export const initCSRF = async () => { try { const response = await axios.get(`${API_URL}/csrf-token`); csrfToken = response.data.csrf_token; } catch (error) { console.error('Failed to fetch CSRF token:', error); } }; // Add CSRF token to requests api.interceptors.request.use( (config: InternalAxiosRequestConfig) => { // Existing auth token logic... // Add CSRF token for state-changing requests if (['post', 'put', 'patch', 'delete'].includes(config.method?.toLowerCase() || '')) { if (csrfToken) { config.headers = config.headers || {}; (config.headers as any)['X-CSRF-Token'] = csrfToken; } } return config; }, (error) => Promise.reject(error) ); ``` 3. **Initialize CSRF in App**: ```typescript // In frontend/src/index.tsx import { initCSRF } from './services/api'; // After root.render() initCSRF(); ``` --- ## 3. Security: Improved Content-Security-Policy ### Implementation Steps 1. **Update CSP in main.go**: ```go // Replace the existing CSP in main.go middleware csp := "default-src 'self'; " + "script-src 'self' https://fonts.googleapis.com https://umami.tdvorak.dev; " + "style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; " + "font-src 'self' https://fonts.gstatic.com data:; " + "img-src 'self' data: https: blob:; " + "connect-src 'self' https://umami.tdvorak.dev https://zonerama.tdvorak.dev; " + "frame-src 'self' https://www.youtube.com https://www.youtube-nocookie.com; " + "object-src 'none'; " + "base-uri 'self'; " + "form-action 'self'; " + "frame-ancestors 'none';" c.Writer.Header().Set("Content-Security-Policy", csp) ``` 2. **Handle CSP violations** (optional): ```go // Add CSP report endpoint r.POST("/api/v1/csp-report", func(c *gin.Context) { var report map[string]interface{} if err := c.ShouldBindJSON(&report); err == nil { logger.Warn("CSP Violation: %+v", report) } c.Status(http.StatusNoContent) }) ``` 3. **Update CSP to include report-uri**: ```go csp += " report-uri /api/v1/csp-report;" ``` --- ## 4. Security: Remove Dev Bypass from Production ### Implementation Steps 1. **Update DevBypass middleware** in `internal/middleware/auth.go`: ```go func DevBypass() gin.HandlerFunc { return func(c *gin.Context) { // ONLY allow in development if config.AppConfig != nil && config.AppConfig.AppEnv == "development" { if strings.ToLower(c.GetHeader("X-Dev-Admin")) == "true" { logger.Warn("Dev bypass used - this should never happen in production!") c.Set("userRole", "admin") c.Set("user", &models.User{Role: "admin"}) c.Next() return } } c.Next() } } ``` 2. **Remove X-Dev-Admin header from frontend in production**: ```typescript // In frontend/src/services/api.ts export const api: AxiosInstance = axios.create({ baseURL: API_URL, headers: { // Only include dev headers in development ...(process.env.NODE_ENV === 'development' ? { 'X-Dev-Admin': 'true' } : {}), }, withCredentials: true, timeout: 20000, }); ``` --- ## 5. Security: HTML Sanitization ### Implementation Steps 1. **Use sanitization in controllers**: ```go // In internal/controllers/base_controller.go import "fotbal-club/pkg/utils" func (bc *BaseController) CreateArticle(c *gin.Context) { // ... existing code ... // Sanitize HTML content body.Content = utils.SanitizeHTML(body.Content) body.Title = utils.RemoveNullBytes(strings.TrimSpace(body.Title)) // ... rest of function } ``` 2. **Add frontend sanitization** for rich text editor: ```bash cd frontend npm install dompurify @types/dompurify ``` ```typescript // In frontend/src/utils/sanitize.ts import DOMPurify from 'dompurify'; export const sanitizeHTML = (dirty: string): string => { return DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'a', 'img', 'blockquote', 'code', 'pre'], ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'target', 'rel'], }); }; ``` --- ## 6. Performance: Code Splitting ### Implementation Steps 1. **Use the lazy-loaded App component**: ```typescript // Update frontend/src/index.tsx import AppLazy from './App.lazy'; // Replace with root.render( ); ``` 2. **Verify bundle splitting**: ```bash cd frontend npm run build ``` Check the build output - you should see multiple chunk files. --- ## 7. Performance: Add Bundle Analysis ### Implementation Steps 1. **Install webpack-bundle-analyzer**: ```bash cd frontend npm install --save-dev webpack-bundle-analyzer npm install --save-dev @craco/craco ``` 2. **Create craco config** if not exists (`frontend/craco.config.js`): ```javascript const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; module.exports = { webpack: { plugins: { add: [ process.env.ANALYZE && new BundleAnalyzerPlugin(), ].filter(Boolean), }, }, }; ``` 3. **Add analysis script** to `frontend/package.json`: ```json { "scripts": { "analyze": "ANALYZE=true npm run build" } } ``` 4. **Run analysis**: ```bash npm run analyze ``` --- ## 8. Security: Add Request Size Limits ### Implementation Steps 1. **Add middleware in main.go**: ```go // Add after router initialization r.Use(func(c *gin.Context) { // Limit request body size to 10MB (except for upload endpoints) if !strings.HasPrefix(c.Request.URL.Path, "/api/v1/upload") { c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 10*1024*1024) } c.Next() }) ``` --- ## Testing Checklist After implementing these changes: ### Security Tests - [ ] CSRF token is required for POST/PUT/DELETE requests - [ ] Dev bypass doesn't work in production environment - [ ] CSP blocks inline scripts - [ ] HTML sanitization removes `