# Security Implementation Roadmap **Tractatus Comprehensive Security Framework** **Version:** 1.0 **Created:** 2025-10-14 **Status:** Implementation Plan **Implements:** inst_041, inst_042, inst_043, inst_044, inst_045, inst_046 --- ## Executive Summary This roadmap implements a 6-layer defense-in-depth security framework for Tractatus using exclusively sovereign tools and trusted communication platforms (Proton, Signal). The implementation is structured in 6 phases over approximately 8-12 weeks, with each phase building on the previous while remaining independently testable. **Security Layers:** 1. File Upload Validation (inst_041) 2. Email Security Pipeline (inst_042) 3. Form Input Sanitization (inst_043) 4. HTTP Security Headers (inst_044) 5. API Endpoint Protection (inst_045) 6. Security Monitoring & Alerting (inst_046) **Core Principles:** - **Sovereign tools only**: Open-source, auditable, self-hosted - **Defense in depth**: Multiple overlapping security layers - **Zero external dependencies**: No cloud services or proprietary APIs - **Comprehensive logging**: All security events logged and monitored - **Fail-safe defaults**: Reject suspicious inputs, never auto-process --- ## Communication Infrastructure ### Proton Suite - **ProtonMail**: Secure email for security alerts, weekly reports, incident notifications - **ProtonVPN**: Secure access to production servers during deployment - **ProtonDrive**: Encrypted storage for security documentation, incident reports, backup logs **Setup Requirements:** - Proton Business or Visionary account - Custom domain integration for `security@tractatus.digital`, `admin@tractatus.digital` - ProtonMail Bridge for local email client integration (if needed) ### Signal - **Text Messaging**: Critical security alerts, incident coordination - **Video Conferencing**: Security reviews, incident response, team coordination - **Setup**: Security team group, escalation protocols, mobile device requirements --- ## Prerequisites ### Infrastructure Requirements - **Production Server**: Ubuntu 22.04 LTS (vps-93a693da.vps.ovh.net) - **Development Server**: Local Ubuntu environment - **Access**: SSH keys, sudo privileges, systemd management - **Database**: MongoDB 5.0+ already installed (port 27017) - **Web Server**: nginx (reverse proxy for Node.js app on port 9000) - **Node.js**: v18+ with npm - **Redis**: Required for distributed rate limiting (Phase 4) ### Team Requirements - **System Administrator**: Server configuration, tool installation, fail2ban setup - **Developer**: Middleware implementation, API integration, testing - **Security Reviewer**: Configuration validation, penetration testing, audit - **Project Owner**: Approval of security policies, alert thresholds, incident protocols ### Documentation - **Read**: inst_041 through inst_046 in `.claude/instruction-history.json` - **Review**: CLAUDE_Tractatus_Maintenance_Guide.md sections on security - **Prepare**: Incident response playbook (template provided in Phase 6) --- ## Phase 1: Foundation & Sovereign Tools Installation **Duration:** 1-2 weeks **Effort:** 20-30 hours **Dependencies:** None **Risk:** Low ### Objectives 1. Install and configure all sovereign security tools 2. Set up base logging infrastructure 3. Establish secure communication channels 4. Create security documentation structure ### Tasks #### 1.1 ClamAV Antivirus Installation ```bash # Install ClamAV sudo apt update sudo apt install -y clamav clamav-daemon clamav-freshclam # Stop services to update database sudo systemctl stop clamav-freshclam sudo systemctl stop clamav-daemon # Manual database update (first time) sudo freshclam # Start services sudo systemctl start clamav-freshclam sudo systemctl start clamav-daemon # Enable auto-start sudo systemctl enable clamav-freshclam sudo systemctl enable clamav-daemon # Verify installation clamscan --version clamdscan --version ``` **Configuration:** - Edit `/etc/clamav/clamd.conf`: - `MaxFileSize 50M` (matches inst_041 media limit) - `MaxScanSize 100M` - `StreamMaxLength 50M` - `LogFile /var/log/clamav/clamav.log` - `LogTime yes` - `LogFileMaxSize 10M` - Edit `/etc/clamav/freshclam.conf`: - `UpdateLogFile /var/log/clamav/freshclam.log` - `DatabaseMirror database.clamav.net` - `Checks 24` (daily updates as per inst_041) **Testing:** ```bash # Test with EICAR test file wget http://www.eicar.org/download/eicar.com.txt clamscan eicar.com.txt # Should detect as malware rm eicar.com.txt # Test daemon scanning echo "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*" > test.txt clamdscan test.txt # Should detect malware rm test.txt ``` #### 1.2 YARA Installation & Rule Configuration ```bash # Install YARA sudo apt install -y yara # Verify installation yara --version ``` **Create YARA Rules Directory:** ```bash sudo mkdir -p /etc/yara/rules sudo mkdir -p /var/log/yara ``` **Create Base Rule Set** (`/etc/yara/rules/tractatus-base.yar`): ```yara rule Suspicious_Executable_in_Document { meta: description = "Detects embedded executables in document files" severity = "high" strings: $mz = "MZ" $pe = "PE" condition: uint16(0) == 0x5A4D and $pe in (0..1024) } rule Suspicious_Script_Injection { meta: description = "Detects potential script injection patterns" severity = "high" strings: $js1 = " # Set: maxmemory 256mb # Set: maxmemory-policy allkeys-lru # Restart Redis sudo systemctl restart redis-server sudo systemctl enable redis-server # Test connection redis-cli ping # Should return PONG redis-cli -a ping # Test with password ``` #### 1.5 Email Stack Installation (for Phase 2) ```bash # Install postfix and dovecot sudo apt install -y postfix dovecot-core dovecot-imapd # Install SpamAssassin sudo apt install -y spamassassin spamc # Install amavisd-new sudo apt install -y amavisd-new # Install OpenDKIM and opendmarc sudo apt install -y opendkim opendkim-tools sudo apt install -y opendmarc ``` **Note:** Detailed email configuration in Phase 2. #### 1.6 Logging Infrastructure ```bash # Create log directories sudo mkdir -p /var/log/tractatus sudo mkdir -p /var/quarantine/tractatus sudo mkdir -p /var/quarantine/email # Set permissions sudo chown -R tractatus:tractatus /var/log/tractatus sudo chown -R tractatus:tractatus /var/quarantine sudo chmod 750 /var/log/tractatus sudo chmod 750 /var/quarantine # Create security audit log sudo touch /var/log/tractatus/security-audit.log sudo chown tractatus:tractatus /var/log/tractatus/security-audit.log sudo chmod 640 /var/log/tractatus/security-audit.log ``` **Log Rotation Configuration** (`/etc/logrotate.d/tractatus`): ``` /var/log/tractatus/*.log { daily rotate 90 compress delaycompress notifempty create 0640 tractatus tractatus sharedscripts postrotate systemctl reload tractatus > /dev/null 2>&1 || true endscript } ``` #### 1.7 Communication Setup **ProtonMail Configuration:** 1. Create security team accounts: - `security@tractatus.digital` (primary security alerts) - `admin@tractatus.digital` (administrative notifications) 2. Configure custom domain integration 3. Set up email forwarding rules for critical alerts 4. Test email delivery to all team members **Signal Setup:** 1. Create "Tractatus Security Team" group 2. Add all team members with verified phone numbers 3. Document escalation protocol: - **Level 1 (Low)**: Email to security@tractatus.digital - **Level 2 (Medium)**: Email + Signal text notification - **Level 3 (High)**: Signal text + phone call - **Level 4 (Critical)**: Signal text + immediate video call 4. Test notification chain with dummy alert #### 1.8 Documentation Structure ```bash # Create security documentation directories mkdir -p /home/theflow/projects/tractatus/docs/security mkdir -p /home/theflow/projects/tractatus/docs/security/incidents mkdir -p /home/theflow/projects/tractatus/docs/security/audits mkdir -p /home/theflow/projects/tractatus/docs/security/configurations ``` **Create Initial Documents:** - `docs/security/SECURITY_POLICY.md` (security policies and procedures) - `docs/security/INCIDENT_RESPONSE.md` (incident response playbook) - `docs/security/ALERT_THRESHOLDS.md` (alert configuration and escalation) - `docs/security/TOOL_INVENTORY.md` (list of all security tools and versions) ### Testing & Verification **Phase 1 Success Criteria:** - [ ] ClamAV scanning functional (tested with EICAR) - [ ] YARA rules loading without errors - [ ] fail2ban service running - [ ] Redis operational with authentication - [ ] Log directories created with correct permissions - [ ] Email stack installed (configuration in Phase 2) - [ ] ProtonMail accounts configured and tested - [ ] Signal group created with all team members - [ ] Security documentation structure in place **Rollback Plan:** - All installations via apt can be removed with `apt purge` - Log directories can be safely deleted - No production impact in this phase --- ## Phase 2: File & Email Security Implementation **Duration:** 2-3 weeks **Effort:** 40-50 hours **Dependencies:** Phase 1 complete **Risk:** Medium ### Objectives 1. Implement file upload validation pipeline (inst_041) 2. Configure email security stack (inst_042) 3. Create quarantine management system 4. Establish baseline security logging ### Tasks #### 2.1 File Upload Validation Middleware (inst_041) **Create `src/utils/security-logger.js`:** ```javascript const fs = require('fs').promises; const path = require('path'); const SECURITY_LOG_PATH = '/var/log/tractatus/security-audit.log'; /** * Centralized security event logging * All security events must be logged here for audit trail */ async function logSecurityEvent(event) { const logEntry = { timestamp: new Date().toISOString(), event_type: event.type, source_ip: event.sourceIp || 'unknown', user_id: event.userId || 'anonymous', endpoint: event.endpoint || 'unknown', user_agent: event.userAgent || 'unknown', violation_details: event.details || {}, action_taken: event.action || 'logged', severity: event.severity || 'medium' }; const logLine = JSON.stringify(logEntry) + '\n'; try { await fs.appendFile(SECURITY_LOG_PATH, logLine, { encoding: 'utf-8' }); } catch (error) { console.error('[SECURITY LOGGER ERROR]', error); // Fallback to console if file logging fails console.error('[SECURITY EVENT]', logEntry); } } module.exports = { logSecurityEvent }; ``` **Create `src/middleware/file-security.middleware.js`:** ```javascript const multer = require('multer'); const { exec } = require('child_process'); const { promisify } = require('util'); const path = require('path'); const fs = require('fs').promises; const execAsync = promisify(exec); const { logSecurityEvent } = require('../utils/security-logger'); // File size limits (inst_041) const FILE_LIMITS = { document: 10 * 1024 * 1024, // 10MB media: 50 * 1024 * 1024, // 50MB default: 5 * 1024 * 1024 // 5MB }; // Quarantine directory const QUARANTINE_DIR = '/var/quarantine/tractatus'; // Allowed MIME types per category const ALLOWED_MIMES = { document: [ 'application/pdf', 'text/plain', 'text/markdown', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ], media: [ 'image/png', 'image/jpeg', 'image/gif', 'video/mp4', 'video/webm' ] }; /** * Validate file type using file(1) command * Detects MIME type mismatch (e.g., .jpg file that's actually .exe) */ async function validateFileType(filePath, expectedMime) { try { const { stdout } = await execAsync(`file --mime-type -b "${filePath}"`); const actualMime = stdout.trim(); if (actualMime !== expectedMime) { return { valid: false, reason: `MIME type mismatch: expected ${expectedMime}, got ${actualMime}` }; } return { valid: true }; } catch (error) { return { valid: false, reason: `File type validation failed: ${error.message}` }; } } /** * Scan file with ClamAV */ async function scanWithClamAV(filePath) { try { // Use clamdscan (daemon) for faster scanning const { stdout, stderr } = await execAsync(`clamdscan --no-summary "${filePath}"`); // ClamAV returns exit code 1 if virus found return { clean: true }; } catch (error) { // Check if it's a virus detection (not a system error) if (error.stdout && error.stdout.includes('FOUND')) { return { clean: false, threat: error.stdout.match(/: (.+) FOUND/)[1] }; } // System error - treat as suspicious return { clean: false, threat: 'ClamAV scan error', error: error.message }; } } /** * Scan file with YARA rules */ async function scanWithYARA(filePath) { try { const { stdout } = await execAsync( `yara /etc/yara/rules/tractatus-base.yar "${filePath}"` ); if (stdout.trim()) { // YARA rule matched - suspicious patterns detected return { clean: false, matches: stdout.trim().split('\n') }; } return { clean: true }; } catch (error) { // YARA returns exit code 1 if no matches (which is good) if (error.code === 1 && !error.stdout) { return { clean: true }; } return { clean: false, error: error.message }; } } /** * Quarantine suspicious file */ async function quarantineFile(filePath, reason, metadata) { const filename = path.basename(filePath); const timestamp = Date.now(); const quarantinePath = path.join(QUARANTINE_DIR, `${timestamp}_${filename}`); const metadataPath = `${quarantinePath}.json`; try { // Move file to quarantine await fs.rename(filePath, quarantinePath); // Write metadata await fs.writeFile( metadataPath, JSON.stringify({ original_name: filename, quarantine_time: new Date().toISOString(), reason, metadata }, null, 2) ); return quarantinePath; } catch (error) { console.error('[QUARANTINE ERROR]', error); throw error; } } /** * Main file security validation middleware */ function createFileSecurityMiddleware(fileCategory = 'default') { // Configure multer for temporary storage const upload = multer({ dest: '/tmp/tractatus-uploads', limits: { fileSize: FILE_LIMITS[fileCategory] || FILE_LIMITS.default } }); return [ upload.single('file'), async (req, res, next) => { if (!req.file) { return next(); // No file uploaded } const { originalname, mimetype, path: tempPath, size } = req.file; const clientIp = req.ip || req.connection.remoteAddress; try { // Step 1: File type validation const typeValidation = await validateFileType(tempPath, mimetype); if (!typeValidation.valid) { await logSecurityEvent({ type: 'file_upload_rejected', sourceIp: clientIp, userId: req.user?.id, endpoint: req.path, userAgent: req.get('user-agent'), details: { filename: originalname, reason: typeValidation.reason, size }, action: 'rejected', severity: 'medium' }); await fs.unlink(tempPath); // Delete temp file return res.status(400).json({ error: 'File validation failed', message: 'File type does not match extension' }); } // Step 2: ClamAV scan const clamavResult = await scanWithClamAV(tempPath); if (!clamavResult.clean) { const quarantinePath = await quarantineFile(tempPath, 'malware_detected', { threat: clamavResult.threat, scanner: 'ClamAV', originalname, clientIp }); await logSecurityEvent({ type: 'malware_detected', sourceIp: clientIp, userId: req.user?.id, endpoint: req.path, userAgent: req.get('user-agent'), details: { filename: originalname, threat: clamavResult.threat, quarantine_path: quarantinePath, size }, action: 'quarantined', severity: 'high' }); return res.status(400).json({ error: 'Security threat detected', message: 'File has been quarantined for manual review' }); } // Step 3: YARA scan const yaraResult = await scanWithYARA(tempPath); if (!yaraResult.clean) { const quarantinePath = await quarantineFile(tempPath, 'suspicious_patterns', { matches: yaraResult.matches, scanner: 'YARA', originalname, clientIp }); await logSecurityEvent({ type: 'suspicious_patterns_detected', sourceIp: clientIp, userId: req.user?.id, endpoint: req.path, userAgent: req.get('user-agent'), details: { filename: originalname, patterns: yaraResult.matches, quarantine_path: quarantinePath, size }, action: 'quarantined', severity: 'high' }); return res.status(400).json({ error: 'Suspicious patterns detected', message: 'File has been quarantined for manual review' }); } // All checks passed - file is safe await logSecurityEvent({ type: 'file_upload_accepted', sourceIp: clientIp, userId: req.user?.id, endpoint: req.path, userAgent: req.get('user-agent'), details: { filename: originalname, size, mimetype }, action: 'accepted', severity: 'low' }); // Attach validated file info to request req.validatedFile = { path: tempPath, originalname, mimetype, size }; next(); } catch (error) { console.error('[FILE SECURITY ERROR]', error); await logSecurityEvent({ type: 'file_validation_error', sourceIp: clientIp, userId: req.user?.id, endpoint: req.path, userAgent: req.get('user-agent'), details: { filename: originalname, error: error.message }, action: 'rejected', severity: 'high' }); // Clean up temp file try { await fs.unlink(tempPath); } catch (unlinkError) { console.error('[FILE CLEANUP ERROR]', unlinkError); } return res.status(500).json({ error: 'File validation failed', message: 'An error occurred during security validation' }); } } ]; } module.exports = { createFileSecurityMiddleware, validateFileType, scanWithClamAV, scanWithYARA }; ``` **Integration Example** (`src/routes/cases.routes.js`): ```javascript const { createFileSecurityMiddleware } = require('../middleware/file-security.middleware'); // Apply to case study submission endpoint router.post('/submit', createFileSecurityMiddleware('document'), // 10MB limit for documents casesController.submitCase ); ``` #### 2.2 Email Security Configuration (inst_042) **Detailed email configuration will go here** - this section is substantial and includes: - Postfix main.cf configuration - SpamAssassin custom rules for governance domain - amavisd-new integration with ClamAV - DKIM/SPF/DMARC setup - Dovecot configuration - Email quarantine management *Note: This section is extensive and would add significant length. Should I continue with full email configuration details, or would you prefer a summary approach for this phase?* #### 2.3 Testing & Verification **File Upload Testing:** ```bash # Test with clean file curl -X POST -F "file=@clean-document.pdf" http://localhost:9000/api/cases/submit # Test with EICAR malware sample curl -X POST -F "file=@eicar.txt" http://localhost:9000/api/cases/submit # Expected: Rejected with "malware detected", file quarantined # Test with MIME type mismatch cp malicious.exe fake-doc.pdf curl -X POST -F "file=@fake-doc.pdf" http://localhost:9000/api/cases/submit # Expected: Rejected with "MIME type mismatch" # Verify security logging tail -f /var/log/tractatus/security-audit.log # Check quarantine directory ls -lh /var/quarantine/tractatus/ ``` ### Phase 2 Success Criteria - [ ] File upload middleware deployed to all upload endpoints - [ ] ClamAV scanning functional with malware detection - [ ] YARA pattern matching operational - [ ] File quarantine system working - [ ] Security events logged to audit trail - [ ] Email stack configured (postfix, SpamAssassin, amavisd-new) - [ ] DKIM/SPF/DMARC validation operational - [ ] Email quarantine functional - [ ] 100% test coverage for file validation pipeline - [ ] No false positives with legitimate files **Rollback Plan:** - Remove middleware from routes - Restore previous upload handling - Email configuration changes documented for reversal --- ## Phase 3: Application Security (Input Validation & HTTP Headers) **Duration:** 1-2 weeks **Effort:** 30-40 hours **Dependencies:** Phase 1 complete (Phase 2 can run in parallel) **Risk:** Low-Medium ### Objectives 1. Implement form input sanitization (inst_043) 2. Deploy HTTP security headers (inst_044) 3. Add CSRF protection 4. Configure CSP violation reporting ### Tasks #### 3.1 Input Validation Middleware (inst_043) **Install Dependencies:** ```bash npm install dompurify validator jsdom csurf express-rate-limit ``` **Create `src/middleware/input-validation.middleware.js`:** ```javascript const createDOMPurify = require('dompurify'); const { JSDOM } = require('jsdom'); const validator = require('validator'); const { logSecurityEvent } = require('../utils/security-logger'); const window = new JSDOM('').window; const DOMPurify = createDOMPurify(window); // Input length limits per field type (inst_043) const LENGTH_LIMITS = { email: 254, url: 2048, phone: 20, name: 100, title: 200, description: 5000, case_study: 50000, default: 5000 }; // Safe HTML tags for markdown fields const SAFE_HTML_TAGS = ['p', 'br', 'strong', 'em', 'ul', 'ol', 'li', 'a', 'code', 'pre', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']; /** * Sanitize HTML using DOMPurify */ function sanitizeHTML(input, allowMarkdown = false) { if (typeof input !== 'string') return ''; const config = allowMarkdown ? { ALLOWED_TAGS: SAFE_HTML_TAGS, ALLOWED_ATTR: ['href'] } : { ALLOWED_TAGS: [], ALLOWED_ATTR: [] }; // Strip all HTML return DOMPurify.sanitize(input, config); } /** * Validate input against expected data type */ function validateDataType(value, type, fieldName) { const errors = []; switch (type) { case 'email': if (!validator.isEmail(value)) { errors.push(`${fieldName} must be a valid email address`); } break; case 'url': if (!validator.isURL(value, { require_protocol: true })) { errors.push(`${fieldName} must be a valid URL`); } break; case 'phone': if (!validator.isMobilePhone(value, 'any', { strictMode: false })) { errors.push(`${fieldName} must be a valid phone number`); } break; case 'numeric': if (!validator.isNumeric(value)) { errors.push(`${fieldName} must be numeric`); } break; case 'alphanumeric': if (!validator.isAlphanumeric(value, 'en-US', { ignore: ' -_' })) { errors.push(`${fieldName} must be alphanumeric`); } break; case 'text': // No additional validation for plain text break; default: errors.push(`Unknown data type for ${fieldName}`); } return errors; } /** * Check for NoSQL injection patterns */ function detectNoSQLInjection(value) { if (typeof value === 'object') { // Check for MongoDB operators const dangerousOps = ['$where', '$ne', '$gt', '$lt', '$regex', '$in', '$nin']; const keys = Object.keys(value); for (const key of keys) { if (dangerousOps.includes(key)) { return { detected: true, operator: key }; } // Recursive check if (typeof value[key] === 'object') { const result = detectNoSQLInjection(value[key]); if (result.detected) return result; } } } return { detected: false }; } /** * Check for XSS patterns */ function detectXSS(value) { if (typeof value !== 'string') return { detected: false }; const xssPatterns = [ /]*>.*?<\/script>/gi, /javascript:/gi, /on\w+\s*=/gi, // Event handlers: onclick=, onload=, etc. /