tractatus/docs/plans/QUICK_WINS_IMPLEMENTATION.md
TheFlow 2298d36bed fix(submissions): restructure Economist package and fix article display
- Create Economist SubmissionTracking package correctly:
  * mainArticle = full blog post content
  * coverLetter = 216-word SIR— letter
  * Links to blog post via blogPostId
- Archive 'Letter to The Economist' from blog posts (it's the cover letter)
- Fix date display on article cards (use published_at)
- Target publication already displaying via blue badge

Database changes:
- Make blogPostId optional in SubmissionTracking model
- Economist package ID: 68fa85ae49d4900e7f2ecd83
- Le Monde package ID: 68fa2abd2e6acd5691932150

Next: Enhanced modal with tabs, validation, export

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 08:47:42 +13:00

11 KiB

Security Quick Wins Implementation Guide

Get 80% of security value with 20% of effort

Status: Ready to Deploy Time to Implement: 1-2 hours Value: HIGH - Immediate protection against common attacks


What You're Getting

Security Headers - Prevents XSS, clickjacking, MIME sniffing Input Validation - Sanitizes HTML, enforces length limits Rate Limiting - Prevents brute force, DoS, spam CSRF Protection - Prevents cross-site request forgery Security Logging - Audit trail for all security events Response Sanitization - Hides stack traces and sensitive data

What This Protects Against:

  • Cross-Site Scripting (XSS) attacks
  • Cross-Site Request Forgery (CSRF)
  • Clickjacking
  • MIME type confusion attacks
  • Brute force authentication
  • Form spam
  • DoS attacks
  • Information disclosure

Prerequisites

1. Install Dependencies

cd /home/theflow/projects/tractatus

# Install required npm packages
npm install express-rate-limit validator csurf cookie-parser

2. Create Log Directory

# Create security log directory
sudo mkdir -p /var/log/tractatus
sudo chown -R $USER:$USER /var/log/tractatus
sudo chmod 750 /var/log/tractatus

Step 1: Update src/server.js

Add the following to your src/server.js file. Insert in the order shown:

const express = require('express');
const cookieParser = require('cookie-parser');
const csrf = require('csurf');

// Import security middleware
const { securityHeadersMiddleware } = require('./middleware/security-headers.middleware');
const { publicRateLimiter } = require('./middleware/rate-limit.middleware');
const { sanitizeErrorResponse, sanitizeResponseData } = require('./middleware/response-sanitization.middleware');

const app = express();

// ============================================================
// SECURITY MIDDLEWARE (Apply BEFORE routes)
// ============================================================

// 1. Security Headers (HIGH VALUE - 5 minutes to add)
app.use(securityHeadersMiddleware);

// 2. Cookie Parser (required for CSRF)
app.use(cookieParser());

// 3. Body Parsers
app.use(express.json({ limit: '1mb' }));  // Payload size limit
app.use(express.urlencoded({ extended: true, limit: '1mb' }));

// 4. Response Data Sanitization
app.use(sanitizeResponseData);

// 5. Public Rate Limiting (100 req/15min per IP)
app.use(publicRateLimiter);

// 6. CSRF Protection (POST/PUT/DELETE/PATCH only)
const csrfProtection = csrf({ cookie: true });
app.use((req, res, next) => {
  if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(req.method)) {
    return csrfProtection(req, res, next);
  }
  next();
});

// ============================================================
// YOUR EXISTING ROUTES
// ============================================================

// CSRF token endpoint (for forms)
app.get('/api/csrf-token', (req, res) => {
  res.json({ csrfToken: req.csrfToken() });
});

// ... your existing routes here ...

// ============================================================
// ERROR HANDLING (Apply AFTER routes)
// ============================================================

// CSRF Error Handler
app.use((err, req, res, next) => {
  if (err.code === 'EBADCSRFTOKEN') {
    return res.status(403).json({
      error: 'Invalid CSRF token',
      message: 'Request blocked for security reasons'
    });
  }
  next(err);
});

