Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion commands/security/analyze.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 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:**
Expand Down
40 changes: 37 additions & 3 deletions mcp-server/src/filesystem.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,58 @@ 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');
fs.writeFileSync('test.txt', 'hello');
execSync('git add .');
execSync('git commit -m "initial commit"');
});

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');
});

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', () => {
it('should return a diff of the current changes when no branches or commits are specified', () => {
fs.writeFileSync('test.txt', 'hello world');
// Defaults to 'git diff' with remote removed
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');
// 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');
});
});
23 changes: 19 additions & 4 deletions mcp-server/src/filesystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,28 @@ export const isGitHubRepository = (): boolean => {
};

/**
* Gets a changelist of the repository
* 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.
* @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();
Expand Down
13 changes: 8 additions & 5 deletions mcp-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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").'),
} as any,
((args: { base?: string; head?: string }) => {
const diff = getAuditScope(args.base, args.head);
return {
content: [
{
Expand All @@ -49,7 +52,7 @@ server.tool(
},
],
};
}
}) as any
);

server.tool(
Expand Down
Loading