feat: implement secure file system access and path validation

- Introduced a restricted file system wrapper to ensure all file operations are confined to the script's directory, enhancing security.
- Updated various modules to utilize the new secure file system methods, replacing direct fs calls with validated operations.
- Enhanced path validation in the server routes and context loaders to prevent unauthorized access to the file system.
- Adjusted environment variable handling to use centralized methods for reading and writing API keys, ensuring consistent security practices.

This change improves the overall security posture of the application by enforcing strict file access controls and validating paths before any operations are performed.
This commit is contained in:
Test User
2025-12-31 18:03:01 -05:00
parent 847a8ff327
commit 2b89b0606c
21 changed files with 1577 additions and 2198 deletions

View File

@@ -10,8 +10,8 @@
import type { Request, Response, NextFunction } from 'express';
import crypto from 'crypto';
import fs from 'fs';
import path from 'path';
import * as secureFs from './secure-fs.js';
const DATA_DIR = process.env.DATA_DIR || './data';
const API_KEY_FILE = path.join(DATA_DIR, '.api-key');
@@ -41,8 +41,8 @@ setInterval(() => {
*/
function loadSessions(): void {
try {
if (fs.existsSync(SESSIONS_FILE)) {
const data = fs.readFileSync(SESSIONS_FILE, 'utf-8');
if (secureFs.existsSync(SESSIONS_FILE)) {
const data = secureFs.readFileSync(SESSIONS_FILE, 'utf-8') as string;
const sessions = JSON.parse(data) as Array<
[string, { createdAt: number; expiresAt: number }]
>;
@@ -74,12 +74,9 @@ function loadSessions(): void {
*/
async function saveSessions(): Promise<void> {
try {
await fs.promises.mkdir(path.dirname(SESSIONS_FILE), { recursive: true });
await secureFs.mkdir(path.dirname(SESSIONS_FILE), { recursive: true });
const sessions = Array.from(validSessions.entries());
await fs.promises.writeFile(SESSIONS_FILE, JSON.stringify(sessions), {
encoding: 'utf-8',
mode: 0o600,
});
await secureFs.writeFile(SESSIONS_FILE, JSON.stringify(sessions), 'utf-8');
} catch (error) {
console.error('[Auth] Failed to save sessions:', error);
}
@@ -101,8 +98,8 @@ function ensureApiKey(): string {
// Try to read from file
try {
if (fs.existsSync(API_KEY_FILE)) {
const key = fs.readFileSync(API_KEY_FILE, 'utf-8').trim();
if (secureFs.existsSync(API_KEY_FILE)) {
const key = (secureFs.readFileSync(API_KEY_FILE, 'utf-8') as string).trim();
if (key) {
console.log('[Auth] Loaded API key from file');
return key;
@@ -115,8 +112,8 @@ function ensureApiKey(): string {
// Generate new key
const newKey = crypto.randomUUID();
try {
fs.mkdirSync(path.dirname(API_KEY_FILE), { recursive: true });
fs.writeFileSync(API_KEY_FILE, newKey, { encoding: 'utf-8', mode: 0o600 });
secureFs.mkdirSync(path.dirname(API_KEY_FILE), { recursive: true });
secureFs.writeFileSync(API_KEY_FILE, newKey, { encoding: 'utf-8' });
console.log('[Auth] Generated new API key');
} catch (error) {
console.error('[Auth] Failed to save API key:', error);

View File

@@ -6,6 +6,7 @@
import { secureFs } from '@automaker/platform';
export const {
// Async methods
access,
readFile,
writeFile,
@@ -20,6 +21,16 @@ export const {
lstat,
joinPath,
resolvePath,
// Sync methods
existsSync,
readFileSync,
writeFileSync,
mkdirSync,
readdirSync,
statSync,
accessSync,
unlinkSync,
rmSync,
// Throttling configuration and monitoring
configureThrottling,
getThrottlingConfig,