Skip to content

Conversation

@Gkrumbach07
Copy link
Collaborator

Summary

Implements runtime credential fetching architecture for GitHub PAT, GitLab PAT, and Jira API tokens. This improves upon PR #562's volume mounting approach by fetching credentials just-in-time from the backend API, ensuring tokens are always fresh for long-running sessions.

Key Features

1. Cluster-Level Credential Storage

  • GitHub PAT: Stored in github-pat-credentials Secret (user-scoped)
  • GitLab PAT: Stored in gitlab-credentials Secret (user-scoped)
  • Jira API Token: Stored in jira-credentials Secret (user-scoped)
  • All credentials keyed by sanitized userID for multi-tenant isolation

2. Runtime Credential Fetching

  • Runner fetches credentials from backend API at session start
  • No pre-mounting or volume syncing required
  • Credentials populated into environment variables just-in-time
  • Auto-refresh for long-running sessions

3. GitHub Token Precedence (Priority-Based)

  1. User's GitHub PAT (highest priority - user control)
  2. GitHub App installation token (auto-minted, 1hr lifetime)
  3. Project-level GITHUB_TOKEN (legacy fallback, deprecated)

4. Backend API Endpoints

  • GET /api/projects/:project/agentic-sessions/:session/credentials/github
  • GET /api/projects/:project/agentic-sessions/:session/credentials/google
  • GET /api/projects/:project/agentic-sessions/:session/credentials/jira
  • GET /api/projects/:project/agentic-sessions/:session/credentials/gitlab

All endpoints require BOT_TOKEN authentication (session-scoped).

Architectural Improvements Over PR #562

PR #562 fixed Google MCP authentication using volume mounting. This PR supersedes that approach:

Aspect PR #562 (Volume Mount) This PR (Runtime Fetch)
Credential freshness Updated every ~60s (K8s sync) Always fresh (on-demand)
Operator complexity Syncs credentials, manages volumes No credential logic needed
Multi-credential support Google only GitHub, GitLab, Jira, Google
Long-running sessions Manual refresh needed Auto-refresh built-in
Security Credentials pre-mounted Fetched just-in-time

Components Changed

Backend

  • handlers/github_auth.go - GitHub PAT and App installation storage/retrieval
  • handlers/gitlab_auth.go - GitLab PAT storage/retrieval (cluster-level)
  • handlers/jira_auth.go - Jira API token storage/retrieval
  • handlers/runtime_credentials.go - NEW - Session runtime credential endpoints
  • handlers/integrations_status.go - NEW - Unified integration status endpoint
  • handlers/integration_validation.go - NEW - Credential validation helpers
  • git/operations.go - Updated GitHub token precedence logic
  • routes.go - Added credential management routes
  • server/server.go - Enhanced token redaction in logs

Frontend

  • components/github-connection-card.tsx - GitHub PAT configuration UI
  • components/gitlab-connection-card.tsx - NEW - GitLab connection UI
  • components/jira-connection-card.tsx - NEW - Jira connection UI
  • app/api/auth/github/pat/* - NEW - GitHub PAT API routes
  • app/api/auth/gitlab/* - NEW - GitLab API routes
  • app/api/auth/jira/* - NEW - Jira API routes
  • app/api/projects/[name]/agentic-sessions/[sessionName]/credentials/* - NEW - Runtime credential endpoints

Operator

  • internal/handlers/sessions.go - Removed volume mounting logic, added runtime fetching comments

Runner

  • adapter.py - _populate_runtime_credentials() fetches all credentials at session start
  • Sets USER_GOOGLE_EMAIL from backend API response
  • Populates JIRA_URL, JIRA_API_TOKEN, GITLAB_TOKEN from fetched credentials

Security

  • ✅ RBAC enforced on all credential endpoints
  • ✅ User-scoped credentials (namespace isolation)
  • ✅ Token format validation (GitHub, GitLab)
  • ✅ Comprehensive secret redaction in logs
  • ✅ Session-scoped BOT_TOKEN for runtime fetching

Testing

  • Manual testing on dev cluster
  • Verify GitHub PAT takes priority over GitHub App
  • Verify GitLab git operations work with PAT
  • Verify Jira MCP authentication
  • CI passes

Migration Notes

For users currently using project-level integration secrets:

  • Legacy ambient-non-vertex-integrations Secret still supported as fallback
  • Users should migrate to cluster-level credentials via Integrations page
  • Project-level secrets will be deprecated in future release

For PR #562 changes:

  • Volume mounting code removed (simpler architecture)
  • Runtime fetching provides same functionality with better token freshness
  • No manual refresh needed for long-running sessions

🤖 Generated with Claude Code

- Introduced endpoints for managing GitHub Personal Access Tokens (PAT) and GitLab credentials at the cluster level.
- Implemented functions to save, retrieve, and delete GitHub PATs, enhancing user authentication options.
- Updated routing to include new credential fetch endpoints for long-running sessions.
- Deprecated project-scoped GitLab authentication endpoints for future removal, ensuring backward compatibility.
- Enhanced GitHub status checks to return both GitHub App and PAT status, improving user experience.

These changes aim to streamline integration management and provide users with flexible authentication methods.
…time credential fetching

- Operator no longer fetches/sets USER_GOOGLE_EMAIL (runner handles this)
- Runner sets USER_GOOGLE_EMAIL from backend API response
- Supersedes PR ambient-code#562's volume mounting with runtime API fetching
- Simpler architecture: no syncing, mounting, or postStart hooks needed
@codecov
Copy link

codecov bot commented Feb 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

This comment has been minimized.

- Operator now sets PROJECT_NAME=sessionNamespace for runtime credential API calls
- Runner uses AGENTIC_SESSION_NAMESPACE as fallback if PROJECT_NAME not set
- Improved error logging in credential fetch to show which env vars are missing
@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

Claude Code Review

Summary

This PR implements runtime credential fetching for GitHub PAT, GitLab, and Jira integration tokens, improving upon PR #562's volume mounting approach. The changes introduce cluster-level credential storage with session-scoped runtime fetching, ensuring tokens are always fresh for long-running sessions.

Overall Assessment: The PR demonstrates strong architectural improvements with a well-designed precedence system for GitHub tokens. However, there are several critical security and code quality issues that must be addressed before merge.


Issues by Severity

🚫 Blocker Issues

1. User Token Authentication Missing in Runtime Credential Endpoints (runtime_credentials.go)

  • Location: Lines 23-79 in all four credential fetch endpoints
  • Issue: Uses user-scoped client for validation but NEVER checks if reqK8s == nil properly follows standard auth pattern
  • Pattern Violation: While the code does call GetK8sClientsForRequest(c) and checks for nil, the pattern is correct. However, there's a type safety issue on line 64:
    k8sClientset, ok := K8sClient.(*kubernetes.Clientset)
    if !ok {
        log.Printf("Failed to convert K8sClient to *kubernetes.Clientset")
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal error"})
        return
    }
  • Fix Required: This type assertion failure should NEVER happen in production. Add a panic with clear error message during initialization if K8sClient is not a *kubernetes.Clientset, not during request handling.

2. Unsafe Type Assertions Without Checking (runtime_credentials.go)

  • Lines: 49-55, 109-115, 191-197, 251-257
  • Issue: Direct type assertions without checking found parameter:
    if spec, ok := obj.Object["spec"].(map[string]interface{}); ok {
        if userContext, ok := spec["userContext"].(map[string]interface{}); ok {
            if uid, ok := userContext["userId"].(string); ok {
  • Pattern Violation: CLAUDE.md Critical Rule Epic: AI Agent Development #4 requires using unstructured.Nested* helpers
  • Fix Required:
    spec, found, err := unstructured.NestedMap(obj.Object, "spec")
    if !found || err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid session format"})
        return
    }
    userContext, found, err := unstructured.NestedMap(spec, "userContext")
    if !found || err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "User context not found"})
        return
    }

3. Token Logging in Integration Validation (integration_validation.go)

  • Lines: 24, 54, 81 (Authorization headers constructed with tokens)
  • Issue: While tokens aren't directly logged, error messages from HTTP requests could leak tokens in stack traces
  • Pattern Violation: CLAUDE.md Critical Rule Epic: Data Source Integration #3 - Token Security
  • Fix Required: Wrap HTTP calls in recovery handlers that redact tokens from error messages

🔴 Critical Issues

4. Missing Token Validation Before Storage (jira_auth.go, gitlab_auth.go)

  • Issue: ConnectJira and ConnectGitLab store credentials without validating them first
  • Impact: Users can save invalid credentials, leading to confusing failures during sessions
  • Fix Required: Call validation functions before storage:
    // In ConnectJira (after line 63)
    valid, err := ValidateJiraToken(c.Request.Context(), req.URL, req.Email, req.APIToken)
    if err != nil || !valid {
        c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid Jira credentials"})
        return
    }

5. Hardcoded Secret Names Without Namespace Constant (jira_auth.go, gitlab_auth.go)

  • Lines: jira_auth.go:159, gitlab_auth.go (similar pattern)
  • Issue: Secret name "jira-credentials" is hardcoded, but Namespace variable usage suggests it should be configurable
  • Pattern Violation: Not following established patterns from github_auth.go which uses constants
  • Fix Required: Define constants at package level:
    const (
        jiraCredentialsSecretName   = "jira-credentials"
        gitlabCredentialsSecretName = "gitlab-credentials"
    )

6. Race Condition in Secret Updates (jira_auth.go, gitlab_auth.go)

  • Lines: jira_auth.go:161-209, gitlab_auth.go (similar pattern)
  • Issue: Retry loop with max 3 attempts, but no exponential backoff
  • Impact: Under high concurrency, all 3 retries could fail, leaving users unable to save credentials
  • Fix Required: Add exponential backoff:
    for i := 0; i < 3; i++ {
        if i > 0 {
            time.Sleep(time.Duration(i*i) * 100 * time.Millisecond) // 100ms, 400ms
        }
        // ... existing retry logic
    }

7. Operator No Longer Sets PROJECT_NAME (operator/internal/handlers/sessions.go)

  • Issue: The PR description mentions "Operator now sets PROJECT_NAME=sessionNamespace" but the diff shows 230 lines DELETED from the operator
  • Impact: Runtime credential fetching depends on PROJECT_NAME, but operator may not be setting it
  • Verification Required: Confirm this environment variable is being set correctly in the Job spec

🟡 Major Issues

8. No RBAC Check in Integration Status Endpoint (integrations_status.go)

  • Lines: 11-43
  • Issue: GetIntegrationsStatus validates user token but doesn't check RBAC permissions
  • Impact: Any authenticated user can query integration status (though they can only see their own)
  • Recommendation: Add explicit RBAC check or document why it's not needed (user-scoped by userID)

9. Inconsistent Error Handling in Token Validation (integration_validation.go)

  • Lines: 13-35, 38-64, 67-92, 95-117
  • Issue: Validation functions return (bool, error), but callers in integrations_status.go ignore errors:
    valid, _ := ValidateGitHubToken(ctx, patCreds.Token)  // Line 67
  • Impact: Network errors, API rate limits, or timeouts are silently treated as "invalid token"
  • Fix Required: Log validation errors separately from invalid credentials

10. Test Coverage Missing for New Endpoints

  • Issue: No integration tests for runtime credential endpoints
  • Impact: Runtime credential fetching is a critical path - needs automated testing
  • Recommendation: Add tests to components/backend/tests/integration/ covering:
    • GitHub token precedence (PAT > App > fallback)
    • Credential fetching with valid session
    • 404 when credentials not configured
    • 401 when BOT_TOKEN invalid

11. Frontend Type Safety Concerns (components/frontend/)

  • Issue: New frontend API routes lack type definitions
  • Files: app/api/auth/*/route.ts, credential endpoint routes
  • Pattern Violation: Frontend Critical Rule Outcome: Reduce Refinement Time with agent System #1 - Zero any types
  • Recommendation: Define TypeScript types for all API responses in src/types/api/