// General Error Handler (hides stack traces in production)
app.use(sanitizeErrorResponse);

// Start server
const PORT = process.env.PORT || 9000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
  console.log('✅ Security middleware active');
});

Step 2: Add Form-Specific Rate Limiting

For form submission endpoints (contact, cases, media inquiries), add stricter rate limiting:

const { formRateLimiter } = require('./middleware/rate-limit.middleware');
const { createInputValidationMiddleware } = require('./middleware/input-validation.middleware');

// Example: Case submission endpoint
app.post('/api/cases/submit',
  formRateLimiter,  // 5 submissions/min
  createInputValidationMiddleware({
    title: { type: 'text', required: true, maxLength: 200 },
    description: { type: 'text', required: true, maxLength: 5000 },
    contact_email: { type: 'email', required: true },
    contact_name: { type: 'text', required: true, maxLength: 100 }
  }),
  casesController.submitCase
);

// Example: Contact form
app.post('/api/contact',
  formRateLimiter,
  createInputValidationMiddleware({
    name: { type: 'text', required: true, maxLength: 100 },
    email: { type: 'email', required: true },
    message: { type: 'text', required: true, maxLength: 5000 }
  }),
  contactController.submitContact
);

Step 3: Update Client-Side Forms for CSRF

Add CSRF token to all forms that POST/PUT/DELETE:

// Fetch CSRF token before form submission
async function submitForm(formData) {
  try {
    // Get CSRF token
    const tokenResponse = await fetch('/api/csrf-token');
    const { csrfToken } = await tokenResponse.json();

    // Add CSRF token to form data
    formData.append('_csrf', csrfToken);

    // Submit form
    const response = await fetch('/api/cases/submit', {
      method: 'POST',
      body: formData
    });

    if (!response.ok) {
      throw new Error('Submission failed');
    }

    return await response.json();
  } catch (error) {
    console.error('Form submission error:', error);
    throw error;
  }
}

For JSON submissions:

async function submitJSON(data) {
  // Get CSRF token
  const tokenResponse = await fetch('/api/csrf-token');
  const { csrfToken } = await tokenResponse.json();

  // Submit with CSRF token
  const response = await fetch('/api/cases/submit', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'CSRF-Token': csrfToken  // Can also use header instead of body
    },
    body: JSON.stringify({
      ...data,
      _csrf: csrfToken  // Or include in body
    })
  });

  return response.json();
}

Step 4: Test the Security Measures

Test Security Headers

# Check headers are present
curl -I http://localhost:9000

# Expected headers:
# Content-Security-Policy: ...
# X-Content-Type-Options: nosniff
# X-Frame-Options: DENY
# X-XSS-Protection: 1; mode=block
# Referrer-Policy: strict-origin-when-cross-origin
# Permissions-Policy: geolocation=(), ...

Test Rate Limiting

# Exceed public limit (should get 429 after 100 requests)
for i in {1..110}; do
  curl -s -o /dev/null -w "%{http_code}\n" http://localhost:9000
done

# Expected: First 100 return 200, remaining return 429

Test Input Validation

# Test XSS payload (should be sanitized)
curl -X POST http://localhost:9000/api/contact \
  -H "Content-Type: application/json" \
  -d '{"name":"<script>alert(1)</script>","email":"test@example.com","message":"test"}'

# Expected: Name sanitized to empty or plain text

Test CSRF Protection

# Request without CSRF token (should be rejected)
curl -X POST http://localhost:9000/api/contact \
  -H "Content-Type: application/json" \
  -d '{"name":"Test","email":"test@example.com","message":"test"}'

# Expected: 403 Forbidden with "Invalid CSRF token"

Check Security Logs

# View security events
tail -f /var/log/tractatus/security-audit.log

# Should see JSON entries for:
# - rate_limit_exceeded
# - input_sanitized
# - input_validation_failure

Step 5: Deploy to Production

1. Commit Changes

git add src/middleware/ src/utils/security-logger.js src/server.js
git commit -m "security: implement quick wins (headers, rate limiting, CSRF, input validation)"

