Skip to content

Commit 7d5dd4c

Browse files
authored
fix: remove dead try/catch in insertNode; fix SENSITIVE_PATHS case-sensitivity (colbymchenry#327)
Drop the no-op try/catch around insertNode.run, and lowercase the Windows SENSITIVE_PATHS entries so validateProjectPath's case-insensitive check actually blocks c:\windows. Adds a validateProjectPath test (POSIX + Windows-gated); the Windows-gated case was validated on a real Windows 11 VM. Closes colbymchenry#327
1 parent c9d2a25 commit 7d5dd4c

3 files changed

Lines changed: 54 additions & 28 deletions

File tree

__tests__/security.test.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
1212
import * as fs from 'fs';
1313
import * as path from 'path';
1414
import * as os from 'os';
15-
import { FileLock } from '../src/utils';
15+
import { FileLock, validateProjectPath } from '../src/utils';
1616
import CodeGraph from '../src/index';
1717
import { ToolHandler, tools } from '../src/mcp/tools';
1818
import { scanDirectory, isSourceFile } from '../src/extraction';
@@ -176,6 +176,36 @@ describe('Path Traversal Prevention', () => {
176176
});
177177
});
178178

179+
describe('validateProjectPath — sensitive directory blocking', () => {
180+
// POSIX-only: on Windows '/etc' resolves to C:\etc (non-existent), not a
181+
// sensitive dir — the Windows case is covered by the win32-gated test below.
182+
it.runIf(process.platform !== 'win32')('blocks POSIX system directories (exact match)', () => {
183+
expect(validateProjectPath('/')).toMatch(/sensitive system directory/i);
184+
expect(validateProjectPath('/etc')).toMatch(/sensitive system directory/i);
185+
});
186+
187+
it('allows a normal, existing directory', () => {
188+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-validate-'));
189+
try {
190+
expect(validateProjectPath(dir)).toBeNull();
191+
} finally {
192+
fs.rmSync(dir, { recursive: true, force: true });
193+
}
194+
});
195+
196+
// SENSITIVE_PATHS stores the Windows entries lowercase and validateProjectPath
197+
// matches via resolved.toLowerCase(), so 'C:\\Windows' and 'c:\\windows' are
198+
// both blocked. path.resolve is platform-specific, so this only runs on Windows.
199+
it.runIf(process.platform === 'win32')(
200+
'blocks Windows system directories regardless of case',
201+
() => {
202+
expect(validateProjectPath('C:\\Windows')).toMatch(/sensitive system directory/i);
203+
expect(validateProjectPath('c:\\windows')).toMatch(/sensitive system directory/i);
204+
expect(validateProjectPath('C:\\WINDOWS\\System32')).toMatch(/sensitive system directory/i);
205+
}
206+
);
207+
});
208+
179209
describe('MCP Input Validation', () => {
180210
let testDir: string;
181211
let cg: CodeGraph;

src/db/queries.ts

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -230,32 +230,28 @@ export class QueryBuilder {
230230
// deleteNode below).
231231
this.nodeCache.delete(node.id);
232232

233-
try {
234-
this.stmts.insertNode.run({
235-
id: node.id,
236-
kind: node.kind,
237-
name: node.name,
238-
qualifiedName: node.qualifiedName ?? node.name,
239-
filePath: node.filePath,
240-
language: node.language,
241-
startLine: node.startLine ?? 0,
242-
endLine: node.endLine ?? 0,
243-
startColumn: node.startColumn ?? 0,
244-
endColumn: node.endColumn ?? 0,
245-
docstring: node.docstring ?? null,
246-
signature: node.signature ?? null,
247-
visibility: node.visibility ?? null,
248-
isExported: node.isExported ? 1 : 0,
249-
isAsync: node.isAsync ? 1 : 0,
250-
isStatic: node.isStatic ? 1 : 0,
251-
isAbstract: node.isAbstract ? 1 : 0,
252-
decorators: node.decorators ? JSON.stringify(node.decorators) : null,
253-
typeParameters: node.typeParameters ? JSON.stringify(node.typeParameters) : null,
254-
updatedAt: node.updatedAt ?? Date.now(),
255-
});
256-
} catch (error) {
257-
throw error;
258-
}
233+
this.stmts.insertNode.run({
234+
id: node.id,
235+
kind: node.kind,
236+
name: node.name,
237+
qualifiedName: node.qualifiedName ?? node.name,
238+
filePath: node.filePath,
239+
language: node.language,
240+
startLine: node.startLine ?? 0,
241+
endLine: node.endLine ?? 0,
242+
startColumn: node.startColumn ?? 0,
243+
endColumn: node.endColumn ?? 0,
244+
docstring: node.docstring ?? null,
245+
signature: node.signature ?? null,
246+
visibility: node.visibility ?? null,
247+
isExported: node.isExported ? 1 : 0,
248+
isAsync: node.isAsync ? 1 : 0,
249+
isStatic: node.isStatic ? 1 : 0,
250+
isAbstract: node.isAbstract ? 1 : 0,
251+
decorators: node.decorators ? JSON.stringify(node.decorators) : null,
252+
typeParameters: node.typeParameters ? JSON.stringify(node.typeParameters) : null,
253+
updatedAt: node.updatedAt ?? Date.now(),
254+
});
259255
}
260256

261257
/**

src/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import * as path from 'path';
4343
const SENSITIVE_PATHS = new Set([
4444
'/', '/etc', '/usr', '/bin', '/sbin', '/var', '/tmp', '/dev', '/proc', '/sys',
4545
'/root', '/boot', '/lib', '/lib64', '/opt',
46-
'C:\\', 'C:\\Windows', 'C:\\Windows\\System32',
46+
'c:\\', 'c:\\windows', 'c:\\windows\\system32',
4747
]);
4848

4949
/**

0 commit comments

Comments
 (0)