const express = require('express'); const fs = require('fs'); const path = require('path'); const { requireAuth, requireAdmin } = require('../middleware/requireAuth'); const router = express.Router(); let pkg; try { pkg = require('../package.json'); } catch { pkg = { version: '0.1.0' }; } // Explicit allowlist of allowed files with resolved paths const ALLOWED_FILES = { 'FUTURE.md': path.resolve(__dirname, '..', 'FUTURE.md'), 'DEVELOPMENT_LOG.md': path.resolve(__dirname, '..', 'DEVELOPMENT_LOG.md'), }; /** * Redact sensitive information from file content * @param {string} content - The content to redact * @returns {string} - The redacted content */ function redactSensitiveContent(content) { if (!content) return content; return content // Redact internal IPs .replace(/\b192\.168\.[0-9]{1,3}\.[0-9]{1,3}\b/g, '[REDACTED]') .replace(/\b10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\b/g, '[REDACTED]') .replace(/\b172\.(1[6-9]|2[0-9]|3[0-1])\.[0-9]{1,3}\.[0-9]{1,3}\b/g, '[REDACTED]') // Redact passwords, api_keys, secrets .replace(/(password|api_key|secret)\s*=\s*[^\\\s]+/gi, '$1=[REDACTED]') // Redact file paths (Unix-style: /home/, /etc/, /var/, /tmp/, /usr/, /opt/) .replace(/\/(?:home|etc|var|tmp|usr|opt)\/[^\s"',;)]+/gi, '[REDACTED]') // Redact Windows-style paths .replace(/[A-Z]:\\(?:Users|Windows|Program Files)[\\\/][^\s"',;)]+/gi, '[REDACTED]') // Redact connection strings .replace(/(?:mongodb|postgres|mysql|redis|amqp):\/\/[^\s"']+/gi, '[REDACTED]') // Redact env var values (KEY=value patterns where key contains secret/pass/key/token) .replace(/([A-Z_]*(?:SECRET|KEY|TOKEN|PASS|PASSWORD|CREDENTIAL|AUTH)[A-Z_]*)\s*=\s*[^\s"']+/gi, '$1=[REDACTED]') // Redact internal URLs .replace(/https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0)(?::\d+)?[^\s"']*/gi, '[REDACTED_URL]') // Redact lines with security-sensitive patterns (CVE IDs, exploit details, attack vectors) .replace(/\bCVE-\d{4}-\d+\b/gi, '[REDACTED]') .replace(/\b(?:sql\s*injection|xss|csrf|csrf\s*token|race\s*condition|buffer\s*overflow|privilege\s*escalation)\b[^.]*\./gi, '[REDACTED_SECURITY_CONTENT].') .replace(/\bpassword\s*=\s*['"][^'"\s]+['"]/gi, 'password=[REDACTED]') } // Admin-only endpoint to serve FUTURE.md and DEVELOPMENT_LOG.md content router.get('/', requireAuth, requireAdmin, (req, res) => { try { // Read both files directly from the allowlist const futureContent = fs.readFileSync(ALLOWED_FILES['FUTURE.md'], 'utf-8'); const devLogContent = fs.readFileSync(ALLOWED_FILES['DEVELOPMENT_LOG.md'], 'utf-8'); // Redact sensitive information const sanitizedFutureContent = redactSensitiveContent(futureContent); const sanitizedDevLogContent = redactSensitiveContent(devLogContent); res.json({ version: pkg.version, future: sanitizedFutureContent, developmentLog: sanitizedDevLogContent }); } catch (err) { // Generic error message to prevent path disclosure console.error('[aboutAdmin] Error reading files'); res.status(500).json({ error: 'Failed to read project documentation files', code: 'FILE_READ_ERROR' }); } }); module.exports = router;