Files
MyClub/DOCS/IMPLEMENTATION_GUIDE.md
T
Tomáš Dvořák 12cba639b9 upload
2025-10-16 13:32:05 +02:00

9.4 KiB

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:
// 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
}
  1. Update Settings Model to include EnableIndexing field if not present:
// In internal/models/settings.go
type Settings struct {
    // ... existing fields
    EnableIndexing bool `json:"enable_indexing" gorm:"default:true"`
}
  1. 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:
// 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
    }
}
  1. Update Frontend API Service to fetch and include CSRF token:
// 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)
);
  1. Initialize CSRF in App:
// 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:
// 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)
  1. Handle CSP violations (optional):
// 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)
})
  1. Update CSP to include report-uri:
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:
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()
    }
}
  1. Remove X-Dev-Admin header from frontend in production:
// 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:
// 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
}
  1. Add frontend sanitization for rich text editor:
cd frontend
npm install dompurify @types/dompurify
// 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:
// Update frontend/src/index.tsx
import AppLazy from './App.lazy';

// Replace <App /> with <AppLazy />
root.render(
  <React.StrictMode>
    <ErrorBoundary>
      <HelmetProvider>
        <ColorModeScript initialColorMode={theme.config.initialColorMode} />
        <AppLazy />
      </HelmetProvider>
    </ErrorBoundary>
  </React.StrictMode>
);
  1. Verify bundle splitting:
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:
cd frontend
npm install --save-dev webpack-bundle-analyzer
npm install --save-dev @craco/craco
  1. Create craco config if not exists (frontend/craco.config.js):
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  webpack: {
    plugins: {
      add: [
        process.env.ANALYZE && new BundleAnalyzerPlugin(),
      ].filter(Boolean),
    },
  },
};
  1. Add analysis script to frontend/package.json:
{
  "scripts": {
    "analyze": "ANALYZE=true npm run build"
  }
}
  1. Run analysis:
npm run analyze

8. Security: Add Request Size Limits

Implementation Steps

  1. Add middleware in main.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 <script> tags
  • Request size limit rejects large payloads

SEO Tests

  • /sitemap.xml returns valid XML
  • /robots.txt includes sitemap reference
  • Meta descriptions are properly set
  • Structured data validates on Google's testing tool

Performance Tests

  • Multiple JS chunks are generated on build
  • Initial bundle size is reduced
  • Pages load incrementally
  • Bundle analyzer shows size breakdown

Deployment Notes

  1. Environment Variables - Ensure production .env has:

    APP_ENV=production
    JWT_SECRET=<strong-random-secret>
    CONTENT_SECURITY_POLICY=<strict-policy>
    
  2. Database Migration - Add EnableIndexing column:

    ALTER TABLE settings ADD COLUMN enable_indexing BOOLEAN DEFAULT true;
    
  3. Frontend Build - Use production build:

    cd frontend
    npm run build
    
  4. Testing - Test all critical paths after deployment


Rollback Plan

If issues arise:

  1. CSRF Issues: Remove CSRFProtection() middleware temporarily
  2. CSP Issues: Set to report-only mode first
  3. Code Splitting Issues: Use original App.tsx instead of App.lazy.tsx
  4. Sitemap Issues: Comment out sitemap routes

Support

For questions or issues during implementation:

  1. Check the COMPREHENSIVE_AUDIT_REPORT.md
  2. Review error logs in logs/ directory
  3. Test in development environment first