Skip to content

feat: add Gerrit integration connector for code review workflows#1349

Open
jeremyeder wants to merge 18 commits intoambient-code:mainfrom
jeremyeder:001-gerrit-integration
Open

feat: add Gerrit integration connector for code review workflows#1349
jeremyeder wants to merge 18 commits intoambient-code:mainfrom
jeremyeder:001-gerrit-integration

Conversation

@jeremyeder
Copy link
Copy Markdown
Contributor

@jeremyeder jeremyeder commented Apr 17, 2026

Summary

  • Reimplements PR feat: add Gerrit integration connector for code review workflows #1078's Gerrit integration design using current harness conventions
  • Multi-instance Gerrit support with HTTP Basic and gitcookies auth methods
  • SSRF protection with DNS rebinding prevention, private/reserved IP blocking
  • Per-user K8s Secret storage with conflict retry handling
  • Dynamic MCP server injection (only when credentials are configured — not in .mcp.json)
  • Feature flag gerrit.enabled with scope:workspace tag
  • Full-stack: backend handlers, frontend connection card, runner credential fetch + MCP config generation

Supersedes #1078

Test plan

  • cd components/backend && make test — backend compiles and vets clean
  • cd components/frontend && npm run build — zero errors, zero warnings
  • cd components/runners/ambient-runner && python -m pytest tests/ — runner tests pass
  • make lint — all pre-commit hooks pass
  • Gerrit connect/disconnect/status/list endpoints work
  • SSRF protection rejects private IPs, loopback, metadata endpoints
  • Mixed auth credentials are rejected
  • Frontend card renders, test/connect/disconnect flows work
  • MCP server only injected when credentials exist (not present by default)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Gerrit integration connector with UI card for multi-instance management (HTTP Basic & gitcookies), connect/disconnect, and per-instance status.
    • Test-connection flow to validate credentials before saving.
    • Agent/runtime injection of Gerrit credentials and generated MCP config for sessions.
  • Bug Fixes / Security

    • Server- and client-side URL/SSRF protections and stricter validation for Gerrit endpoints.
  • Chores

    • Added workspace-scoped feature flag to gate Gerrit support.

Ambient Code Bot and others added 3 commits April 17, 2026 00:28
Spec, plan, and tasks extracted from PR ambient-code#1078's design, targeting
reimplementation with current harness conventions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Multi-instance Gerrit support with HTTP Basic and gitcookies auth,
SSRF protection, per-user K8s Secret storage, and dynamic MCP server
injection when credentials are configured.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…comments

- Hoist blocked CIDR ranges to package-level var (avoid per-call alloc)
- Add gerritAuthHTTPBasic/gerritAuthGitCookies constants
- Use Create return value directly instead of re-fetching Secret
- Remove WHAT comments, keep only non-obvious WHY

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 17, 2026

Deploy Preview for cheerful-kitten-f556a0 canceled.

Name Link
🔨 Latest commit 2ac05ed
🔍 Latest deploy log https://app.netlify.com/projects/cheerful-kitten-f556a0/deploys/69e68af82a29d40008d282a9

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 17, 2026

📝 Walkthrough

Walkthrough

Adds a Gerrit integration connector behind gerrit.enabled: backend handlers for connect/test/list/status/disconnect with SSRF-safe URL and token validation, per-user Kubernetes Secret storage, frontend UI and proxy routes for multi-instance management, and runner-side credential fetching with MCP config generation and gitcookies aggregation.

Changes

