From 28ca713b3849949b77ea9ee53a64b966dfb24742 Mon Sep 17 00:00:00 2001 From: EstbanIsLife Date: Sat, 6 Jun 2026 11:14:15 -0500 Subject: [PATCH] security: path traversal guard, MCP audit logging, chmod warning - Reject `..` in DatabaseConnection paths and enforce .db extension to prevent path traversal via initialize/open. - Log every MCP tool call to stderr (timestamp, pid, tool name) so unexpected invocations are visible in daemon logs. - Surface daemon socket chmod failure as a stderr warning instead of silently swallowing it. Co-Authored-By: Claude Sonnet 4.6 --- CHANGELOG.md | 6 ++++++ src/db/index.ts | 11 +++++++++++ src/mcp/daemon.ts | 4 +++- src/mcp/session.ts | 1 + 4 files changed, 21 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 57adbe59f..e40486cce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,12 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Security + +- Database paths with `..` traversal sequences are now rejected in `DatabaseConnection.initialize` and `DatabaseConnection.open`, and non-`.db` extensions are blocked, so a caller cannot be tricked into opening an arbitrary file as a SQLite database. +- Every MCP tool call is now logged to stderr with a timestamp, process PID, and tool name, making unexpected tool invocations visible in daemon logs. +- The daemon socket `chmod 0600` failure is now surfaced as a warning to stderr instead of being silently swallowed, so permission issues on shared filesystems don't go unnoticed. + ### New Features - Cross-file impact and blast-radius coverage now spans **all 22 supported languages and 14 web frameworks**, each validated on a real-world repo — see the new coverage table in the README. This release ships the cross-file resolution behind it, including Lua and Luau `require`, Shopify OS 2.0 Liquid section templates, Delphi form code-behind, Rust cross-module calls and Rocket route macros, Swift Fluent relationships, and the SvelteKit / Nuxt / Vapor / Axum route conventions. The residual everywhere is genuine static-analysis frontiers (runtime dispatch, reflection / DI, framework-convention entry points), never hidden. diff --git a/src/db/index.ts b/src/db/index.ts index cbc08b8f0..d2cf09f40 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -12,6 +12,15 @@ import { runMigrations, getCurrentVersion, CURRENT_SCHEMA_VERSION } from './migr export { SqliteDatabase, SqliteBackend } from './sqlite-adapter'; +function validateDbPath(dbPath: string): void { + if (dbPath.includes('..')) { + throw new Error(`Invalid database path: path traversal not allowed`); + } + if (path.extname(path.resolve(dbPath)) !== '.db') { + throw new Error(`Invalid database path: must have .db extension`); + } +} + /** * Apply connection-level PRAGMAs. Shared by `initialize` and `open` so the two * paths can't drift. @@ -54,6 +63,7 @@ export class DatabaseConnection { * Initialize a new database at the given path */ static initialize(dbPath: string): DatabaseConnection { + validateDbPath(dbPath); // Ensure parent directory exists const dir = path.dirname(dbPath); if (!fs.existsSync(dir)) { @@ -85,6 +95,7 @@ export class DatabaseConnection { * Open an existing database */ static open(dbPath: string): DatabaseConnection { + validateDbPath(dbPath); if (!fs.existsSync(dbPath)) { throw new Error(`Database not found: ${dbPath}`); } diff --git a/src/mcp/daemon.ts b/src/mcp/daemon.ts index d14373db4..f290b76f0 100644 --- a/src/mcp/daemon.ts +++ b/src/mcp/daemon.ts @@ -139,7 +139,9 @@ export class Daemon { // POSIX: tighten permissions to user-only — the socket lives under // `.codegraph/`, which is git-ignored but may be on a shared FS. if (process.platform !== 'win32') { - try { fs.chmodSync(this.socketPath, 0o600); } catch { /* best-effort */ } + try { fs.chmodSync(this.socketPath, 0o600); } catch (err) { + process.stderr.write(`[CodeGraph daemon] warning: failed to restrict socket permissions: ${err instanceof Error ? err.message : String(err)}\n`); + } } this.server = server; resolve(); diff --git a/src/mcp/session.ts b/src/mcp/session.ts index 3b83e24a5..f0449fa8d 100644 --- a/src/mcp/session.ts +++ b/src/mcp/session.ts @@ -227,6 +227,7 @@ export class MCPSession { await this.retryInitIfNeeded(); + process.stderr.write(`[CodeGraph MCP] ${new Date().toISOString()} tool_call pid=${process.pid} tool=${toolName}\n`); const result = await this.engine.getToolHandler().execute(toolName, toolArgs); this.transport.sendResult(request.id, result); }