security: Fix path traversal vulnerabilities

- Add filename validation to prevent path traversal
- Validate resolved paths are within expected directories
- Check for dangerous path characters (.., /, \)
This commit is contained in:
fr4iser
2026-02-28 20:40:13 +01:00
parent 4cb01fd482
commit 896c4fc520
2 changed files with 38 additions and 1 deletions

View File

@@ -259,7 +259,19 @@ function parseMemoryDir(dir, entries) {
try { try {
const files = fs.readdirSync(dir).filter(f => f.endsWith('.md')); const files = fs.readdirSync(dir).filter(f => f.endsWith('.md'));
for (const file of files) { for (const file of files) {
// Validate file name to prevent path traversal
if (file.includes('..') || file.includes('/') || file.includes('\\')) {
continue;
}
const filePath = path.join(dir, file); const filePath = path.join(dir, file);
// Additional validation: ensure resolved path is within the base directory
const resolvedPath = path.resolve(filePath);
const resolvedDir = path.resolve(dir);
if (!resolvedPath.startsWith(resolvedDir)) {
continue; // Path traversal attempt detected
}
const content = fs.readFileSync(filePath, 'utf-8'); const content = fs.readFileSync(filePath, 'utf-8');
if (!content.trim()) continue; if (!content.trim()) continue;

View File

@@ -7,7 +7,7 @@
import initSqlJs from 'sql.js'; import initSqlJs from 'sql.js';
import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs'; import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync, statSync } from 'fs';
import { dirname, join, basename } from 'path'; import { dirname, join, basename, resolve } from 'path';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { execSync } from 'child_process'; import { execSync } from 'child_process';
@@ -154,7 +154,19 @@ function countFilesAndLines(dir, ext = '.ts') {
try { try {
const entries = readdirSync(currentDir, { withFileTypes: true }); const entries = readdirSync(currentDir, { withFileTypes: true });
for (const entry of entries) { for (const entry of entries) {
// Validate entry name to prevent path traversal
if (entry.name.includes('..') || entry.name.includes('/') || entry.name.includes('\\')) {
continue;
}
const fullPath = join(currentDir, entry.name); const fullPath = join(currentDir, entry.name);
// Additional validation: ensure resolved path is within the base directory
const resolvedPath = resolve(fullPath);
const resolvedCurrentDir = resolve(currentDir);
if (!resolvedPath.startsWith(resolvedCurrentDir)) {
continue; // Path traversal attempt detected
}
if (entry.isDirectory() && !entry.name.includes('node_modules')) { if (entry.isDirectory() && !entry.name.includes('node_modules')) {
walk(fullPath); walk(fullPath);
} else if (entry.isFile() && entry.name.endsWith(ext)) { } else if (entry.isFile() && entry.name.endsWith(ext)) {
@@ -209,7 +221,20 @@ function calculateModuleProgress(moduleDir) {
* Check security file status * Check security file status
*/ */
function checkSecurityFile(filename, minLines = 100) { function checkSecurityFile(filename, minLines = 100) {
// Validate filename to prevent path traversal
if (filename.includes('..') || filename.includes('/') || filename.includes('\\')) {
return false;
}
const filePath = join(V3_DIR, '@claude-flow/security/src', filename); const filePath = join(V3_DIR, '@claude-flow/security/src', filename);
// Additional validation: ensure resolved path is within the expected directory
const resolvedPath = resolve(filePath);
const expectedDir = resolve(join(V3_DIR, '@claude-flow/security/src'));
if (!resolvedPath.startsWith(expectedDir)) {
return false; // Path traversal attempt detected
}
if (!existsSync(filePath)) return false; if (!existsSync(filePath)) return false;
try { try {