Cohort / File(s) Summary
Specification & Planning
.specify/specs/gerrit-integration/spec.md, .../plan.md, .../tasks.md
New spec, implementation plan, and task list defining user stories, SSRF/HTTPS constraints, validation, MCP config generation, rollout, and tests.
Backend Handlers & Validation
components/backend/handlers/gerrit_auth.go, components/backend/handlers/integration_validation.go
New handlers: Connect/Test/Disconnect/List/Status; HTTPS+hostname resolution and private-IP blocking; DNS-rebinding/SSRF-safe transport; ValidateGerritToken via /a/accounts/self; Kubernetes Secret CRUD with retry-on-conflict; gitcookies parsing.
Integrations Status & Runtime Credentials
components/backend/handlers/integrations_status.go, components/backend/handlers/runtime_credentials.go
Adds Gerrit entry to unified integrations status and GetGerritCredentialsForSession endpoint returning per-instance creds for agentic sessions with RBAC enforcement and appropriate error semantics.
Backend Routes
components/backend/routes.go
Registers new routes: /api/auth/gerrit/connect, /test, /instances, /:instanceName/status, /:instanceName/disconnect, and session runtime credentials route.
Frontend API Proxy Routes
components/frontend/src/app/api/auth/gerrit/connect/route.ts, test/route.ts, instances/route.ts, [instanceName]/status/route.ts, [instanceName]/disconnect/route.ts
Next.js proxy endpoints forwarding requests to backend, propagating headers; test endpoint uses 15s timeout.
Frontend UI & Components
components/frontend/src/components/gerrit-connection-card.tsx, components/frontend/src/app/integrations/IntegrationsClient.tsx
New GerritConnectionCard: multi-instance UI, connect/disconnect flows, auth-method-specific fields (http_basic or git_cookies), client-side validation, test connection UX, and integration into Integrations grid.
Frontend Services & Queries
components/frontend/src/services/api/gerrit-auth.ts, components/frontend/src/services/queries/use-gerrit.ts, components/frontend/src/services/api/integrations.ts
Typed API client and discriminated union request types, React Query hooks for instances/connect/disconnect/test, and IntegrationsStatus extended with gerrit field.
Feature Flag
components/manifests/base/core/flags.json
Added workspace-scoped gerrit.enabled feature flag.
Runner / MCP Integration
components/runners/ambient-runner/.../bridges/claude/mcp.py, components/runners/ambient-runner/.../platform/auth.py, components/runners/ambient-runner/.../platform/__init__.py
Fetches Gerrit creds, generates /tmp/gerrit-mcp/gerrit_config.json, aggregates .gitcookies, sets/clears GERRIT_CONFIG_PATH, registers gerrit MCP server, and handles permission vs network error semantics.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Frontend
    participant FrontendAPI
    participant BackendAPI
    participant Validator
    participant K8sSecret

    User->>Frontend: Enter instance name, URL, auth
    Frontend->>FrontendAPI: POST /api/auth/gerrit/test
    FrontendAPI->>BackendAPI: Forward /auth/gerrit/test
    BackendAPI->>Validator: ValidateGerritToken (SSRF checks, /a/accounts/self)
    Validator-->>BackendAPI: valid true/false
    BackendAPI-->>FrontendAPI: { valid, message }
    FrontendAPI-->>Frontend: Response
    User->>Frontend: POST Save Instance
    Frontend->>FrontendAPI: POST /api/auth/gerrit/connect
    FrontendAPI->>BackendAPI: Forward /auth/gerrit/connect
    BackendAPI->>Validator: ValidateGerritToken
    BackendAPI->>K8sSecret: Store credentials (retry-on-conflict)
    K8sSecret-->>BackendAPI: Stored
    BackendAPI-->>FrontendAPI: 200
    FrontendAPI-->>Frontend: Instance connected
Loading
sequenceDiagram
    participant Session
    participant BackendAPI
    participant K8sSecret
    participant Runner
    participant MCPServer

    Session->>BackendAPI: GET /agentic-sessions/:session/credentials/gerrit
    BackendAPI->>K8sSecret: listGerritCredentials for user
    K8sSecret-->>BackendAPI: instances[]
    BackendAPI-->>Session: instances[]
    Session->>Runner: start with instances[]
    Runner->>Runner: generate_gerrit_config(instances)
    alt any git_cookies
        Runner->>Runner: write combined .gitcookies file
    end
    Runner->>Runner: set GERRIT_CONFIG_PATH and register MCP entry
    Runner->>MCPServer: start with GERRIT_CONFIG_PATH
    MCPServer-->>Runner: ready
Loading

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 2 warnings)

