diff --git a/CHANGELOG.md b/CHANGELOG.md index 382cec9c..150f7a65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,12 +5,15 @@ a [GitHub Release](https://github.com/colbymchenry/codegraph/releases) tagged `vX.Y.Z`, which is where most people will look. This project follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) -and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +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. - Closed a path-traversal hole where a symbolic link inside an indexed project that pointed *outside* the project root could make CodeGraph serve that out-of-root file's contents (for example a file under your home directory) to the AI agent. CodeGraph now resolves symlinks when validating file access and refuses to read anything whose real location is outside the project, while still allowing symlinks that stay within it. Thanks @sulthonzh. (#527) - CodeGraph now indexes Spring configuration files (`application.properties` / `application.yml`) by key only, and never includes their values in `codegraph_explore` or `codegraph_node` output. Previously a secret committed to one of these files — a database password, API key, or connection string with embedded credentials — could be surfaced to an AI agent that asked about nearby code, even though the agent never opened the file. The configuration keys are still indexed, so reference and impact analysis are unaffected; an agent that genuinely needs a value reads the file itself. Shopify Liquid `{% schema %}` blocks are likewise indexed by name only. (#383) diff --git a/src/db/index.ts b/src/db/index.ts index e6d52d47..f073c25c 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -13,6 +13,15 @@ import { getCodeGraphDir } from '../directory'; 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. @@ -55,6 +64,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)) { @@ -86,6 +96,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 df9a7d82..ce0aa0d1 100644 --- a/src/mcp/daemon.ts +++ b/src/mcp/daemon.ts @@ -177,7 +177,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 3b83e24a..f0449fa8 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); }