From 68b42e4d256276032e4d941f81ebc24745d0926b Mon Sep 17 00:00:00 2001 From: Janet Vu Date: Wed, 28 Jan 2026 19:01:18 +0000 Subject: [PATCH 1/6] Expands ability to customize audit scope to compare branches or commit hashes --- commands/security/analyze.toml | 3 ++- mcp-server/src/filesystem.ts | 22 ++++++++++++++++++---- mcp-server/src/index.ts | 11 +++++++---- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/commands/security/analyze.toml b/commands/security/analyze.toml index cde5ded..ab1c2ab 100644 --- a/commands/security/analyze.toml +++ b/commands/security/analyze.toml @@ -108,7 +108,8 @@ Your first action is to create a `SECURITY_ANALYSIS_TODO.md` file with the follo You will now begin executing the plan. The following are your precise instructions to start with. 1. **To complete the 'Define the audit scope' task:** - * You **MUST** use the `get_audit_scope` tool and nothing else to get a list of changed files to perform a security scan on. + * Identify if the user specified specific branches to compare (e.g. "compare main and dev"). + * You **MUST** use the `get_audit_scope` tool. Pass the branch names as arguments if the user provided them; otherwise call it with no arguments to scan the current changes. * After using the tool, provide the user a list of changed files. If the list of files is empty, ask the user to provide files to be scanned. 2. **Immediately after defining the scope, you must refine your plan:** diff --git a/mcp-server/src/filesystem.ts b/mcp-server/src/filesystem.ts index d3850a1..ee00f05 100644 --- a/mcp-server/src/filesystem.ts +++ b/mcp-server/src/filesystem.ts @@ -27,13 +27,27 @@ export const isGitHubRepository = (): boolean => { }; /** - * Gets a changelist of the repository + * Gets a changelist of the repository between two commits. + * If no commits are provided, it gets the changelist of the working directory. + * @param base The base commit branch or hash. + * @param head The head commit branch or hash. + * @returns The changelist as a string. */ -export function getAuditScope(): string { - let command = isGitHubRepository() ? 'git diff --merge-base origin/HEAD' : 'git diff'; +export function getAuditScope(base?: string, head?: string): string { + // Default to working directory diff if no commits are provided + const args: string[] = ["diff"]; + + // Add commit range if both base and head are provided + if (base !== undefined && head !== undefined) { + args.push(base, head); + } + // Otherwise, if this is a GitHub repository, use origin/HEAD as the base + else if (isGitHubRepository()) { + args.push('--merge-base', 'origin/HEAD'); + } try { const diff = ( - spawnSync('git', command.split(' ').slice(1), { + spawnSync('git', args, { encoding: 'utf-8', }).stdout || '' ).trim(); diff --git a/mcp-server/src/index.ts b/mcp-server/src/index.ts index 4e85832..ab8b264 100644 --- a/mcp-server/src/index.ts +++ b/mcp-server/src/index.ts @@ -37,10 +37,13 @@ server.tool( server.tool( 'get_audit_scope', - 'Checks if the current directory is a GitHub repository.', - {}, - () => { - const diff = getAuditScope(); + 'Gets the git diff of the current changes. Can optionally compare two specific branches.', + { + base: z.string().optional().describe('The base branch or commit hash (e.g., "main").'), + head: z.string().optional().describe('The head branch or commit hash (e.g., "feature-branch").'), + }, + (args: { base?: string; head?: string }) => { + const diff = getAuditScope(args.base, args.head); return { content: [ { From 9ae6e27262c7b8ad385cc32aa878320d5724a6f6 Mon Sep 17 00:00:00 2001 From: Janet Vu Date: Wed, 28 Jan 2026 19:13:12 +0000 Subject: [PATCH 2/6] re-add missing sentence, clarify jsdoc string --- commands/security/analyze.toml | 2 +- mcp-server/src/filesystem.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/commands/security/analyze.toml b/commands/security/analyze.toml index ab1c2ab..d8f1eab 100644 --- a/commands/security/analyze.toml +++ b/commands/security/analyze.toml @@ -109,7 +109,7 @@ You will now begin executing the plan. The following are your precise instructio 1. **To complete the 'Define the audit scope' task:** * Identify if the user specified specific branches to compare (e.g. "compare main and dev"). - * You **MUST** use the `get_audit_scope` tool. Pass the branch names as arguments if the user provided them; otherwise call it with no arguments to scan the current changes. + * You **MUST** use the `get_audit_scope` tool and nothing else to get a list of changed files to perform a security scan on. Pass the branch names as arguments if the user provided them; otherwise call it with no arguments to scan the current changes. * After using the tool, provide the user a list of changed files. If the list of files is empty, ask the user to provide files to be scanned. 2. **Immediately after defining the scope, you must refine your plan:** diff --git a/mcp-server/src/filesystem.ts b/mcp-server/src/filesystem.ts index ee00f05..af51c3f 100644 --- a/mcp-server/src/filesystem.ts +++ b/mcp-server/src/filesystem.ts @@ -28,6 +28,7 @@ export const isGitHubRepository = (): boolean => { /** * Gets a changelist of the repository between two commits. + * Can compare between two commits, or get the diff of the working directory. * If no commits are provided, it gets the changelist of the working directory. * @param base The base commit branch or hash. * @param head The head commit branch or hash. From 4cada3fea6995e6d567ce379b490d337712dbde4 Mon Sep 17 00:00:00 2001 From: Janet Vu Date: Wed, 28 Jan 2026 20:39:50 +0000 Subject: [PATCH 3/6] add filesystem getauditscope tests for specified branches --- mcp-server/src/filesystem.test.ts | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/mcp-server/src/filesystem.test.ts b/mcp-server/src/filesystem.test.ts index 7af19d5..584bc5f 100644 --- a/mcp-server/src/filesystem.test.ts +++ b/mcp-server/src/filesystem.test.ts @@ -19,7 +19,9 @@ describe('filesystem', () => { }); afterAll(() => { - fs.unlinkSync('test.txt'); + // Cleanup created files and git repository if they exist for all tests + if (fs.existsSync('test.txt')) fs.unlinkSync('test.txt'); + if (fs.existsSync('branch-test.txt')) fs.unlinkSync('branch-test.txt'); execSync('rm -rf .git'); }); @@ -27,9 +29,34 @@ describe('filesystem', () => { expect(isGitHubRepository()).toBe(true); }); - it('should return a diff of the current changes', () => { + it('should return a diff of the current changes when no branches or commits are specified', () => { fs.writeFileSync('test.txt', 'hello world'); const diff = getAuditScope(); expect(diff).toContain('hello world'); }); + + it('should return a diff between two specific branches', () => { + // 1. Base branch with specific content + execSync('git checkout -b pre'); + fs.writeFileSync('branch-test.txt', 'pre content'); + execSync('git add .'); + execSync('git commit -m "pre branch commit"'); + + // 2. Head branch with the content modified + execSync('git checkout -b post'); + fs.writeFileSync('branch-test.txt', 'post content'); + execSync('git add .'); + execSync('git commit -m "post branch commit"'); + + // 3. Compare them using the new arguments + const diff = getAuditScope('pre', 'post'); + + // 4. Verify the diff output + expect(diff).toContain('diff --git a/branch-test.txt b/branch-test.txt'); + expect(diff).toContain('-base content'); + expect(diff).toContain('+head content'); + + // Cleanup by switching back to the main, so other tests aren't affected + execSync('git checkout master || git checkout main'); + }); }); From 5e025a13a3dc747b17b8d853ef5d1297b0788cea Mon Sep 17 00:00:00 2001 From: Janet Vu Date: Thu, 29 Jan 2026 18:32:21 +0000 Subject: [PATCH 4/6] align tool definitions typing with other tool defs --- mcp-server/src/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mcp-server/src/index.ts b/mcp-server/src/index.ts index ab8b264..2ebd816 100644 --- a/mcp-server/src/index.ts +++ b/mcp-server/src/index.ts @@ -41,8 +41,8 @@ server.tool( { base: z.string().optional().describe('The base branch or commit hash (e.g., "main").'), head: z.string().optional().describe('The head branch or commit hash (e.g., "feature-branch").'), - }, - (args: { base?: string; head?: string }) => { + } as any, + ((args: { base?: string; head?: string }) => { const diff = getAuditScope(args.base, args.head); return { content: [ @@ -52,7 +52,7 @@ server.tool( }, ], }; - } + }) as any ); server.tool( From b3db47a48e36db8d56a0dc94e86791bc19dbfff5 Mon Sep 17 00:00:00 2001 From: Janet Vu Date: Thu, 29 Jan 2026 19:03:46 +0000 Subject: [PATCH 5/6] fix test --- mcp-server/src/filesystem.test.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/mcp-server/src/filesystem.test.ts b/mcp-server/src/filesystem.test.ts index 584bc5f..4834839 100644 --- a/mcp-server/src/filesystem.test.ts +++ b/mcp-server/src/filesystem.test.ts @@ -12,7 +12,7 @@ import * as fs from 'fs'; describe('filesystem', () => { beforeAll(() => { execSync('git init'); - execSync('git remote add origin https://github.com/gemini-testing/gemini-test-repo.git'); + // REMOVED: git remote add origin ... (Moved to the specific test case below) fs.writeFileSync('test.txt', 'hello'); execSync('git add .'); execSync('git commit -m "initial commit"'); @@ -26,11 +26,18 @@ describe('filesystem', () => { }); it('should return true if the directory is a github repository', () => { + // Setup: Add remote specifically for this test + execSync('git remote add origin https://github.com/gemini-testing/gemini-test-repo.git'); + expect(isGitHubRepository()).toBe(true); + + // Cleanup: Remove remote so it doesn't affect other tests + execSync('git remote remove origin'); }); it('should return a diff of the current changes when no branches or commits are specified', () => { fs.writeFileSync('test.txt', 'hello world'); + // Since we removed the remote, this now defaults to 'git diff', which works locally const diff = getAuditScope(); expect(diff).toContain('hello world'); }); @@ -53,8 +60,9 @@ describe('filesystem', () => { // 4. Verify the diff output expect(diff).toContain('diff --git a/branch-test.txt b/branch-test.txt'); - expect(diff).toContain('-base content'); - expect(diff).toContain('+head content'); + // FIXED: Updated expectations to match the actual file content + expect(diff).toContain('-pre content'); + expect(diff).toContain('+post content'); // Cleanup by switching back to the main, so other tests aren't affected execSync('git checkout master || git checkout main'); From 1d76e601904da6d83c4fda35324cf0372cd9e06f Mon Sep 17 00:00:00 2001 From: Janet Vu Date: Thu, 29 Jan 2026 19:05:55 +0000 Subject: [PATCH 6/6] final touches --- mcp-server/src/filesystem.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mcp-server/src/filesystem.test.ts b/mcp-server/src/filesystem.test.ts index 4834839..c3cd1e1 100644 --- a/mcp-server/src/filesystem.test.ts +++ b/mcp-server/src/filesystem.test.ts @@ -12,7 +12,6 @@ import * as fs from 'fs'; describe('filesystem', () => { beforeAll(() => { execSync('git init'); - // REMOVED: git remote add origin ... (Moved to the specific test case below) fs.writeFileSync('test.txt', 'hello'); execSync('git add .'); execSync('git commit -m "initial commit"'); @@ -37,7 +36,7 @@ describe('filesystem', () => { it('should return a diff of the current changes when no branches or commits are specified', () => { fs.writeFileSync('test.txt', 'hello world'); - // Since we removed the remote, this now defaults to 'git diff', which works locally + // Defaults to 'git diff' with remote removed const diff = getAuditScope(); expect(diff).toContain('hello world'); });