Check name Status Explanation Resolution
Security And Secret Handling ❌ Error PR contains three critical security violations: unauthenticated credential test endpoint, missing OwnerReferences on K8s Secrets, and plaintext credentials in API responses. Add auth checks to TestGerritConnection, implement OwnerReferences for Secrets garbage collection, and sanitize credential fields in responses or add encryption.
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Kubernetes Resource Safety ⚠️ Warning Gerrit Secrets created without OwnerReferences violate K8s resource safety requirement for parent ownership and cleanup. Add OwnerReferences to Gerrit Secrets in storeGerritCredentials() via user-scoped CR ownership, garbage collection, or documented cleanup logic.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title follows Conventional Commits format with 'feat' type and clear scope/description. It accurately summarizes the main change: adding Gerrit integration.
Performance And Algorithmic Complexity ✅ Passed Code demonstrates sound performance practices with no O(n²) algorithms, N+1 patterns, or unbounded growth; K8s Secret access is single-call-per-user, regex compiled once at init, parsing is linear, React Query cache invalidation proper.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
✨ Simplify code
  • Create PR with simplified code

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
components/frontend/src/components/gerrit-connection-card.tsx (2)

21-34: Unused status prop.

The status prop is defined in Props type but not destructured or used in the component. Either remove it from the type or wire it up (e.g., for initial state before useGerritInstances loads).

♻️ Remove unused prop
 type Props = {
-  status?: {
-    connected: boolean
-    instances?: Array<{
-      instanceName: string
-      url: string
-      authMethod: string
-      connected: boolean
-    }>
-  }
   onRefresh?: () => void
 }