🔵 Minor Issues

12. Inconsistent Logging Levels

  • Lines: github_auth.go uses log.Printf without levels, while operator uses structured logging
  • Recommendation: Adopt consistent logging approach (preferably structured logs)

13. Magic Numbers in Timeouts

  • Lines: integration_validation.go:18, 42, 72, 100 (Timeout: 10 * time.Second)
  • Recommendation: Define constants:
    const defaultAPITimeout = 10 * time.Second

14. Missing Documentation for Token Precedence

  • Issue: GitHub token precedence logic is well-implemented but not documented in ADRs
  • Recommendation: Create ADR-0006 documenting the credential fetching architecture and precedence rules

15. Server Test Missing Negative Cases (server/server_test.go)

  • Lines: 7-90
  • Issue: TestSanitizeUserID covers positive cases but missing edge cases:
    • Null bytes in input
    • Unicode characters (emoji, non-Latin scripts)
    • Very long consecutive invalid characters
  • Recommendation: Add test cases for security edge cases

Positive Highlights

Excellent Architecture: Runtime credential fetching is a significant improvement over volume mounting
Strong Token Precedence: GitHub PAT > App > fallback pattern is well-designed
Comprehensive Secret Management: All credentials properly stored in K8s Secrets
Good Separation of Concerns: Validation helpers separated from auth handlers
Deterministic UserID Sanitization: Server tests demonstrate thoughtful handling of edge cases
Clean Operator Simplification: Removing 230 lines of volume mounting code improves maintainability
Token Redaction in Logs: Enhanced server/server.go properly redacts tokens from query strings


Recommendations

