85 lines
2.7 KiB
JavaScript
85 lines
2.7 KiB
JavaScript
|
|
const express = require('express');
|
||
|
|
const fs = require('fs');
|
||
|
|
const path = require('path');
|
||
|
|
const { requireAuth, requireAdmin } = require('../middleware/requireAuth');
|
||
|
|
|
||
|
|
const router = express.Router();
|
||
|
|
|
||
|
|
// Base directory for path validation
|
||
|
|
const BASE_DIR = path.resolve(__dirname, '..');
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Sanitize file paths to prevent path traversal attacks
|
||
|
|
* @param {string} filePath - The file path to sanitize
|
||
|
|
* @returns {string|null} - The sanitized absolute path or null if invalid
|
||
|
|
*/
|
||
|
|
function sanitizePath(filePath) {
|
||
|
|
try {
|
||
|
|
// Resolve the absolute path
|
||
|
|
const resolvedPath = path.resolve(BASE_DIR, filePath);
|
||
|
|
|
||
|
|
// Check if the resolved path is within the project directory
|
||
|
|
if (!resolvedPath.startsWith(BASE_DIR)) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return resolvedPath;
|
||
|
|
} catch (err) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 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]');
|
||
|
|
}
|
||
|
|
|
||
|
|
// Admin-only endpoint to serve FUTURE.md and DEVELOPMENT_LOG.md content
|
||
|
|
router.get('/', requireAuth, requireAdmin, (req, res) => {
|
||
|
|
try {
|
||
|
|
// Sanitize paths to prevent path traversal
|
||
|
|
const futureMdPath = sanitizePath('FUTURE.md');
|
||
|
|
const devLogMdPath = sanitizePath('DEVELOPMENT_LOG.md');
|
||
|
|
|
||
|
|
// Check if paths are valid
|
||
|
|
if (!futureMdPath || !devLogMdPath) {
|
||
|
|
return res.status(400).json({
|
||
|
|
error: 'Invalid file path',
|
||
|
|
code: 'INVALID_FILE_PATH'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
const futureContent = fs.readFileSync(futureMdPath, 'utf-8');
|
||
|
|
const devLogContent = fs.readFileSync(devLogMdPath, 'utf-8');
|
||
|
|
|
||
|
|
// Redact sensitive information
|
||
|
|
const sanitizedFutureContent = redactSensitiveContent(futureContent);
|
||
|
|
const sanitizedDevLogContent = redactSensitiveContent(devLogContent);
|
||
|
|
|
||
|
|
res.json({
|
||
|
|
future: sanitizedFutureContent,
|
||
|
|
developmentLog: sanitizedDevLogContent
|
||
|
|
});
|
||
|
|
} catch (err) {
|
||
|
|
// Sanitize error message to prevent path disclosure
|
||
|
|
console.error('[aboutAdmin] Error reading files:', err.message.replace(BASE_DIR, '[REDACTED]'));
|
||
|
|
res.status(500).json({
|
||
|
|
error: 'Failed to read project documentation files',
|
||
|
|
code: 'FILE_READ_ERROR'
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
module.exports = router;
|