-export function GerritConnectionCard({ onRefresh }: Props) {
+export function GerritConnectionCard({ onRefresh }: Props) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/frontend/src/components/gerrit-connection-card.tsx` around lines
21 - 34, The Props type declares a status prop that GerritConnectionCard doesn't
use; either remove status from Props or wire it into the component: update the
Props type to drop status if not needed, or destructure status in
GerritConnectionCard and use it to seed local state or render initial UI (e.g.,
before useGerritInstances loads) and replace any hardcoded/empty initial state
with status.instances and status.connected; ensure references to
status.instanceName/url/authMethod/connected align with existing
useGerritInstances usage so behavior and types remain consistent.

95-113: Consider validating auth-specific fields before test.

handleTest only checks !url but doesn't validate that required auth fields are filled. User could click "Test Connection" with empty username/token and get a less helpful error from the backend.

♻️ Add auth field validation to handleTest
   const handleTest = () => {
     if (!url) {
       toast.error('Please enter a Gerrit URL')
       return
     }
+    if (authMethod === 'http_basic' && (!username || !httpToken)) {
+      toast.error('Please enter username and HTTP token')
+      return
+    }
+    if (authMethod === 'git_cookies' && !gitcookiesContent) {
+      toast.error('Please enter gitcookies content')
+      return
+    }

     testMutation.mutate(buildTestPayload(), {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/frontend/src/components/gerrit-connection-card.tsx` around lines
95 - 113, handleTest currently only checks url and can call testMutation.mutate
with missing auth fields, causing poor backend errors; before calling
testMutation.mutate (in handleTest) validate the auth-specific inputs (e.g.,
username and token/password or selected authType) are present and non-empty,
surface user-friendly toast.error messages for the missing fields, and only call
buildTestPayload()/testMutation.mutate when all required auth fields are filled
so the UI-side validation prevents unnecessary backend calls.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/backend/handlers/gerrit_auth.go`:
- Around line 148-153: ConnectGerrit (and the other handlers
getGerritCredentials, listGerritCredentials, deleteGerritCredentials) obtain a
request-scoped Kubernetes client via GetK8sClientsForRequest(c) but then call
Secret helper functions (e.g., storeGerritCredentials and other secret helpers)
that use the global K8sClient; update each handler to pass the obtained reqK8s
into those helpers, and change the helper function signatures
(storeGerritCredentials and related secret helpers) to accept and use the
passed-in K8s client instead of the global K8sClient so all user-facing API ops
use the request-scoped client.

In `@components/backend/routes.go`:
- Around line 171-176: Register server-side enforcement for the gerrit feature
flag by gating all Gerrit endpoints (ConnectGerrit, TestGerritConnection,
ListGerritInstances, GetGerritStatus, DisconnectGerrit) behind the
`gerrit.enabled` check—either by wrapping the route registrations with a
middleware that reads the `gerrit.enabled` flag or by adding an early guard
inside each handler to return 404/403 when the flag is off; also ensure
TestGerritConnection requires user authentication the same way as the other
handlers (apply the same auth middleware used for
ConnectGerrit/ListGerritInstances/etc. or add an auth check at the top of
TestGerritConnection).

In `@components/frontend/src/app/api/auth/gerrit/instances/route.ts`:
- Around line 7-14: The current proxy call using fetch to
`${BACKEND_URL}/auth/gerrit/instances` (resp/resp.text()) lacks a timeout and
error handling; wrap the fetch in an AbortController with a short hard timeout,
await resp.ok then parse body, and surround the fetch/response logic with
try/catch to return appropriate non-500 responses on network failures (e.g.,
504/502 and a JSON error message) instead of letting the framework throw; update
the code paths that create Response (the return new Response(...) block) to use
the caught error/status and ensure 'Content-Type: application/json' is
preserved, and apply this same pattern to all new Gerrit proxy route handlers.

In `@components/frontend/src/services/api/gerrit-auth.ts`:
- Around line 28-38: The TypeScript type GerritInstanceStatus requires connected
but the backend ListGerritInstances (gerrit_auth.go) does not return it; update
the frontend type to make connected optional (connected?: boolean) in
GerritInstanceStatus or alternatively update the ListGerritInstances response to
include a connected boolean so it matches GetGerritStatus — change whichever
side you prefer (edit GerritInstanceStatus in
components/frontend/src/services/api/gerrit-auth.ts or add connected in the
ListGerritInstances response in gerrit_auth.go) so both shapes align.

---

Nitpick comments:
In `@components/frontend/src/components/gerrit-connection-card.tsx`:
- Around line 21-34: The Props type declares a status prop that
GerritConnectionCard doesn't use; either remove status from Props or wire it
into the component: update the Props type to drop status if not needed, or
destructure status in GerritConnectionCard and use it to seed local state or
render initial UI (e.g., before useGerritInstances loads) and replace any
hardcoded/empty initial state with status.instances and status.connected; ensure
references to status.instanceName/url/authMethod/connected align with existing
useGerritInstances usage so behavior and types remain consistent.
- Around line 95-113: handleTest currently only checks url and can call
testMutation.mutate with missing auth fields, causing poor backend errors;
before calling testMutation.mutate (in handleTest) validate the auth-specific
inputs (e.g., username and token/password or selected authType) are present and
non-empty, surface user-friendly toast.error messages for the missing fields,
and only call buildTestPayload()/testMutation.mutate when all required auth
fields are filled so the UI-side validation prevents unnecessary backend calls.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 68398905-5e79-4c72-ac5c-61475894b6df

📥 Commits

Reviewing files that changed from the base of the PR and between 4e063b0 and b2eeca5.

📒 Files selected for processing (22)
  • .specify/specs/gerrit-integration/plan.md
  • .specify/specs/gerrit-integration/spec.md
  • .specify/specs/gerrit-integration/tasks.md
  • components/backend/handlers/gerrit_auth.go
  • components/backend/handlers/integration_validation.go
  • components/backend/handlers/integrations_status.go
  • components/backend/handlers/runtime_credentials.go
  • components/backend/routes.go
  • components/frontend/src/app/api/auth/gerrit/[instanceName]/disconnect/route.ts
  • components/frontend/src/app/api/auth/gerrit/[instanceName]/status/route.ts
  • components/frontend/src/app/api/auth/gerrit/connect/route.ts
  • components/frontend/src/app/api/auth/gerrit/instances/route.ts
  • components/frontend/src/app/api/auth/gerrit/test/route.ts
  • components/frontend/src/app/integrations/IntegrationsClient.tsx
  • components/frontend/src/components/gerrit-connection-card.tsx
  • components/frontend/src/services/api/gerrit-auth.ts
  • components/frontend/src/services/api/integrations.ts
  • components/frontend/src/services/queries/use-gerrit.ts
  • components/manifests/base/core/flags.json
  • components/runners/ambient-runner/ambient_runner/bridges/claude/mcp.py
  • components/runners/ambient-runner/ambient_runner/platform/__init__.py
  • components/runners/ambient-runner/ambient_runner/platform/auth.py

Comment on lines +148 to +153
func ConnectGerrit(c *gin.Context) {
reqK8s, _ := GetK8sClientsForRequest(c)
if reqK8s == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or missing token"})
return
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Secret operations use backend service account instead of user's K8s client.

ConnectGerrit correctly calls GetK8sClientsForRequest(c) but then storeGerritCredentials (and all other Secret helpers) use the global K8sClient. Per coding guidelines, user-facing API ops must use the request-scoped client.

Same issue in getGerritCredentials, listGerritCredentials, and deleteGerritCredentials.

🔧 Proposed fix: Pass K8s client to helpers
-func storeGerritCredentials(ctx context.Context, creds *GerritCredentials) error {
+func storeGerritCredentials(ctx context.Context, k8sClient kubernetes.Interface, creds *GerritCredentials) error {
   // ... use k8sClient instead of K8sClient ...

-func getGerritCredentials(ctx context.Context, userID, instanceName string) (*GerritCredentials, error) {
+func getGerritCredentials(ctx context.Context, k8sClient kubernetes.Interface, userID, instanceName string) (*GerritCredentials, error) {

-func listGerritCredentials(ctx context.Context, userID string) ([]GerritCredentials, error) {
+func listGerritCredentials(ctx context.Context, k8sClient kubernetes.Interface, userID string) ([]GerritCredentials, error) {

-func deleteGerritCredentials(ctx context.Context, userID, instanceName string) error {
+func deleteGerritCredentials(ctx context.Context, k8sClient kubernetes.Interface, userID, instanceName string) error {

Then in handlers:

 func ConnectGerrit(c *gin.Context) {
   reqK8s, _ := GetK8sClientsForRequest(c)
   // ...
-  if err := storeGerritCredentials(c.Request.Context(), creds); err != nil {
+  if err := storeGerritCredentials(c.Request.Context(), reqK8s, creds); err != nil {

As per coding guidelines: "User-facing API ops MUST use GetK8sClientsForRequest(c), never the backend service account."

Also applies to: 372-429

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/backend/handlers/gerrit_auth.go` around lines 148 - 153,
ConnectGerrit (and the other handlers getGerritCredentials,
listGerritCredentials, deleteGerritCredentials) obtain a request-scoped
Kubernetes client via GetK8sClientsForRequest(c) but then call Secret helper
functions (e.g., storeGerritCredentials and other secret helpers) that use the
global K8sClient; update each handler to pass the obtained reqK8s into those
helpers, and change the helper function signatures (storeGerritCredentials and
related secret helpers) to accept and use the passed-in K8s client instead of
the global K8sClient so all user-facing API ops use the request-scoped client.

Comment on lines +171 to +176
// Cluster-level Gerrit (user-scoped, multi-instance)
api.POST("/auth/gerrit/connect", handlers.ConnectGerrit)
api.POST("/auth/gerrit/test", handlers.TestGerritConnection)
api.GET("/auth/gerrit/instances", handlers.ListGerritInstances)
api.GET("/auth/gerrit/:instanceName/status", handlers.GetGerritStatus)
api.DELETE("/auth/gerrit/:instanceName/disconnect", handlers.DisconnectGerrit)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find where gerrit.enabled is evaluated in backend code
rg -n --type=go -C3 'gerrit\.enabled|feature.?flag|EvaluateFeatureFlag|GetFeatureFlag' components/backend

# 2) Inspect Gerrit handler entry points for explicit flag checks
rg -n --type=go -C6 'func (ConnectGerrit|TestGerritConnection|ListGerritInstances|GetGerritStatus|DisconnectGerrit)\(' components/backend/handlers

Repository: ambient-code/platform

Length of output: 50377


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find Gerrit-related handler files
find components/backend -name "*gerrit*" -type f

# Search for gerrit in handler files (broader search)
rg -l gerrit components/backend/handlers/ 2>/dev/null || echo "No gerrit files found in handlers"

# Look for any gerrit-related code in entire backend
rg -n "ConnectGerrit|TestGerritConnection|ListGerritInstances|GetGerritStatus|DisconnectGerrit" components/backend/ -t go --max-count=50

Repository: ambient-code/platform

Length of output: 1379


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read gerrit_auth.go to inspect the handler implementations for flag checks
echo "=== gerrit_auth.go ===" 
wc -l components/backend/handlers/gerrit_auth.go

# Read the first part and look for flag checks
head -200 components/backend/handlers/gerrit_auth.go

echo ""
echo "=== Searching for flag checks in gerrit_auth.go ===" 
rg -n "gerrit\.enabled|FeatureEnabledForRequest|EvaluateFeatureFlag|flags\.|feature" components/backend/handlers/gerrit_auth.go

Repository: ambient-code/platform

Length of output: 5889


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read more of gerrit_auth.go starting from ConnectGerrit function (line 148)
sed -n '148,300p' components/backend/handlers/gerrit_auth.go

echo ""
echo "=== TestGerritConnection from integration_validation.go ===" 
# Find and read TestGerritConnection function
rg -n "func TestGerritConnection" components/backend/handlers/integration_validation.go -A 50

Repository: ambient-code/platform

Length of output: 7036


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read remaining Gerrit handlers
echo "=== ListGerritInstances and DisconnectGerrit ===" 
sed -n '299,400p' components/backend/handlers/gerrit_auth.go

echo ""
echo "=== Check routes.go for middleware on Gerrit endpoints ===" 
# Look at context around the Gerrit routes to see if middleware is applied
sed -n '160,185p' components/backend/routes.go

Repository: ambient-code/platform

Length of output: 4871


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check broader routes context to confirm no middleware at route group level
echo "=== Full route group context ===" 
sed -n '100,190p' components/backend/routes.go | head -100

echo ""
echo "=== Check if there's any global middleware that checks gerrit.enabled ===" 
rg -n "gerrit\.enabled" components/backend/ -t go

Repository: ambient-code/platform

Length of output: 5446


Add server-side feature-flag enforcement for Gerrit endpoints.

Lines 171-176 register Gerrit routes without any gerrit.enabled gate. All five handlers (ConnectGerrit, TestGerritConnection, ListGerritInstances, GetGerritStatus, DisconnectGerrit) lack server-side flag checks, allowing direct API bypass of workspace-scoped rollout control. Additionally, TestGerritConnection is unauthenticated—it should require user authentication like the other handlers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/backend/routes.go` around lines 171 - 176, Register server-side
enforcement for the gerrit feature flag by gating all Gerrit endpoints
(ConnectGerrit, TestGerritConnection, ListGerritInstances, GetGerritStatus,
DisconnectGerrit) behind the `gerrit.enabled` check—either by wrapping the route
registrations with a middleware that reads the `gerrit.enabled` flag or by
adding an early guard inside each handler to return 404/403 when the flag is
off; also ensure TestGerritConnection requires user authentication the same way
as the other handlers (apply the same auth middleware used for
ConnectGerrit/ListGerritInstances/etc. or add an auth check at the top of
TestGerritConnection).

Comment on lines +7 to +14
const resp = await fetch(`${BACKEND_URL}/auth/gerrit/instances`, {
method: 'GET',
headers,
})

const data = await resp.text()
return new Response(data, { status: resp.status, headers: { 'Content-Type': 'application/json' } })
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add timeout + network-failure handling for backend proxy calls.

Line 7 currently has no timeout, and fetch failures are unhandled, so transient backend/network issues can produce hanging requests or framework-level 500s. Please apply the same hardening to all new Gerrit proxy routes.

Suggested fix
 export async function GET(request: Request) {
   const headers = await buildForwardHeadersAsync(request)
-
-  const resp = await fetch(`${BACKEND_URL}/auth/gerrit/instances`, {
-    method: 'GET',
-    headers,
-  })
-
-  const data = await resp.text()
-  return new Response(data, { status: resp.status, headers: { 'Content-Type': 'application/json' } })
+  try {
+    const resp = await fetch(`${BACKEND_URL}/auth/gerrit/instances`, {
+      method: 'GET',
+      headers,
+      signal: AbortSignal.timeout(15000),
+    })
+
+    const data = await resp.text()
+    return new Response(data, {
+      status: resp.status,
+      headers: { 'Content-Type': 'application/json' },
+    })
+  } catch {
+    return Response.json({ error: 'Failed to reach backend service' }, { status: 502 })
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const resp = await fetch(`${BACKEND_URL}/auth/gerrit/instances`, {
method: 'GET',
headers,
})
const data = await resp.text()
return new Response(data, { status: resp.status, headers: { 'Content-Type': 'application/json' } })
}
try {
const resp = await fetch(`${BACKEND_URL}/auth/gerrit/instances`, {
method: 'GET',
headers,
signal: AbortSignal.timeout(15000),
})
const data = await resp.text()
return new Response(data, {
status: resp.status,
headers: { 'Content-Type': 'application/json' },
})
} catch {
return Response.json({ error: 'Failed to reach backend service' }, { status: 502 })
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/frontend/src/app/api/auth/gerrit/instances/route.ts` around lines
7 - 14, The current proxy call using fetch to
`${BACKEND_URL}/auth/gerrit/instances` (resp/resp.text()) lacks a timeout and
error handling; wrap the fetch in an AbortController with a short hard timeout,
await resp.ok then parse body, and surround the fetch/response logic with
try/catch to return appropriate non-500 responses on network failures (e.g.,
504/502 and a JSON error message) instead of letting the framework throw; update
the code paths that create Response (the return new Response(...) block) to use
the caught error/status and ensure 'Content-Type: application/json' is
preserved, and apply this same pattern to all new Gerrit proxy route handlers.

Comment on lines +28 to +38
export type GerritInstanceStatus = {
connected: boolean
instanceName: string
url: string
authMethod: GerritAuthMethod
updatedAt?: string
}

export type GerritInstancesResponse = {
instances: GerritInstanceStatus[]
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify backend list response structure
rg -n 'result = append.*gin.H' components/backend/handlers/gerrit_auth.go -A5 -B2

Repository: ambient-code/platform

Length of output: 47


🏁 Script executed:

# Find the ListGerritInstances function definition
rg -n "func.*ListGerritInstances" components/backend/ -A 30

Repository: ambient-code/platform

Length of output: 2494


🏁 Script executed:

# Search for where instances are appended in the backend
rg -n "instances.*append\|instances\s*=" components/backend/handlers/gerrit_auth.go -B 2 -A 3

Repository: ambient-code/platform

Length of output: 47


🏁 Script executed:

# Check the frontend file to see how GerritInstanceStatus is used
cat -n components/frontend/src/services/api/gerrit-auth.ts | head -60

Repository: ambient-code/platform

Length of output: 2097


🏁 Script executed:

# Get the complete ListGerritInstances function
sed -n '328,370p' components/backend/handlers/gerrit_auth.go

Repository: ambient-code/platform

Length of output: 1369


🏁 Script executed:

# Also check if there's any connection status logic or connected field being set
rg -n "connected" components/backend/handlers/gerrit_auth.go -B 2 -A 2

Repository: ambient-code/platform

Length of output: 984


Type mismatch: connected field missing from list response.

GerritInstanceStatus requires connected: boolean (line 29), but ListGerritInstances in gerrit_auth.go (lines 353-362) returns only instanceName, url, authMethod, updatedAt — no connected field.

The GetGerritStatus endpoint includes connected, but the list endpoint doesn't. Either add connected to the list response or make it optional in the type.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/frontend/src/services/api/gerrit-auth.ts` around lines 28 - 38,
The TypeScript type GerritInstanceStatus requires connected but the backend
ListGerritInstances (gerrit_auth.go) does not return it; update the frontend
type to make connected optional (connected?: boolean) in GerritInstanceStatus or
alternatively update the ListGerritInstances response to include a connected
boolean so it matches GetGerritStatus — change whichever side you prefer (edit
GerritInstanceStatus in components/frontend/src/services/api/gerrit-auth.ts or
add connected in the ListGerritInstances response in gerrit_auth.go) so both
shapes align.

@jeremyeder
Copy link
Copy Markdown
Contributor Author

Follow-up: #1350 adds Gerrit workflow skills (review, submit, create) that teach agents how to compose the MCP server tools this PR enables.

@jeremyeder
Copy link
Copy Markdown
Contributor Author

Couldn't get HTTP credentials for my @gmail or @redhat accounts.

ss_ 2026-04-17 at 13 11 57 ss_ 2026-04-17 at 13 11 45

But it looks like this
image

jeremyeder added a commit that referenced this pull request Apr 20, 2026
## Summary

- Three Claude Code plugin skills teaching agents Gerrit-specific
workflows
- **gerrit-review**: fetch changes, read diffs, post inline comments,
vote with labels
- **gerrit-submit**: check submit requirements, rebase, submit changes
- **gerrit-create**: push code for review, update patch sets, manage
metadata
- References the Gerrit MCP server tools (`fetch_gerrit_change`,
`fetch_patchset_diff`, `submit_gerrit_review`)
- Complements #1349 (Gerrit integration connector) — skills provide
workflow judgment, connector provides plumbing

Depends on #1349

## Test plan
- [ ] Skills discovered by Claude Code plugin system
- [ ] Skill descriptions trigger on relevant user prompts
- [ ] Skill content references correct MCP tool names

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added Gerrit skills plugin providing comprehensive guides for creating
Gerrit changes, reviewing code with voting and inline comments, and
submitting changes for merge, including troubleshooting for common
workflow issues.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Ambient Code Bot <bot@ambient-code.local>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
components/backend/routes.go (1)

102-102: ⚠️ Potential issue | 🟠 Major

Enforce gerrit.enabled server-side for Gerrit endpoints (including runtime credential fetch).

Line 102 and Lines 177-183 expose Gerrit API surfaces without any visible route-level feature-flag guard. If handlers also lack a guard, this allows direct API access even when workspace rollout is disabled in UI. Add a server-side gerrit.enabled check (middleware or early handler return with 403/404) for these endpoints.

As per coding guidelines, "Prioritize Critical and Major severity issues. Minimize Minor and Trivial findings." and "Flag bugs, security vulnerabilities, logic errors, data loss risks, and meaningful refactoring opportunities."

Also applies to: 177-183

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/backend/routes.go` at line 102, The Gerrit routes (e.g.,
projectGroup.GET("/agentic-sessions/:sessionName/credentials/gerrit",
handlers.GetGerritCredentialsForSession)) are exposed without a server-side
feature flag check; add a guard so requests are blocked when gerrit.enabled is
false by either creating a small middleware that reads the server/workspace
config and returns 403 (or 404) when disabled and applying it to all
Gerrit-related routes, or update each handler (e.g.,
handlers.GetGerritCredentialsForSession and the other Gerrit handlers referenced
in the diff) to perform an early check of gerrit.enabled and return the
appropriate HTTP error before doing any work.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@components/backend/routes.go`:
- Line 102: The Gerrit routes (e.g.,
projectGroup.GET("/agentic-sessions/:sessionName/credentials/gerrit",
handlers.GetGerritCredentialsForSession)) are exposed without a server-side
feature flag check; add a guard so requests are blocked when gerrit.enabled is
false by either creating a small middleware that reads the server/workspace
config and returns 403 (or 404) when disabled and applying it to all
Gerrit-related routes, or update each handler (e.g.,
handlers.GetGerritCredentialsForSession and the other Gerrit handlers referenced
in the diff) to perform an early check of gerrit.enabled and return the
appropriate HTTP error before doing any work.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b633fbff-32b2-47dd-87df-6732149a0dab

📥 Commits

Reviewing files that changed from the base of the PR and between b2eeca5 and 08e455d.

📒 Files selected for processing (2)
  • components/backend/routes.go
  • components/runners/ambient-runner/ambient_runner/platform/auth.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/runners/ambient-runner/ambient_runner/platform/auth.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant