diff --git a/.github/workflows/gemini-review.yml b/.github/workflows/gemini-review.yml new file mode 100644 index 0000000..edfe400 --- /dev/null +++ b/.github/workflows/gemini-review.yml @@ -0,0 +1,188 @@ +name: '🔎 Gemini Review & Security Analysis' + +on: + pull_request: + types: + - 'opened' + issue_comment: + types: + - 'created' + +concurrency: + group: '${{ github.workflow }}-review-${{ github.event_name }}-${{ github.event.pull_request.number || github.event.issue.number }}' + cancel-in-progress: true + +defaults: + run: + shell: 'bash' + +jobs: + review: + if: | + (github.event_name == 'pull_request' && github.event.action == 'opened') || + (github.event_name == 'issue_comment' && github.event.comment.body == '@gemini-cli /review') + runs-on: 'ubuntu-latest' + timeout-minutes: 15 + permissions: + contents: 'read' + id-token: 'write' + issues: 'write' + pull-requests: 'write' + steps: + - name: 'Mint identity token' + id: 'mint_identity_token' + if: |- + ${{ vars.APP_ID }} + uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2 + with: + app-id: '${{ vars.APP_ID }}' + private-key: '${{ secrets.APP_PRIVATE_KEY }}' + permission-contents: 'read' + permission-issues: 'write' + permission-pull-requests: 'write' + + - name: 'Acknowledge request' + env: + GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' + ISSUE_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + MESSAGE: |- + 🤖 Hi @${{ github.actor }}, I've received your request, and I'm working on it now! You can track my progress [in the logs](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for more details. + REPOSITORY: '${{ github.repository }}' + run: |- + gh issue comment "${ISSUE_NUMBER}" \ + --body "${MESSAGE}" \ + --repo "${REPOSITORY}" + + - name: 'Checkout repository' + uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 + + - name: 'Run Gemini pull request review' + uses: 'google-github-actions/run-gemini-cli@main' # ratchet:exclude + id: 'gemini_pr_review' + env: + GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' + ISSUE_TITLE: '${{ github.event.pull_request.title || github.event.issue.title }}' + ISSUE_BODY: '${{ github.event.pull_request.body || github.event.issue.body }}' + PULL_REQUEST_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + REPOSITORY: '${{ github.repository }}' + with: + gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' + gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' + gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}' + gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' + gemini_api_key: '${{ secrets.GEMINI_API_KEY }}' + gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' + gemini_debug: '${{ fromJSON(vars.DEBUG || vars.ACTIONS_STEP_DEBUG || false) }}' + gemini_model: '${{ vars.GEMINI_MODEL }}' + google_api_key: '${{ secrets.GOOGLE_API_KEY }}' + use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' + use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + settings: |- + { + "model": { + "maxSessionTurns": 25 + }, + "telemetry": { + "enabled": ${{ vars.GOOGLE_CLOUD_PROJECT != '' }}, + "target": "gcp" + }, + "mcpServers": { + "github": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server" + ], + "includeTools": [ + "add_comment_to_pending_review", + "create_pending_pull_request_review", + "pull_request_read", + "submit_pending_pull_request_review" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" + } + } + }, + "tools": { + "core": [ + "run_shell_command(cat)", + "run_shell_command(echo)", + "run_shell_command(grep)", + "run_shell_command(head)", + "run_shell_command(tail)" + ] + } + } + prompt: '/gemini-review' + - name: 'Run Gemini security analysis review' + uses: 'google-github-actions/run-gemini-cli@main' # ratchet:exclude + id: 'gemini_security_analysis' + env: + GITHUB_TOKEN: '${{ steps.mint_identity_token.outputs.token || secrets.GITHUB_TOKEN || github.token }}' + ISSUE_TITLE: '${{ github.event.pull_request.title || github.event.issue.title }}' + ISSUE_BODY: '${{ github.event.pull_request.body || github.event.issue.body }}' + PULL_REQUEST_NUMBER: '${{ github.event.pull_request.number || github.event.issue.number }}' + REPOSITORY: '${{ github.repository }}' + with: + gcp_location: '${{ vars.GOOGLE_CLOUD_LOCATION }}' + gcp_project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}' + gcp_service_account: '${{ vars.SERVICE_ACCOUNT_EMAIL }}' + gcp_workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}' + gemini_api_key: '${{ secrets.GEMINI_API_KEY }}' + gemini_cli_version: '${{ vars.GEMINI_CLI_VERSION }}' + gemini_debug: '${{ fromJSON(vars.DEBUG || vars.ACTIONS_STEP_DEBUG || false) }}' + gemini_model: '${{ vars.GEMINI_MODEL }}' + google_api_key: '${{ secrets.GOOGLE_API_KEY }}' + use_gemini_code_assist: '${{ vars.GOOGLE_GENAI_USE_GCA }}' + use_vertex_ai: '${{ vars.GOOGLE_GENAI_USE_VERTEXAI }}' + extensions: | + [ + "https://github.com/gemini-cli-extensions/security.git" + ] + settings: |- + { + "model": { + "maxSessionTurns": 100 + }, + "telemetry": { + "enabled": ${{ vars.GOOGLE_CLOUD_PROJECT != '' }}, + "target": "gcp" + }, + "mcpServers": { + "github": { + "command": "docker", + "args": [ + "run", + "-i", + "--rm", + "-e", + "GITHUB_PERSONAL_ACCESS_TOKEN", + "ghcr.io/github/github-mcp-server" + ], + "includeTools": [ + "add_comment_to_pending_review", + "create_pending_pull_request_review", + "pull_request_read", + "submit_pending_pull_request_review" + ], + "env": { + "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" + } + } + }, + "tools": { + "core": [ + "run_shell_command(cat)", + "run_shell_command(echo)", + "run_shell_command(grep)", + "run_shell_command(head)", + "run_shell_command(tail)" + ] + } + } + prompt: '/security:analyze-github-pr' \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1eae0cf..81340bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +.env + dist/ -node_modules/ +node_modules/ \ No newline at end of file diff --git a/README.md b/README.md index 2458edf..6672a52 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,13 @@ By default, the `/security:analyze` command determines the scope of the analysis ## GitHub Integration -Coming soon! +Integrate the Gemini CLI Security Extension into your GitHub worklfow for analyzing incoming code can be done with the following: + +* Base your workflow off of the [google-github-actions/run-gemini-cli GitHub Action](https://github.com/google-github-actions/run-gemini-cli) +* Ensure that the [gemini-cli-extensions/security](https://github.com/gemini-cli-extensions/security) extension is passed to the action for installation. +* Pass `/security:analyze-github-pr` as the prompt to invoke the Security Extension! + +See this [Example Workflow](https://github.com/gemini-cli-extensions/security/blob/main/.github/workflows/github-review.yml) ## Benchmark diff --git a/commands/security/analyze-github-pr.toml b/commands/security/analyze-github-pr.toml index 41e2f7c..0dd6425 100644 --- a/commands/security/analyze-github-pr.toml +++ b/commands/security/analyze-github-pr.toml @@ -111,9 +111,9 @@ You will now begin executing the plan. The following are your precise instructio - Retrieve the GitHub repository name from the environment variable "${REPOSITORY}". - Retrieve the GitHub pull request number from the environment variable "${PULL_REQUEST_NUMBER}". - Retrieve the additional user instructions and context from the environment variable "${ADDITIONAL_CONTEXT}". - - Use `mcp__github__get_pull_request` to get the title, body, and metadata about the pull request. - - Use `mcp__github__get_pull_request_files` to get the list of files that were added, removed, and changed in the pull request. - - Use `mcp__github__get_pull_request_diff` to get the diff from the pull request. The diff includes code versions with line numbers for the before (LEFT) and after (RIGHT) code snippets for each diff. + - Use `pull_request_read.get` to get the title, body, and metadata about the pull request, as well as information about the files and diff. + - Use `pull_request_files.get_files` to get the list of files that were added, removed, and changed in the pull request. + - Use `pull_request_diff.get_diff` to get the diff from the pull request. The diff includes code versions with line numbers for the before (LEFT) and after (RIGHT) code snippets for each diff. * Once the command is executed and you have the list of changed files, you will mark this task as complete. @@ -129,9 +129,9 @@ After completing these two initial tasks, continue executing the dynamically gen After your **Core Operational Loop** is completed, report the final report back to GitHub: - 3.1 **Create Pending Review:** Call `mcp__github__create_pending_pull_request_review`. Ignore errors like "can only have one pending review per pull request" and proceed to the next step. + 3.1 **Create Pending Review:** Call `create_pending_pull_request_review`. Ignore errors like "can only have one pending review per pull request" and proceed to the next step. - 3.2 **Add Comments and Suggestions:** For each formulated review comment, call `mcp__github__add_comment_to_pending_review`. + 3.2 **Add Comments and Suggestions:** For each formulated review comment, call `add_comment_to_pending_review`. 2a. When there is a code suggestion (preferred), structure the comment payload using this exact template: @@ -149,7 +149,7 @@ After completing these two initial tasks, continue executing the dynamically gen {{SEVERITY}} {{COMMENT_TEXT}} - 3.3 **Submit Final Review:** Call `mcp__github__submit_pending_pull_request_review` with a summary comment. **DO NOT** approve the pull request. **DO NOT** request changes. The summary comment **MUST** use this exact markdown format: + 3.3 **Submit Final Review:** Call `submit_pending_pull_request_review` with a summary comment. **DO NOT** approve the pull request. **DO NOT** request changes. The summary comment **MUST** use this exact markdown format: ## 📋 Security Analysis Summary