Before Merge (Required)

  1. Fix all Blocker issues (Outcome: Reduce Refinement Time with agent System #1-3) - Type safety and token security
  2. Add token validation before storage (Epic: AI Agent Development #4) - Jira and GitLab
  3. Verify PROJECT_NAME env var (Test: Automation Workflow Validation #7) - Check operator Job spec
  4. Add error handling (Bump actions/add-to-project from 0.5.0 to 1.0.2 #9) - Don't silently swallow validation errors

Before Merge (Strongly Recommended)

  1. Add integration tests (Bump actions/checkout from 4 to 5 #10) - Runtime credential endpoints
  2. Fix race condition (Epic: Testing & Validation #6) - Exponential backoff in Secret updates
  3. Document in ADR (Add OpenShift AI Virtual Team agents as source of truth #14) - Credential architecture and precedence

Post-Merge (Technical Debt)

  1. Improve frontend type safety (Bump actions/github-script from 6 to 7 #11) - TypeScript types for new APIs
  2. Refactor logging (Add vTeam shared Claude Code configuration with hooks-based enforcement #12) - Adopt structured logging consistently
  3. Extract constants (Epic: Jira Integration & Workflow #5, Review context7 MCP server and patternfly team usage #13) - Secret names and timeouts

Security Review Checklist

  • ✅ RBAC enforced on credential storage endpoints (uses GetK8sClientsForRequest)
  • ✅ User-scoped credentials with namespace isolation
  • ✅ Session-scoped BOT_TOKEN for runtime fetching
  • ⚠️ Token redaction in logs (mostly correct, but see issue Epic: Data Source Integration #3)
  • ⚠️ Type-safe unstructured access (violated in runtime_credentials.go)
  • ✅ No tokens in error messages returned to users
  • ✅ Credential validation functions properly timeout

Testing Recommendations

  1. Manual Testing:

    • Test GitHub PAT takes priority over GitHub App ✅ (as described)
    • Test credential fetch with expired/invalid tokens
    • Test credential fetch with network errors
    • Test long-running session with token refresh
  2. Automated Testing (missing):

    • Integration tests for runtime credential endpoints
    • Unit tests for token precedence logic
    • E2E tests for credential management UI

Final Verdict

Request Changes - This PR has strong architectural design but requires fixes for:

  1. Type safety violations (blocker Epic: RAT Architecture & Design #2)
  2. Token validation before storage (critical Epic: AI Agent Development #4)
  3. Operator PROJECT_NAME verification (critical Test: Automation Workflow Validation #7)
  4. Error handling improvements (critical Bump actions/add-to-project from 0.5.0 to 1.0.2 #9)

Once these issues are addressed, this PR will be ready to merge. The runtime credential fetching architecture is a significant improvement over volume mounting and aligns well with the project's security standards.

🤖 Review performed by Claude Sonnet 4.5 using project memory system:

  • CLAUDE.md - Backend/Operator Development Standards
  • .claude/context/security-standards.md - Token Security Patterns
  • .claude/patterns/k8s-client-usage.md - User Token Authentication
  • .claude/patterns/error-handling.md - Error Handling Patterns

🔍 View AI decision process (logs available for 90 days)

📋 View memory system files loaded (click to expand)

What Amber Loaded for Code Review

Amber automatically loaded these repository standards from the memory system:

  1. CLAUDE.md - Master project instructions, development standards
  2. backend-development.md - Go backend, K8s integration patterns
  3. frontend-development.md - NextJS, Shadcn UI, React Query patterns
  4. security-standards.md - Auth, RBAC, token handling
  5. k8s-client-usage.md - User token vs service account patterns
  6. error-handling.md - Consistent error patterns
  7. react-query-usage.md - Data fetching patterns

Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

Claude Code Review - PR #568

Summary

This PR implements runtime credential fetching for GitHub PAT, GitLab PAT, and Jira API tokens, superseding the volume mounting approach from PR #562. The architecture improves credential freshness by fetching on-demand from the backend API rather than pre-mounting credentials.

Overall Assessment: Strong architectural improvement with good security patterns, but contains several critical issues that must be addressed before merge.


Issues by Severity

🚫 Blocker Issues

1. Type Assertion Violations in runtime_credentials.go

Location: components/backend/handlers/runtime_credentials.go:48-55

Violation: Direct type assertions without using unstructured.Nested* helpers violates CLAUDE.md line 452.

Pattern repeated in 4 functions (lines 48-55, 108-115, 190-197, 252-259).

Required Fix: Use type-safe unstructured helpers:

import "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"

userID, found, err := unstructured.NestedString(obj.Object, "spec", "userContext", "userId")
if \!found || err \!= nil || userID == "" {
    c.JSON(http.StatusInternalServerError, gin.H{"error": "User ID not found"})
    return
}

2. Missing RBAC Validation in Runtime Credential Endpoints

Location: components/backend/handlers/runtime_credentials.go:23-79

Issue: Endpoints validate K8s token but don't verify user has permission to read session credentials.

Required: Add RBAC check before returning credentials in all 4 endpoints (GitHub, Google, Jira, GitLab).


3. Incomplete Token Redaction in Logs

Location: components/backend/server/server.go:28-32

Issue: Only redacts query string tokens, not Authorization headers (primary transport in this PR).

Required: Extend redaction to include Authorization headers in request logs.


4. UserID Ownership Validation Missing

Location: components/backend/handlers/integrations_status.go:13-43

Issue: GetIntegrationsStatus doesn't verify authenticated user matches requested userID.

Security Risk: Potential to query other users' integration status.


🔴 Critical Issues

5. Token Logging Risk in Validation Functions

Location: components/backend/handlers/integration_validation.go:24,54,81

Issue: Wrapped errors could leak tokens from HTTP request details.

Fix: Don't wrap errors from client.Do(req) - use generic error messages.


6. Google Token Refresh Returns Expired Tokens

Location: components/backend/handlers/runtime_credentials.go:142-152

Issue: Returns expired token on refresh failure instead of error.

Fix: Return 401 error and require re-authentication.


7. Missing Input Validation

Location: All credential endpoints in runtime_credentials.go

Issue: Session/project names not validated before K8s API calls.

Fix: Validate with isValidK8sName() to prevent injection.


🟡 Major Issues

8. Error Messages Expose Internals

  • Line 74: err.Error() exposes git package details
  • Use generic messages: "GitHub token not available"

9. Missing Unit Tests

  • sanitizeUserID function needs tests for email, LDAP, OpenShift formats

🔵 Minor Issues

10. Frontend Routes Missing Error Handling
11. Integration Tests Not Documented
12. Operator Comments Could Be Clearer


Positive Highlights

✅ Excellent architectural improvement over volume mounting
✅ Comprehensive token format validation
✅ Good separation of concerns (validation/status/runtime handlers)
✅ Proper retry logic in Secret updates
✅ Privacy-preserving logging (tokenLen=%d)
✅ Excellent PR description with comparison table


Recommendations

Before Merge (Blockers):

  1. Fix type assertions → use unstructured.Nested*
  2. Add RBAC checks to credential endpoints
  3. Add userID ownership validation
  4. Enhance token redaction for Authorization headers

High Priority (Critical):

  1. Fix token logging risk in validation
  2. Fix Google token refresh error handling
  3. Add input validation for resource names

Medium Priority:

  1. Add unit tests for sanitizeUserID
  2. Document integration tests
  3. Improve error messages

Code Quality Metrics

  • Lines Changed: +3,145 / -637 = +2,508 net
  • Files Changed: 42 (12 Go, 21 TS, 1 operator, 1 runner)
  • Estimated Fix Effort: 4-6 hours for blockers, 8-10 hours total

Final Verdict

Recommendation: Request Changes

Strong architectural improvement, but 4 blocker security issues must be fixed:

  • Type assertion safety violations
  • Missing RBAC checks
  • Incomplete token redaction
  • UserID ownership validation

The credential fetching architecture is sound but needs security hardening to meet CLAUDE.md standards.

🤖 Reviewed by Claude Code with memory context from:

  • .claude/context/backend-development.md
  • .claude/context/security-standards.md
  • .claude/patterns/k8s-client-usage.md
  • .claude/patterns/error-handling.md

🔍 View AI decision process (logs available for 90 days)

📋 View memory system files loaded (click to expand)

What Amber Loaded for Code Review

Amber automatically loaded these repository standards from the memory system:

  1. CLAUDE.md - Master project instructions, development standards
  2. backend-development.md - Go backend, K8s integration patterns
  3. frontend-development.md - NextJS, Shadcn UI, React Query patterns
  4. security-standards.md - Auth, RBAC, token handling
  5. k8s-client-usage.md - User token vs service account patterns
  6. error-handling.md - Consistent error patterns
  7. react-query-usage.md - Data fetching patterns

Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines.

- oauth.go: Simplify isValidUserID logic (De Morgan's law)
- server.go: Use strings.ReplaceAll instead of strings.Replace with -1
@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

Claude Code Review

Summary

This PR implements runtime credential fetching for GitHub PAT, GitLab, and Jira, replacing PR #562's volume mounting approach with an on-demand API-based architecture. The implementation is generally well-structured with good security practices, but there are several critical security issues and architectural concerns that must be addressed before merge.

Issues by Severity

🚫 Blocker Issues

1. CRITICAL: Runtime credential endpoints bypass user token authentication (SECURITY VIOLATION)

Location: components/backend/handlers/runtime_credentials.go - ALL endpoints (lines 23-282)

Problem: All four runtime credential endpoints (GetGitHubTokenForSession, GetGoogleCredentialsForSession, GetJiraCredentialsForSession, GetGitLabTokenForSession) use GetK8sClientsForRequest(c) which requires a user token, but the PR description states they require "BOT_TOKEN authentication (session-scoped)".

Evidence:

// Line 28 - uses GetK8sClientsForRequest (user token)
reqK8s, reqDyn := GetK8sClientsForRequest(c)
if reqK8s == nil {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or missing token"})
    return
}

Why this is a BLOCKER:

  • Pattern violation: According to CLAUDE.md and security-standards.md, GetK8sClientsForRequest is for user-initiated operations only
  • If these endpoints are meant for BOT_TOKEN (session pods), they should NOT use user-scoped clients
  • If bots have user tokens, that's a privilege escalation risk
  • The authentication mechanism is fundamentally unclear

Required Fix:

  1. Clarify authentication design: Are these endpoints for runner pods (BOT_TOKEN) or user browsers (user token)?
  2. If BOT_TOKEN: Create separate authentication middleware and use backend service account for K8s operations
  3. If user token: Update PR description to remove "BOT_TOKEN" claim
  4. Add explicit RBAC validation for session access
  5. Add comprehensive authentication tests

Reference: .claude/patterns/k8s-client-usage.md lines 27-45, CLAUDE.md lines 435-439


2. CRITICAL: Token validation calls expose tokens in API requests without verification

Location: components/backend/handlers/integrations_status.go lines 67, 115, 133

Problem: The unified status endpoint validates ALL credentials on EVERY status check by calling external APIs (GitHub, GitLab, Jira) with user tokens. This is:

  1. Performance disaster: 4 external API calls per status check
  2. Security risk: Sends tokens to external services unnecessarily
  3. Rate limiting: Will hit API rate limits quickly
// Line 67 - validates GitHub PAT on every status check
valid, _ := ValidateGitHubToken(ctx, patCreds.Token)

Required Fix:

  1. Make validation opt-in via query parameter (?validate=true)
  2. Cache validation results (5-10 minute TTL)
  3. Use expiry time for Google tokens instead of API validation
  4. Add circuit breaker for external API failures
  5. Document rate limiting implications

🔴 Critical Issues

3. Unsafe type assertions without checking (panic risk)

Location: Multiple files

  • runtime_credentials.go:64 - K8sClient type assertion
  • runtime_credentials.go:49-55 - Unstructured access to spec.userContext

Problem: Direct type assertions without type checking:

// Line 64 - unsafe cast
k8sClientset, ok := K8sClient.(*kubernetes.Clientset)
if !ok {
    // Returns error, but doesn't handle nil K8sClient
}

// Lines 49-55 - unsafe nested map access
if spec, ok := obj.Object["spec"].(map[string]interface{}); ok {
    if userContext, ok := spec["userContext"].(map[string]interface{}); ok {
        if uid, ok := userContext["userId"].(string); ok {
            userID = uid
        }
    }
}

Why this is CRITICAL: Pattern violation per CLAUDE.md lines 452-456. Should use unstructured.Nested* helpers.

Required Fix:

// Use type-safe helpers
userID, found, err := unstructured.NestedString(obj.Object, "spec", "userContext", "userId")
if !found || err != nil {
    return fmt.Errorf("userID not found in session spec")
}

Reference: CLAUDE.md lines 452-456, backend-development.md lines 69-85


4. Credentials exposed in response bodies (Information Disclosure)

Location:

  • runtime_credentials.go:78 - Returns GitHub token in JSON
  • runtime_credentials.go:155-160 - Returns Google access token
  • runtime_credentials.go:217-221 - Returns Jira API token
  • runtime_credentials.go:278-281 - Returns GitLab token

Problem: Tokens returned as plain JSON with no encryption. If HTTPS is terminated at proxy, tokens travel in cleartext.

// Line 78 - token in response body
c.JSON(http.StatusOK, gin.H{"token": token})

Required Fix:

  1. Document that HTTPS is mandatory for these endpoints
  2. Add middleware to enforce HTTPS-only
  3. Consider short-lived response tokens (encrypt with session key, expire in 60s)
  4. Add audit logging for credential access

5. Missing token redaction in new log statements

Location: server/server.go enhanced token redaction

Problem: While PR adds comprehensive token redaction (good!), the NEW handlers don't consistently redact. For example:

  • github_auth.go:558 - logs "Stored GitHub PAT" but might log token length elsewhere
  • No redaction patterns for Jira/GitLab tokens in error paths

Required Fix:

  1. Audit all new log statements for token leakage
  2. Add explicit log.Printf("tokenLen=%d", len(token)) pattern everywhere
  3. Add server_test.go test cases for Jira/GitLab redaction
  4. Run: grep -r 'log.Printf.*[Tt]oken' components/backend/handlers/ to find all instances

Reference: CLAUDE.md lines 446-450, security-standards.md lines 22-47


🟡 Major Issues

6. No RBAC validation before returning session credentials

Location: runtime_credentials.go:36 - All endpoints get session then return creds

Problem: Endpoints verify session exists, extract userID, then return credentials without checking if the requesting user has permission to access that session. RBAC gap!

// Gets session
obj, err := reqDyn.Resource(gvr).Namespace(project).Get(...)

// Extracts userID
userID := ... // from session spec

// Returns credentials for that userID
// BUT: No check that c.GetString("userID") == extracted userID!

Attack scenario: User A could call endpoint for User B's session if they know the session name.

Required Fix:

// After extracting userID from session
requestingUser := c.GetString("userID")
if requestingUser != userID {
    c.JSON(http.StatusForbidden, gin.H{"error": "Cannot access another user's credentials"})
    return
}

Reference: security-standards.md lines 52-70


7. Inconsistent error handling - some errors return 500, should be 404

Location: runtime_credentials.go:74 (GitHub), similar in others

Problem: When git.GetGitHubToken fails, returns 404. But if it fails for K8s reasons, returns 500. Inconsistent.

// Line 74
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})

Issue: Exposes internal errors ("failed to get secret") vs. user-facing errors ("not configured").

Required Fix: Distinguish between:

  • NotFound: User hasn't configured credentials (404)
  • Internal: K8s/API errors (500)
  • Forbidden: Access denied (403)

Reference: error-handling.md lines 9-41


8. Duplicate code across all 4 credential endpoints (DRY violation)

Location: runtime_credentials.go:23-282

Problem: Lines 23-60 are identical in all 4 endpoints (get session, extract userID). ~150 lines of duplicate code.

Required Fix: Extract common logic:

func getUserIDFromSession(c *gin.Context, project, session string) (string, error) {
    reqK8s, reqDyn := GetK8sClientsForRequest(c)
    if reqK8s == nil {
        return "", fmt.Errorf("unauthorized")
    }
    // ... common logic
    return userID, nil
}

Then:

func GetGitHubTokenForSession(c *gin.Context) {
    userID, err := getUserIDFromSession(c, c.Param("projectName"), c.Param("sessionName"))
    if err != nil { /* handle */ }
    // ... GitHub-specific logic
}

9. GitLab validation allows HTTP URLs (security downgrade)

Location: gitlab_auth.go:76-77

// Line 76
if parsedURL.Scheme != "https" {
    return fmt.Errorf("instance URL must use HTTPS")
}

Issue: Good validation! But error message says "must use HTTPS" while code checks != "https". What about http://? It's caught, but what about empty scheme?

Required Fix: Explicit check:

if parsedURL.Scheme == "" || parsedURL.Scheme == "http" {
    return fmt.Errorf("instance URL must use HTTPS (got: %s)", parsedURL.Scheme)
}

10. Integration status validation makes 4 concurrent external API calls

Location: integrations_status.go:31-40

Problem: Calls getGitHubStatusForUser, getGoogleStatusForUser, getJiraStatusForUser, getGitLabStatusForUser sequentially. Each validates token via external API.

Performance impact: 4-6 seconds per status check (4 × 1-1.5s each).

Required Fix:

  1. Validate in parallel with errgroup
  2. Add 10s timeout per validation
  3. Return partial results on failure
  4. Add caching

🔵 Minor Issues

11. Google token refresh error handling is too lenient

Location: runtime_credentials.go:147-152

if err != nil {
    log.Printf("Failed to refresh Google token: %v", err)
    // Return existing token (may be expired, but better than nothing)
}

Issue: Returning expired token is worse than returning error. Client will make failed API calls.

Better:

if err != nil {
    c.JSON(http.StatusInternalServerError, gin.H{
        "error": "Failed to refresh expired Google token",
        "action": "Please reconnect your Google account",
    })
    return
}

12. Missing input validation for Jira URL

Location: jira_auth.go:59-62

Problem: Jira URL is not validated (no HTTPS check, no format validation). Only checks if empty.

Required Fix: Add similar validation as GitLab (HTTPS required, valid hostname).


13. Retry logic uses magic number 3

Location: Multiple files (github_auth.go:318, gitlab_auth.go:499, jira_auth.go:161)

for i := 0; i < 3; i++ { // retry on conflict

Better: Extract constant:

const maxRetries = 3
for i := 0; i < maxRetries; i++ {

14. Inconsistent timestamp formats

Location:

  • github_auth.go:468 - RFC3339
  • gitlab_auth.go:414 - Unix timestamp string
  • jira_auth.go:70 - time.Now()

Required Fix: Use RFC3339 everywhere for consistency.


15. No tests for new credential endpoints

Problem: No test coverage for:

  • Runtime credential fetching
  • Token precedence logic
  • Integration status endpoint
  • Credential validation

Required: Add comprehensive tests covering:

  • Happy path (PAT → App → fallback)
  • Error cases (missing creds, invalid tokens)
  • RBAC scenarios
  • Token redaction in logs

Positive Highlights

Excellent token redaction improvements in server/server.go with comprehensive test coverage

Good security practices: HTTPS validation for GitLab, input sanitization, RBAC checks in global endpoints

Well-documented GitHub token precedence (PAT > App > fallback) in git/operations.go

Proper use of user-scoped clients in global auth endpoints (SaveGitHubPAT, ConnectJira, etc.)

Conflict retry logic for Secret updates (prevents race conditions)

Comprehensive credential validation helpers in integration_validation.go

Clean separation between global auth and runtime credential endpoints


Recommendations

Before Merge (Priority Order)

  1. Fix authentication model for runtime endpoints (Blocker Outcome: Reduce Refinement Time with agent System #1) - Clarify BOT_TOKEN vs user token
  2. Make credential validation opt-in (Blocker Epic: RAT Architecture & Design #2) - Add ?validate=true parameter
  3. Use unstructured.Nested helpers* (Critical Epic: Data Source Integration #3) - Fix type safety
  4. Add RBAC check for session ownership (Major Epic: Testing & Validation #6) - Prevent cross-user access
  5. Extract duplicate session-fetching code (Major Test: Updated Workflow Validation #8) - DRY violation
  6. Add HTTPS-only middleware for credential endpoints (Critical Epic: AI Agent Development #4)
  7. Add comprehensive tests (Minor Add RFE Council workflow diagram #15)

Architecture Improvements

Consider adding a credential service layer:

handlers/ (HTTP layer)
  └─> services/credentials/ (business logic)
       ├─> github.go (GitHub-specific logic)
       ├─> gitlab.go
       ├─> jira.go
       └─> validation.go

This would:

  • Eliminate code duplication
  • Centralize validation logic
  • Make testing easier
  • Follow backend package organization patterns

Security Checklist

  • Authentication model clarified and documented
  • RBAC validation added to runtime endpoints
  • Token exposure documented (HTTPS mandatory)
  • All tokens redacted in logs
  • Input validation for all endpoints
  • Rate limiting strategy documented
  • Audit logging for credential access

Overall Assessment: This PR makes significant architectural improvements over PR #562, but MUST address the authentication and security issues before merge. The runtime credential fetching design is sound, but the implementation has several critical security gaps that violate established patterns in CLAUDE.md.

Estimated fixes: 4-6 hours (authentication model + RBAC + deduplication + tests)

🤖 Generated with Claude Code


🔍 View AI decision process (logs available for 90 days)

📋 View memory system files loaded (click to expand)

What Amber Loaded for Code Review

Amber automatically loaded these repository standards from the memory system:

  1. CLAUDE.md - Master project instructions, development standards
  2. backend-development.md - Go backend, K8s integration patterns
  3. frontend-development.md - NextJS, Shadcn UI, React Query patterns
  4. security-standards.md - Auth, RBAC, token handling
  5. k8s-client-usage.md - User token vs service account patterns
  6. error-handling.md - Consistent error patterns
  7. react-query-usage.md - Data fetching patterns

Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

Claude Code Review

Summary

PR #568 implements runtime credential fetching for GitHub PAT, GitLab, and Jira integrations. Strong architectural improvement with several critical security issues to address.

Overall Assessment: Request changes - address critical security issues before merge.

Critical Issues (MUST FIX)

1. Token Logging Violation (runtime_credentials.go:78)

Returns GitHub token in plain JSON - will be logged by Gin middleware.
FIX: Return status instead of token value.

2. Missing User Authorization Check

Runtime credential endpoints do NOT verify requesting user owns the session.
ATTACK: User B can fetch User A credentials via their session ID.
FIX: Add check - currentUserID must match session owner userID.

3. Unsafe Type Assertions

Missing ok checks on type assertions (runtime_credentials.go:49-50).
FIX: Use unstructured.NestedMap() with three-value returns.

4. GitLab Token Validation Bug

Logic error - rejects valid tokens with hyphens/underscores.
FIX: Use regex pattern for validation.

Major Issues

  • GitHub PAT validation too restrictive (may reject legacy tokens)
  • Missing RBAC check on GetIntegrationsStatus
  • Error messages expose internal details
  • Secret retry loop needs better logging

Minor Issues

  • Operator binary committed (77MB) - add to gitignore
  • Inconsistent time format in responses
  • Test coverage gaps

Positive Highlights

✅ User token authentication enforced
✅ Strong architectural design
✅ Good code organization
✅ Token format validation
✅ Clear migration path

Recommendation

Request Changes - Fix 4 critical security issues before merge. Architecture is excellent but security patterns need tightening.

Estimated fix time: 2-4 hours for critical issues.


🔍 View AI decision process (logs available for 90 days)

📋 View memory system files loaded (click to expand)

What Amber Loaded for Code Review

Amber automatically loaded these repository standards from the memory system:

  1. CLAUDE.md - Master project instructions, development standards
  2. backend-development.md - Go backend, K8s integration patterns
  3. frontend-development.md - NextJS, Shadcn UI, React Query patterns
  4. security-standards.md - Auth, RBAC, token handling
  5. k8s-client-usage.md - User token vs service account patterns
  6. error-handling.md - Consistent error patterns
  7. react-query-usage.md - Data fetching patterns

Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

See full review below

@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

Claude Code Review

Summary

This PR implements runtime credential fetching for GitHub PAT, GitLab PAT, and Jira API tokens, replacing the volume-mounting approach from PR #562. The architecture is sound: credentials are fetched just-in-time from the backend API, ensuring tokens are always fresh for long-running sessions.

Overall Assessment: The implementation follows most project patterns correctly, but there are security and architectural issues that must be addressed before merge.


Issues by Severity

🚫 Blocker Issues

1. Critical RBAC Violation in Runtime Credentials Endpoints (runtime_credentials.go)

Lines 28-33, 94-99, 182-187, 249-254 use user-scoped clients but then extract userID from the session CR. This creates a privilege escalation vulnerability.

Problem: A malicious user could:

  1. Create a session with spec.userContext.userId: "victim-user"
  2. Call /credentials/github endpoint
  3. Get victim-user's credentials because RBAC only checks if they can read the session

Required Fix:

// After getting userID from session CR (line 49-54)
authenticatedUserID := c.GetString("userID")
if authenticatedUserID \!= userID {
    log.Printf("RBAC violation: user %s attempted to access credentials for session owned by %s", authenticatedUserID, userID)
    c.JSON(http.StatusForbidden, gin.H{"error": "Access denied: session belongs to different user"})
    return
}

This check already exists in lines 57-67 but needs to be added to ALL four credential endpoints (GitHub, Google, Jira, GitLab).

Reference: See security-standards.md lines 208-220 for RBAC enforcement patterns.


2. Unauthorized Backend Service Account Usage (runtime_credentials.go:78, 224, 291)

Lines 78, 224, 291 use backend service account (K8sClient, DynamicClient) to fetch credentials WITHOUT validating user permissions first.

Problem: Violates Critical Rule #1 from CLAUDE.md:435-439:

"FORBIDDEN: Using backend service account for user-initiated API operations"

Required Fix: These are user-initiated operations (session runtime credential fetching), so they MUST use user-scoped clients:

// Use reqDyn instead of DynamicClient to respect RBAC
obj, err := reqDyn.Resource(gvr).Namespace(project).Get(c.Request.Context(), session, v1.GetOptions{})

Reference: See k8s-client-usage.md lines 19-26 for correct pattern.


🔴 Critical Issues

3. Missing Error Context in Token Validation (integration_validation.go:29-31, 59-61, 88-90, 114-116)

Lines 29, 59, 88, 114 suppress errors that could leak tokens:

return false, fmt.Errorf("request failed")  // Lost original error context

Problem: While preventing token leakage is correct, losing ALL error context makes debugging impossible.

Recommended Fix:

if err \!= nil {
    log.Printf("GitHub token validation failed (request): %v", err)  // Log for debugging
    return false, fmt.Errorf("request failed")  // Generic user message
}

Reference: See error-handling.md lines 196-220 for error handling patterns.


4. Inconsistent RBAC Ownership Verification (runtime_credentials.go)

Lines 57-67 include RBAC ownership verification (authenticatedUserID \!= userID), but this check is duplicated in all 4 endpoints. This is error-prone and violates DRY.

Problem: If this logic needs to change, it must be updated in 4 places.

Recommended Fix: Extract to shared helper function:

func verifySessionOwnership(c *gin.Context, session *unstructured.Unstructured) error {
    userID, found, err := unstructured.NestedString(session.Object, "spec", "userContext", "userId")
    if \!found || err \!= nil || userID == "" {
        return fmt.Errorf("user ID not found in session")
    }
    
    authenticatedUserID := c.GetString("userID")
    if authenticatedUserID == "" {
        return fmt.Errorf("missing authenticated userID")
    }
    
    if authenticatedUserID \!= userID {
        log.Printf("RBAC violation: user %s attempted to access session owned by %s", authenticatedUserID, userID)
        return fmt.Errorf("access denied: session belongs to different user")
    }
    
    return nil
}

5. Potential Token Leakage in Logs (server/server.go:33-40)

Lines 33-40 redact Authorization headers, but only for Bearer tokens:

if strings.HasPrefix(auth, "Bearer ") {
    authHeader = "Bearer [REDACTED]"
} else {
    authHeader = "[REDACTED]"  // What if it's "Token ghp_xyz..."?
}

Problem: GitHub Personal Access Tokens might be sent as Token ghp_... or token ghp_... (case-insensitive).

Recommended Fix:

if auth := param.Request.Header.Get("Authorization"); auth \!= "" {
    authHeader = "[REDACTED]"  // Always redact, regardless of format
}

Reference: See security-standards.md lines 22-50 for token redaction patterns.


🟡 Major Issues

6. Missing Input Validation for Token Format (jira_auth.go, gitlab_auth.go)

Jira: No validation of token format before storage (jira_auth.go:47-56)
GitLab: Token validation only checks length and character set (gitlab_auth.go:91-109)

Problem: Invalid tokens get stored and fail later during session execution.

Recommended Fix: Add validation helper in integration_validation.go:

// ValidateGitHubTokenFormat checks PAT format (ghp_, gho_, ghs_, ghu_, etc.)
func ValidateGitHubTokenFormat(token string) error {
    if \!strings.HasPrefix(token, "ghp_") && \!strings.HasPrefix(token, "gho_") {
        return fmt.Errorf("invalid GitHub token format")
    }
    return nil
}

Call validation before storing in ConnectJira, ConnectGitLabGlobal, etc.


7. Inconsistent Error Messages (runtime_credentials.go, git/operations.go)

Lines 81, 142, 227, 293 return different error messages for the same scenario:

  • "Failed to get GitHub token" (runtime_credentials.go:81)
  • "no GitHub credentials available. Connect GitHub on the Integrations page" (git/operations.go:105)

Problem: Inconsistent UX, harder to debug.

Recommended Fix: Standardize error messages across all credential endpoints:

const (
    ErrGitHubNotConfigured = "GitHub credentials not configured. Connect GitHub on the Integrations page"
    ErrGitLabNotConfigured = "GitLab credentials not configured. Connect GitLab on the Integrations page"
    ErrJiraNotConfigured   = "Jira credentials not configured. Connect Jira on the Integrations page"
)

8. Missing Token Expiry Handling for Non-Refreshable Tokens (runtime_credentials.go)

Google OAuth tokens have refresh logic (lines 153-166), but GitHub PAT, GitLab PAT, and Jira API tokens do NOT.

Problem: If a PAT expires or is revoked, sessions fail with generic "401 Unauthorized" from GitHub/GitLab API.

Recommended Fix: Add validation before returning tokens:

// For GitHub PAT (after line 78)
token, err := git.GetGitHubToken(...)
if err \!= nil {
    // ... existing error handling
}

// Validate token is still valid
valid, _ := ValidateGitHubToken(c.Request.Context(), token)
if \!valid {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "GitHub token expired or revoked. Please reconnect on Integrations page"})
    return
}

🔵 Minor Issues

9. Missing Unit Tests for New Endpoints

No tests for:

  • GetGitHubTokenForSession
  • GetJiraCredentialsForSession
  • GetGitLabTokenForSession
  • GetGoogleCredentialsForSession

Recommended: Add table-driven tests in runtime_credentials_test.go:

func TestGetGitHubTokenForSession(t *testing.T) {
    tests := []struct {
        name           string
        session        *unstructured.Unstructured
        expectedStatus int
        expectedError  string
    }{
        {"valid session", validSession, http.StatusOK, ""},
        {"session not found", nil, http.StatusNotFound, "Session not found"},
        {"missing userID", sessionNoUserID, http.StatusInternalServerError, "User ID not found"},
        {"RBAC violation", sessionDifferentUser, http.StatusForbidden, "Access denied"},
    }
    // ... test implementation
}

10. Hardcoded Secret Names Without Constants (jira_auth.go:159, gitlab_auth.go:497)

Lines 159, 497 use hardcoded secret names:

const secretName = "jira-credentials"
const secretName = "gitlab-credentials"

Problem: If secret naming changes, must update in multiple places.

Recommended Fix: Define constants at package level:

const (
    GitHubPATSecretName = "github-pat-credentials"
    GitLabSecretName    = "gitlab-credentials"
    JiraSecretName      = "jira-credentials"
)

11. Type Assertion Without Safety Check (runtime_credentials.go:72-76)

Lines 72-76 use type assertion without checking:

k8sClientset, ok := K8sClient.(*kubernetes.Clientset)
if \!ok {
    log.Printf("Failed to convert K8sClient to *kubernetes.Clientset")
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal error"})
    return
}

Problem: While the check exists, the error message is too generic.

Recommended Fix:

if \!ok {
    log.Printf("K8sClient type assertion failed: expected *kubernetes.Clientset, got %T", K8sClient)
    c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal configuration error"})
    return
}

12. Frontend: Missing Error Boundaries for New Components

New components gitlab-connection-card.tsx, jira-connection-card.tsx lack error boundaries.

Recommended: Wrap in Suspense/ErrorBoundary per frontend patterns.


Positive Highlights

Excellent Security: RBAC ownership verification (runtime_credentials.go:57-67) correctly prevents cross-user credential access

Type-Safe Unstructured Access: Consistent use of unstructured.NestedString (runtime_credentials.go:49, 115, 203, 270)

Token Redaction: Comprehensive token redaction in logs (server/server.go:26-50, server_test.go:1-109)

Retry Logic: Proper conflict retry handling for Secret updates (jira_auth.go:161-209, gitlab_auth.go:499-547)

Google Token Refresh: Automatic token refresh before expiry (runtime_credentials.go:153-166)

Operator Simplification: Removed 230 lines of volume mounting logic (operator/sessions.go:-230)

Frontend React Query: Proper use of React Query hooks in new components


Recommendations

Priority 1 (Must Fix Before Merge)

  1. Fix RBAC violation in runtime credentials endpoints (Blocker Outcome: Reduce Refinement Time with agent System #1)
  2. Switch to user-scoped clients for session lookup (Blocker Epic: RAT Architecture & Design #2)

Priority 2 (Should Fix Before Merge)

  1. Add error context to token validation (Critical Epic: Data Source Integration #3)
  2. Extract RBAC ownership verification to shared helper (Critical Epic: AI Agent Development #4)
  3. Fix token redaction for non-Bearer auth headers (Critical Epic: Jira Integration & Workflow #5)

Priority 3 (Can Address in Follow-Up)

  1. Add input validation for token formats (Major Epic: Testing & Validation #6)
  2. Standardize error messages (Major Test: Automation Workflow Validation #7)
  3. Add token expiry validation for PATs (Major Test: Updated Workflow Validation #8)
  4. Add unit tests for new endpoints (Minor Bump actions/add-to-project from 0.5.0 to 1.0.2 #9)

Architecture Improvements

  • Consider adding rate limiting for credential validation endpoints (prevent brute-force token testing)
  • Add audit logging for credential access (who accessed which credentials when)
  • Consider implementing credential rotation support (notify users when tokens are near expiry)

Testing Checklist

Before merge, verify:

  • User A cannot access User B's credentials via session manipulation
  • Invalid tokens are rejected with clear error messages
  • Token refresh works for Google OAuth (long-running sessions)
  • GitHub PAT takes priority over GitHub App tokens
  • Revoked tokens are detected and reported to users
  • All new endpoints return 401 for missing/invalid authentication
  • CI passes with 0 linting errors

Overall: Strong architectural improvement over PR #562, but security issues must be fixed before merge. The runtime fetching approach is the right solution - just needs RBAC hardening.

Generated with Claude Code Review


🔍 View AI decision process (logs available for 90 days)

📋 View memory system files loaded (click to expand)

What Amber Loaded for Code Review

Amber automatically loaded these repository standards from the memory system:

  1. CLAUDE.md - Master project instructions, development standards
  2. backend-development.md - Go backend, K8s integration patterns
  3. frontend-development.md - NextJS, Shadcn UI, React Query patterns
  4. security-standards.md - Auth, RBAC, token handling
  5. k8s-client-usage.md - User token vs service account patterns
  6. error-handling.md - Consistent error patterns
  7. react-query-usage.md - Data fetching patterns

Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines.

Blocker Issues Fixed:
1. Type assertions: Use unstructured.NestedString() instead of unsafe type assertions
2. RBAC validation: Verify authenticated user owns session before returning credentials
3. Token redaction: Extend log redaction to include Authorization headers
4. Token logging: Remove error wrapping that could leak tokens in validation functions
5. Expired tokens: Return 401 error on refresh failure instead of expired token

All 4 runtime credential endpoints now:
- Use type-safe unstructured helpers (CLAUDE.md compliance)
- Verify session ownership (prevent cross-user credential access)
- Return 401 on token refresh failures (force re-authentication)
- Don't leak tokens in error messages or logs
@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

Claude Code Review

Summary

This PR implements runtime credential fetching for GitHub PAT, GitLab, Jira, and Google OAuth credentials. The architectural approach is sound—moving from volume mounting to on-demand API fetching ensures fresh credentials for long-running sessions. However, there are several critical security issues and code quality concerns that must be addressed before merge.


Issues by Severity

🚫 Blocker Issues

1. Token Logging in Server Redaction (components/backend/server/server.go:27-50)

  • Issue: Authorization header redaction ONLY logs "[REDACTED]" but the actual token value is still passed through the request pipeline where it could be logged elsewhere
  • Location: server/server.go:34-42
  • Risk: Token leakage if error logging or debug logging prints full request context
  • Recommendation:
    • Ensure ALL logging paths use the custom formatter
    • Add integration test to verify no tokens appear in logs under error conditions
    • Consider adding a panic handler that redacts tokens from stack traces

2. Missing RBAC Validation in Runtime Credential Endpoints (components/backend/handlers/runtime_credentials.go)

  • Issue: While user ownership is verified (authenticatedUserID == sessionUserID), there's no RBAC check to confirm the user has permission to access the session's project namespace
  • Location: Lines 28-67 (repeated in all 4 credential endpoints)
  • Risk: User could access credentials from sessions in namespaces they shouldn't have access to if they know the session name
  • Recommendation: Add RBAC check using SelfSubjectAccessReview before returning credentials:
ssar := &authv1.SelfSubjectAccessReview{
    Spec: authv1.SelfSubjectAccessReviewSpec{
        ResourceAttributes: &authv1.ResourceAttributes{
            Group:     "vteam.ambient-code",
            Resource:  "agenticsessions",
            Verb:      "get",
            Namespace: project,
        },
    },
}
res, err := reqK8s.AuthorizationV1().SelfSubjectAccessReviews().Create(ctx, ssar, v1.CreateOptions{})
if err != nil || !res.Status.Allowed {
    c.JSON(http.StatusForbidden, gin.H{"error": "Unauthorized"})
    return
}

🔴 Critical Issues

3. Service Account Used for Credential Writes Without User Permission Check (handlers/jira_auth.go, gitlab_auth.go)

  • Issue: storeJiraCredentials and storeGitLabCredentials use K8sClient (service account) to write secrets WITHOUT first validating user has permission to create/update credentials
  • Location:
    • jira_auth.go:154-210
    • gitlab_auth.go (similar pattern)
  • Violation: CLAUDE.md Critical Rule Outcome: Reduce Refinement Time with agent System #1 - "Backend service account ONLY for CR writes and token minting after validation"
  • Recommendation: In ConnectJira, ConnectGitLab, add RBAC check BEFORE calling store functions:
// After getting reqK8s, BEFORE storing
ssar := &authv1.SelfSubjectAccessReview{
    Spec: authv1.SelfSubjectAccessReviewSpec{
        ResourceAttributes: &authv1.ResourceAttributes{
            Resource:  "secrets",
            Verb:      "update",
            Namespace: Namespace,
            Name:      "jira-credentials", // or gitlab-credentials
        },
    },
}
res, err := reqK8s.AuthorizationV1().SelfSubjectAccessReviews().Create(ctx, ssar, v1.CreateOptions{})
if err != nil || !res.Status.Allowed {
    c.JSON(http.StatusForbidden, gin.H{"error": "Unauthorized to manage credentials"})
    return
}

4. Type Assertion Without Check (runtime_credentials.go:71-76)

  • Issue: Direct type assertion k8sClientset, ok := K8sClient.(*kubernetes.Clientset) followed by error if not ok, but this should be validated at initialization, not runtime
  • Location: runtime_credentials.go:71-76
  • Risk: If K8sClient is somehow not a Clientset (testing, configuration error), all credential fetches fail
  • Recommendation:
    • Validate K8sClient type at server startup in main.go
    • Use panic during init if type is wrong (fail fast)
    • Or better: refactor git.GetGitHubToken to accept kubernetes.Interface instead of *kubernetes.Clientset

5. Validation Functions Return Bool + Error But Ignore Error in Status Checks (integrations_status.go:67, 115, 133)

  • Issue: ValidateGitHubToken, ValidateJiraToken, ValidateGitLabToken can return (false, error), but callers only check valid, ignoring the error. This could hide network failures as "invalid token"
  • Location:
    • integrations_status.go:67: valid, _ := ValidateGitHubToken(...)
    • integrations_status.go:115: valid, _ := ValidateJiraToken(...)
    • integrations_status.go:133: valid, _ := ValidateGitLabToken(...)
  • Risk: User sees "token invalid" when it's actually a network error or API outage
  • Recommendation: Check errors and distinguish "invalid" from "unable to validate":
valid, err := ValidateGitHubToken(ctx, patCreds.Token)
if err != nil {
    status["pat"] = gin.H{
        "configured": true,
        "valid": false,
        "validationError": "Unable to validate (network error)",
    }
} else {
    status["pat"] = gin.H{
        "configured": true,
        "valid": valid,
    }
}

6. Hardcoded Timeouts Without Context (integration_validation.go)

  • Issue: HTTP clients created with Timeout: 10 * time.Second but contexts are passed to NewRequestWithContext - the context timeout is ignored if client timeout triggers first
  • Location:
    • integration_validation.go:18, 47, 74, 103
  • Recommendation: Use context for timeout control:
client := &http.Client{} // No timeout
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
// Use ctx in NewRequestWithContext

🟡 Major Issues

7. Excessive Code Duplication in Runtime Credential Handlers

  • Issue: All 4 credential endpoints (GitHub, Google, Jira, GitLab) have nearly identical structure (lines 24-67 repeated)
  • Location: runtime_credentials.go - all 4 functions
  • Impact: Maintenance burden, copy-paste errors, harder to fix bugs
  • Recommendation: Extract common logic into helper:
func validateSessionAccess(c *gin.Context, project, session string) (userID string, err error) {
    reqK8s, reqDyn := GetK8sClientsForRequest(c)
    if reqK8s == nil {
        return "", fmt.Errorf("unauthorized")
    }
    
    // Get session, extract userID, verify ownership
    // Return userID or error
}

func GetGitHubTokenForSession(c *gin.Context) {
    project := c.Param("projectName")
    session := c.Param("sessionName")
    
    userID, err := validateSessionAccess(c, project, session)
    if err != nil {
        c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
        return
    }
    
    // Now just fetch GitHub token for userID
}

8. Missing Input Sanitization in Jira/GitLab Connection

  • Issue: URL inputs are not validated or sanitized before storage
  • Location:
    • jira_auth.go:47-56 (accepts any URL string)
    • gitlab_auth.go (similar)
  • Risk: Could store malformed URLs that cause runtime errors during validation
  • Recommendation: Add URL parsing and validation:
parsedURL, err := url.Parse(req.URL)
if err != nil || parsedURL.Scheme == "" || parsedURL.Host == "" {
    c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid URL format"})
    return
}
// Store normalized URL
creds.URL = parsedURL.String()

9. Secret Update Race Conditions Not Handled Robustly

  • Issue: Retry loops for secret updates (jira_auth.go:161-209) use 3 retries but don't implement exponential backoff
  • Location:
    • jira_auth.go:161, 245
    • gitlab_auth.go (similar pattern)
  • Impact: Under high concurrency, all 3 retries could fail due to rapid conflicts
  • Recommendation: Add exponential backoff:
for i := 0; i < 5; i++ { // Increase retries
    secret, err := K8sClient.CoreV1().Secrets(Namespace).Get(ctx, secretName, v1.GetOptions{})
    // ... update secret ...
    if _, uerr := K8sClient.CoreV1().Secrets(Namespace).Update(ctx, secret, v1.UpdateOptions{}); uerr != nil {
        if errors.IsConflict(uerr) {
            time.Sleep(time.Duration(i*100) * time.Millisecond) // Exponential backoff
            continue
        }
        return uerr
    }
    return nil
}

10. Inconsistent Error Messages Expose Internal State

  • Issue: Some errors return detailed internal messages (e.g., "failed to convert K8sClient to *kubernetes.Clientset") which could aid attackers
  • Location: runtime_credentials.go:74
  • Recommendation: Return generic "Internal error" to user, log detailed error internally

🔵 Minor Issues

11. Unused Variable in GitLab/Jira Test Functions

  • Issue: TestGitLabConnection and TestJiraConnection return valid, err from validation but ignore err in response
  • Location: integration_validation.go:137-179
  • Impact: User doesn't know if failure is due to invalid credentials or network error
  • Recommendation: Include error details in response

12. Hardcoded Secret Names Without Constants

  • Issue: "jira-credentials", "gitlab-credentials" repeated throughout code
  • Location: Multiple files (jira_auth.go:159, 218, gitlab_auth.go similar)
  • Recommendation: Define constants:
const (
    JiraCredentialsSecret = "jira-credentials"
    GitLabCredentialsSecret = "gitlab-credentials"
    GitHubPATSecret = "github-pat-credentials"
)

13. Missing godoc Comments on Public Functions

  • Issue: GetGitLabCredentials, GetJiraCredentials, storeGitLabCredentials lack godoc comments
  • Impact: Harder to understand API surface for future maintainers
  • Recommendation: Add godoc comments following Go conventions

14. Frontend: Potential TypeScript any Type Usage

  • Issue: Unable to verify without full frontend diff, but new API routes may use any types
  • Location: components/frontend/src/app/api/auth/**
  • Recommendation: Ensure all new frontend code follows type over interface and zero any types rule from DESIGN_GUIDELINES.md

15. Adapter.py: Comment Says Credentials Fetched On-Demand But Doesn't Show Implementation

  • Issue: adapter.py:89-92 says "Credentials will be fetched on-demand from backend API" but the diff doesn't show _populate_runtime_credentials() implementation
  • Location: adapter.py:89-92
  • Recommendation: Verify _populate_runtime_credentials() actually makes API calls to fetch credentials (check PR diff for this function)

Positive Highlights

Excellent architectural improvement: Runtime fetching vs. volume mounting is much cleaner and ensures fresh credentials

Good RBAC ownership checks: Verifying authenticatedUserID == sessionUserID prevents cross-user credential access

Type-safe unstructured access: Correctly using unstructured.NestedString throughout (runtime_credentials.go:49, 115, 203, 270)

Comprehensive token redaction: server.go custom formatter redacts tokens in URLs and auth headers

Proper retry logic for Secret conflicts: Using retry loops for concurrent Secret updates (though needs backoff)

User-scoped secrets: Credentials keyed by sanitized userID for multi-tenant isolation

Validation endpoints: Test connections before saving credentials is great UX


Recommendations

Priority 1 (Must Fix Before Merge)

  1. Fix RBAC validation gap in runtime credential endpoints (Blocker Epic: RAT Architecture & Design #2)
  2. Add permission checks before credential storage (Critical Epic: Data Source Integration #3)
  3. Fix token redaction under error conditions (Blocker Outcome: Reduce Refinement Time with agent System #1)
  4. Handle validation errors properly in status checks (Critical Epic: Jira Integration & Workflow #5)

Priority 2 (Should Fix Before Merge)

  1. Refactor duplicated code in credential handlers (Major Test: Automation Workflow Validation #7)
  2. Add URL sanitization for Jira/GitLab (Major Test: Updated Workflow Validation #8)
  3. Fix type assertion pattern (Critical Epic: AI Agent Development #4)

Priority 3 (Can Fix Post-Merge)

  1. Add exponential backoff to Secret retries (Major Bump actions/add-to-project from 0.5.0 to 1.0.2 #9)
  2. Extract hardcoded secret names to constants (Minor Add vTeam shared Claude Code configuration with hooks-based enforcement #12)
  3. Add godoc comments (Minor Review context7 MCP server and patternfly team usage #13)
  4. Verify frontend TypeScript types (Minor Add OpenShift AI Virtual Team agents as source of truth #14)

Testing Recommendations

Security Testing:

  • Verify user A cannot access user B's credentials (cross-tenant)
  • Verify user cannot access credentials from sessions they don't own
  • Verify tokens never appear in logs (grep logs for real tokens)
  • Test RBAC boundaries - user without session access can't fetch creds

Integration Testing:

  • Test concurrent credential updates (Secret conflict handling)
  • Test credential refresh flow for long-running sessions
  • Verify GitHub token precedence (PAT > App > fallback)
  • Test all 4 credential types (GitHub, GitLab, Jira, Google)

Error Handling:

  • Test behavior when credentials API returns 500
  • Test behavior when validation APIs are down
  • Test invalid URL inputs to Jira/GitLab

Overall Assessment

Architecture: ✅ Excellent - runtime fetching is the right approach

Security: ❌ Blocker issues must be fixed - RBAC gaps and potential token logging

Code Quality: ⚠️ Needs improvement - excessive duplication, missing error handling

Recommendation: Request Changes - Fix blockers and critical issues, then re-review.


🤖 Generated with Claude Code


🔍 View AI decision process (logs available for 90 days)

📋 View memory system files loaded (click to expand)

What Amber Loaded for Code Review

Amber automatically loaded these repository standards from the memory system:

  1. CLAUDE.md - Master project instructions, development standards
  2. backend-development.md - Go backend, K8s integration patterns
  3. frontend-development.md - NextJS, Shadcn UI, React Query patterns
  4. security-standards.md - Auth, RBAC, token handling
  5. k8s-client-usage.md - User token vs service account patterns
  6. error-handling.md - Consistent error patterns
  7. react-query-usage.md - Data fetching patterns

Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines.

- Check parsedURL.User to catch user@host syntax
- Test expects 400 BadRequest for invalid URL format
- Prevents injection via user info in URL
@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

Claude Code Review

Summary

PR #568 implements runtime credential fetching for GitHub PAT, GitLab, and Jira tokens. The implementation demonstrates excellent adherence to established patterns with strong security compliance. The architectural shift from volume-mounted credentials to on-demand API fetching is a significant improvement.

Recommendation: APPROVE ✅ (with 3 minor non-blocking suggestions)


Issues by Severity

🚫 Blocker Issues

None - All critical security patterns are compliant.

🔴 Critical Issues

None - RBAC, authentication, and token handling all follow CLAUDE.md standards.

🟡 Major Issues

None - Type-safe unstructured access, error handling, and logging patterns are correct.

🔵 Minor Issues (Non-Blocking)

1. Missing URL Validation in Jira Handler

File: components/backend/handlers/jira_auth.go:58-62

Issue: Basic validation only checks empty string, missing format validation unlike GitLab handler.

if req.URL == "" {
    c.JSON(http.StatusBadRequest, gin.H{"error": "Jira URL is required"})
    return
}
// No further URL validation\!

Recommendation: Add validation similar to gitlab_auth.go:66-93:

parsedURL, err := url.Parse(req.URL)
if err \!= nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid URL format"})
    return
}
if parsedURL.Scheme \!= "https" {
    c.JSON(http.StatusBadRequest, gin.H{"error": "URL must use HTTPS"})
    return
}
if parsedURL.User \!= nil {
    c.JSON(http.StatusBadRequest, gin.H{"error": "URL must not contain userinfo"})
    return
}

Impact: Low (malformed URLs will fail at API call time)

2. Token Format Validation Completeness

File: components/backend/handlers/github_auth.go:537-542

Issue: Missing ghc_ prefix (GitHub App user-to-server token) in validation.

if \!strings.HasPrefix(req.Token, "ghp_") && \!strings.HasPrefix(req.Token, "gho_") &&
    \!strings.HasPrefix(req.Token, "ghu_") && \!strings.HasPrefix(req.Token, "ghs_") &&
    \!strings.HasPrefix(req.Token, "github_pat_") {
    // Missing: ghc_ for GitHub App tokens

Impact: Very Low (GitHub App tokens are minted by backend, not user-provided)

3. Error Message Consistency

File: components/backend/handlers/integration_validation.go:29-30, 59-60

Current approach:

// Don't wrap error - could leak token from request details
return false, fmt.Errorf("request failed")

Note: This is good security practice (prevents token leakage), but generic messages may hinder debugging.

Suggestion: Add internal structured logging while keeping user-facing errors generic:

log.Printf("GitHub token validation failed: %v", err) // Internal debug
return false, fmt.Errorf("request failed") // Generic user message

Impact: Very Low (debugging convenience only)


Positive Highlights

🔒 Excellent Security Compliance

1. User Token Authentication (All 4 Credential Endpoints)

runtime_credentials.go correctly uses user-scoped clients:

reqK8s, reqDyn := GetK8sClientsForRequest(c)
if reqK8s == nil {
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or missing token"})
    return
}

2. RBAC Enforcement - Session Ownership Verification

✅ Excellent pattern prevents credential leakage between users (runtime_credentials.go:56-67):

authenticatedUserID := c.GetString("userID")
if authenticatedUserID \!= userID {
    log.Printf("RBAC violation: user %s attempted to access credentials for session owned by %s", 
               authenticatedUserID, userID)
    c.JSON(http.StatusForbidden, gin.H{"error": "Access denied"})
    return
}

3. Type-Safe Unstructured Access

✅ All handlers use unstructured.Nested* helpers with proper three-value return checking:

userID, found, err := unstructured.NestedString(obj.Object, "spec", "userContext", "userId")
if \!found || err \!= nil || userID == "" {
    log.Printf("Failed to extract userID: found=%v, err=%v", found, err)
    c.JSON(http.StatusInternalServerError, gin.H{"error": "User ID not found"})
    return
}

4. Token Redaction

✅ No tokens logged anywhere, enhanced redaction in server.go:27-51

🏗️ Architectural Improvements

Operator simplification: Removed ~230 lines of volume mounting logic
Fresh tokens: Runtime fetching ensures always-current credentials
Security improvement: No stale credentials in long-running sessions
Decoupled design: Runner no longer depends on volume mounts

📝 Code Quality

Pattern consistency: All handlers follow established error handling patterns
Proper logging: Contextual logs with no sensitive data
Input validation: URL format validation in GitLab handler is exemplary
Well-documented: Clear comments explaining security decisions


Recommendations

Priority 1: Before Merge

None - All critical patterns are compliant.

Priority 2: Nice to Have (Post-Merge)

  1. Add URL validation to jira_auth.go similar to GitLab handler
  2. Add ghc_ prefix to GitHub token validation (low priority)
  3. Consider adding internal structured logging in validation functions

Pre-Commit Checklist Status

  • Authentication: All user-facing endpoints use GetK8sClientsForRequest(c)
  • Authorization: RBAC checks performed (session ownership + secret access)
  • Error Handling: All errors logged with context, appropriate HTTP status codes
  • Token Security: No tokens or sensitive data in logs
  • Type Safety: Used unstructured.Nested* helpers, checked found before using values
  • Resource Cleanup: Not applicable (no child resources created)
  • Status Updates: Not applicable (no CR status changes)
  • Logging: Structured logs with relevant context (namespace, resource name, userID)
  • Code Quality: gofmt, go vet, golangci-lint passing

Files Reviewed

Backend (Go)

  • handlers/runtime_credentials.go (NEW - 379 lines) - Zero critical issues
  • handlers/github_auth.go (+288/-12) - Excellent OAuth flow
  • handlers/gitlab_auth.go (+242/-42) - Exemplary URL validation
  • handlers/jira_auth.go (NEW - 269 lines) - 1 minor issue (URL validation)
  • handlers/integration_validation.go (NEW - 180 lines) - Zero issues
  • handlers/integrations_status.go (NEW - 141 lines) - Zero issues
  • server/server.go (+70/-2) - Enhanced sanitization

Operator (Go)

  • internal/handlers/sessions.go (+15/-230) - Excellent simplification

Runner (Python)

  • adapter.py (+166/-141) - Zero issues, proper error handling

Summary

This PR is production-ready with excellent security compliance and code quality. The three minor issues identified are low-impact and do not block merge. The architectural shift from volume-mounted credentials to runtime API fetching is a significant security and reliability improvement.

🎉 Great work adhering to CLAUDE.md standards!


🤖 Generated by Claude Code Review


🔍 View AI decision process (logs available for 90 days)

📋 View memory system files loaded (click to expand)

What Amber Loaded for Code Review

Amber automatically loaded these repository standards from the memory system:

  1. CLAUDE.md - Master project instructions, development standards
  2. backend-development.md - Go backend, K8s integration patterns
  3. frontend-development.md - NextJS, Shadcn UI, React Query patterns
  4. security-standards.md - Auth, RBAC, token handling
  5. k8s-client-usage.md - User token vs service account patterns
  6. error-handling.md - Consistent error patterns
  7. react-query-usage.md - Data fetching patterns

Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines.

Tests now expect 401 Unauthorized when auth is missing instead of 400 BadRequest.
This aligns with security improvements where authentication is checked first.

Updated tests:
- ConnectGitLabGlobal: Now tests auth requirement (not project name)
- GetGitLabStatusGlobal: Now tests auth requirement
- DisconnectGitLabGlobal: Now tests auth requirement
- Invalid user ID type: Expects 401 (GetString returns empty for non-string)
- JSON validation: Don't check exact Gin validation message (varies)
@github-actions
Copy link
Contributor

github-actions bot commented Feb 2, 2026

Claude Code Review

Summary

This PR implements runtime credential fetching for GitHub PAT, GitLab, and Jira tokens, replacing the volume mounting approach from PR #562. The implementation follows CLAUDE.md standards and introduces critical security fixes that were identified during review.

Overall Assessment: ✅ APPROVE with recommendations

The code quality is excellent, with comprehensive security improvements addressing multiple RBAC and token handling issues. All backend patterns align with project standards.


Issues by Severity

🚫 Blocker Issues

None - All critical security issues were fixed in commit e10bdcd.


🔴 Critical Issues (FIXED in this PR)

The following critical issues were identified and resolved during development:

1. ✅ Type-Safe Unstructured Access (Fixed)

Location: components/backend/handlers/runtime_credentials.go

Issue: Original code used unsafe type assertions that could panic:

// Before (unsafe)
if spec, ok := obj.Object["spec"].(map[string]interface{}); ok { ... }

Resolution: Now uses type-safe unstructured.NestedString() helper (CLAUDE.md compliance):

// After (safe)
userID, found, err := unstructured.NestedString(obj.Object, "spec", "userContext", "userId")
if \!found || err \!= nil { /* handle error */ }

Applied consistently to all 4 credential endpoints (lines 48, 115, 203, 270).


2. ✅ RBAC Session Ownership Validation (Fixed)

Location: components/backend/handlers/runtime_credentials.go

Issue: Original implementation lacked verification that the authenticated user owns the session being accessed. Any authenticated user could potentially access any session's credentials.

Resolution: Added 3-layer RBAC validation to all 4 endpoints:

// 1. Validate user token
reqK8s, reqDyn := GetK8sClientsForRequest(c)
if reqK8s == nil { return 401 }

// 2. Fetch session with user-scoped client (K8s RBAC)
obj, err := reqDyn.Resource(gvr).Namespace(project).Get(...)

// 3. Verify session ownership
authenticatedUserID := c.GetString("userID")  // From middleware
if authenticatedUserID \!= sessionOwnerID {
    log.Printf("RBAC violation: user %s attempted to access credentials for session owned by %s", authenticatedUserID, sessionOwnerID)
    return 403 Forbidden
}

Security Chain:

  • User-scoped K8s client ensures namespace isolation
  • Session userID extraction verifies ownership
  • Audit logging for violation attempts

Applied to: GitHub (L56-67), Google (L122-133), Jira (L210-221), GitLab (L277-288)


3. ✅ Token Redaction in Logs (Fixed)

Location: components/backend/handlers/integration_validation.go

Issue: Error wrapping with %w could leak tokens through HTTP client error messages.

Resolution: Removed error wrapping in all 4 validation functions:

// Before (risky)
return false, fmt.Errorf("request failed: %w", err)  // ← Could leak Authorization header

// After (safe)
return false, fmt.Errorf("request failed")  // ← No token exposure

Applied to: GitHub (L19), GitLab (L57), Jira (L86), Google (L112)


4. ✅ Authorization Header Redaction (Fixed)

Location: components/backend/server/server.go:34-41

Issue: HTTP access logs exposed Authorization headers.

Resolution: Enhanced log formatter to redact Bearer tokens:

authHeader := "[none]"
if auth := param.Request.Header.Get("Authorization"); auth \!= "" {
    if strings.HasPrefix(auth, "Bearer ") {
        authHeader = "Bearer [REDACTED]"
    } else {
        authHeader = "[REDACTED]"
    }
}

Log Output Example:

  • Before: [GIN] GET | 200 | 127.0.0.1 | /api/projects/myproject/sessions/s1/credentials/github
  • After: [GIN] GET | 200 | 127.0.0.1 | /api/projects/myproject/sessions/s1/credentials/github | Auth: Bearer [REDACTED]

5. ✅ Expired Token Handling (Fixed)

Location: components/backend/handlers/runtime_credentials.go:158-162

Issue: Original code returned expired tokens as fallback when refresh failed.

Resolution: Now returns 401 Unauthorized, forcing re-authentication:

if err \!= nil {
    log.Printf("Failed to refresh Google token for user %s: %v", userID, err)
    c.JSON(http.StatusUnauthorized, gin.H{"error": "Google token expired and refresh failed. Please re-authenticate."})
    return  // ← Force re-auth instead of serving expired token
}

🟡 Major Issues

1. Missing Test Coverage for RBAC Validation

Impact: No automated tests verify that users cannot access other users' session credentials.

Recommendation: Add integration tests for all 4 endpoints:

// Test: User A attempts to access User B's session credentials
func TestGetGitHubTokenRBACViolation(t *testing.T) {
    // Setup: Create session owned by user-b
    session := createSession("user-b", "myproject")
    
    // Act: User-a attempts to fetch credentials
    resp := makeRequest("GET", "/credentials/github", tokenForUser("user-a"))
    
    // Assert: Should return 403 Forbidden, not 200 OK
    assert.Equal(t, http.StatusForbidden, resp.StatusCode)
    assert.Contains(t, resp.Body, "Access denied")
}

Suggested test file: components/backend/handlers/runtime_credentials_test.go

Priority: Medium - Current manual testing likely catches this, but automated coverage is important.


2. Inconsistent Error Message Detail

Location: components/backend/handlers/runtime_credentials.go:81

Issue: GitHub token endpoint returns err.Error() which could expose internal details:

token, err := git.GetGitHubToken(...)
if err \!= nil {
    c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})  // ← Potential detail leakage
    return
}

Comparison: Other endpoints use generic messages:

  • Google: "Failed to get Google credentials" (L143)
  • Jira: "Failed to get Jira credentials" (L227)
  • GitLab: "GitLab credentials not configured" (L299)

Recommendation: Change to:

if err \!= nil {
    log.Printf("Failed to get GitHub token for user %s: %v", userID, err)
    c.JSON(http.StatusNotFound, gin.H{"error": "GitHub credentials not found"})
    return
}

Priority: Low-Medium - Unlikely to leak sensitive data, but improves consistency.


🔵 Minor Issues

1. Operator Volume Mounting Cleanup

Location: components/operator/internal/handlers/sessions.go

Observation: PR removes 230 lines of volume mounting logic. Excellent simplification!

Note: Comment at L409 references "runtime fetching", but no code enforces that credentials are fetched before use.

Recommendation: Consider adding a startup check in the runner to fail fast if credential fetching fails:

# adapter.py:_populate_runtime_credentials()
if not self._fetch_github_credentials():
    raise RuntimeError("Failed to fetch GitHub credentials at startup")

Priority: Low - Current implementation logs warnings, which is acceptable.


2. GitHub Token Precedence Logic

Location: components/backend/git/operations.go:77-130

Observation: New precedence order is well-documented:

  1. User's GitHub PAT (highest priority)
  2. GitHub App installation token (1hr lifetime)
  3. Project-level GITHUB_TOKEN (deprecated)

Note: Lines 117-130 handle GitHub App token minting with service account K8sClient, which is correct per CLAUDE.md standards (backend SA for privileged operations).

Recommendation: None - Implementation is correct and well-structured.


Positive Highlights

✅ Architecture Improvements

  1. Simplified Operator: Removed 230 lines of volume mounting complexity
  2. Runtime Credential Fetching: Ensures tokens are always fresh for long-running sessions
  3. Multi-Credential Support: Unified architecture for GitHub, GitLab, Jira, Google
  4. Token Precedence: User PAT takes priority over GitHub App, giving users control

✅ Security Excellence

  1. User Token Authentication: All 4 endpoints use GetK8sClientsForRequest(c) (CLAUDE.md compliance)
  2. RBAC Enforcement: Session ownership verified via authenticatedUserID == sessionOwnerID pattern
  3. Type-Safe K8s Access: Uses unstructured.NestedString() instead of unsafe assertions
  4. Token Redaction: Comprehensive redaction in logs (Authorization headers, error wrapping)
  5. Expired Token Handling: Forces re-authentication instead of serving stale credentials

✅ Code Quality

  1. Consistent Error Handling: Follows CLAUDE.md patterns (log + return generic message)
  2. DRY Principle: 4 credential endpoints follow identical RBAC pattern (copy-paste consistency)
  3. Documentation: Inline comments explain token precedence and security checks
  4. Migration Path: Maintains backward compatibility with project-level secrets

✅ Frontend Integration

  1. New UI Components: GitLab and Jira connection cards match GitHub patterns
  2. React Query Hooks: All data fetching uses proper React Query patterns (CLAUDE.md compliance)
  3. API Layer Separation: services/api/* and services/queries/* properly separated
  4. Type Safety: No any types in frontend changes

Recommendations

Immediate (Before Merge)

  1. All critical issues fixed - No blockers remain
  2. ⚠️ Consider adding error message consistency fix (L81 in runtime_credentials.go)

Follow-Up PRs

  1. Add RBAC Violation Tests (Priority: Medium)

    • File: components/backend/handlers/runtime_credentials_test.go
    • Test cases: Cross-user credential access (should return 403)
  2. Runner Startup Checks (Priority: Low)

    • File: components/runners/claude-code-runner/adapter.py
    • Fail fast if credential fetching fails at session startup

CLAUDE.md Compliance Review

Standard Status Details
User Token Authentication ✅ Pass All 4 endpoints use GetK8sClientsForRequest(c)
RBAC Enforcement ✅ Pass Session ownership verified via userID comparison
Type-Safe Unstructured Access ✅ Pass Uses unstructured.NestedString() helpers
No Token Logging ✅ Pass Error wrapping removed, Authorization headers redacted
Token Redaction ✅ Pass Comprehensive redaction in server.go and validation.go
Error Handling ✅ Pass Explicit error returns with context logging
No Panic in Production ✅ Pass No panic statements in credential endpoints
Service Account Usage ✅ Pass Only used for GitHub App token minting (correct pattern)

Final Verdict

Status: ✅ APPROVE

This PR represents high-quality, security-focused engineering. All critical backend patterns align with CLAUDE.md standards, and the architectural simplification (removing volume mounting) significantly improves maintainability.

The comprehensive security fixes (RBAC, token redaction, type safety) demonstrate attention to detail and proper understanding of the platform's security model. The frontend changes follow established patterns with proper React Query usage and type safety.

Recommendation: Merge after CI passes. Follow up with test coverage PR to validate RBAC behavior.

Commit reviewed: e10bdcd


🔍 View AI decision process (logs available for 90 days)

📋 View memory system files loaded (click to expand)

What Amber Loaded for Code Review

Amber automatically loaded these repository standards from the memory system:

  1. CLAUDE.md - Master project instructions, development standards
  2. backend-development.md - Go backend, K8s integration patterns
  3. frontend-development.md - NextJS, Shadcn UI, React Query patterns
  4. security-standards.md - Auth, RBAC, token handling
  5. k8s-client-usage.md - User token vs service account patterns
  6. error-handling.md - Consistent error patterns
  7. react-query-usage.md - Data fetching patterns

Impact: This review used your repository's specific code quality standards, security patterns, and best practices from the memory system (PRs #359, #360) - not just generic code review guidelines.

@Gkrumbach07 Gkrumbach07 merged commit 312471a into ambient-code:main Feb 2, 2026
18 of 19 checks passed
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