2. Deploy to Production

# Your deployment script
./scripts/deploy-full-project-SAFE.sh

# Or manual deployment
rsync -avz --chmod=D755,F644 -e "ssh -i ~/.ssh/tractatus_deploy" \
  src/middleware/ src/utils/security-logger.js \
  ubuntu@vps-93a693da.vps.ovh.net:/var/www/tractatus/src/

# Restart production server
ssh -i ~/.ssh/tractatus_deploy ubuntu@vps-93a693da.vps.ovh.net \
  "sudo systemctl restart tractatus"

3. Verify Production

# Check headers on production
curl -I https://agenticgovernance.digital

# Test rate limiting
for i in {1..110}; do
  curl -s -o /dev/null -w "%{http_code}\n" https://agenticgovernance.digital
done

# Check SecurityHeaders.com grade
# Visit: https://securityheaders.com/?q=https://agenticgovernance.digital
# Expected: Grade A or A+

4. Monitor for Issues

# Watch production logs
ssh -i ~/.ssh/tractatus_deploy ubuntu@vps-93a693da.vps.ovh.net \
  "tail -f /var/www/tractatus/logs/combined.log"

# Watch security logs
ssh -i ~/.ssh/tractatus_deploy ubuntu@vps-93a693da.vps.ovh.net \
  "tail -f /var/log/tractatus/security-audit.log"

What's Next? (Phase 1-6)

These quick wins give you immediate protection. When ready for comprehensive security:

  1. Phase 1 - Install ClamAV, YARA, fail2ban, Redis
  2. Phase 2 - File upload malware scanning, email security
  3. Phase 3 - Enhanced input validation (NoSQL injection, XSS detection)
  4. Phase 4 - JWT authentication, Redis rate limiting, IP blocking
  5. Phase 5 - Security dashboard, ProtonMail alerts, Signal notifications
  6. Phase 6 - Penetration testing, team training, external audit

See docs/plans/security-implementation-roadmap.md for full details.


Troubleshooting

"Cannot find module 'express-rate-limit'"

npm install express-rate-limit validator csurf cookie-parser

"Permission denied: /var/log/tractatus/"

sudo mkdir -p /var/log/tractatus
sudo chown -R $USER:$USER /var/log/tractatus
sudo chmod 750 /var/log/tractatus

CSRF token errors on GET requests

CSRF protection is only applied to POST/PUT/DELETE/PATCH. GET requests should not be affected.

Rate limit too restrictive

Adjust limits in src/middleware/rate-limit.middleware.js:

const publicRateLimiter = createRateLimiter({
  windowMs: 15 * 60 * 1000,
  max: 200,  // Increase from 100 to 200
  tier: 'public'
});

Security headers causing issues

Temporarily disable specific headers in src/middleware/security-headers.middleware.js while debugging.


Performance Impact

Expected performance impact: <10ms per request

  • Security headers: <1ms (set headers only)
  • Input validation: 2-5ms (depends on input size)
  • Rate limiting: 1-2ms (in-memory lookup)
  • CSRF validation: 1-2ms (token comparison)
  • Response sanitization: 1-2ms (field removal)

Total: ~5-10ms additional latency per request (negligible for most applications)


Success Criteria

Security headers present on all responses SecurityHeaders.com grade A or A+ Rate limiting preventing abuse (429 errors after limits) CSRF tokens required for POST/PUT/DELETE Input sanitization removing HTML tags Security events logged to /var/log/tractatus/security-audit.log Error responses sanitized (no stack traces in production) Zero false positives with legitimate traffic


Support

Questions? Check:

  • docs/plans/security-implementation-roadmap.md - Full implementation plan
  • docs/plans/security-implementation-tracker.md - Phase tracking
  • .claude/instruction-history.json - inst_041 through inst_046

Issues? Create incident report in docs/security/incidents/

Ready for More? Proceed to Phase 1 in the tracker!


Last Updated: 2025-10-14 Version: 1.0 (Quick Wins) Next: Full Phase 1-6 implementation