Skip to content

[User Story] Implement Secure Git Client with Authentication #43

@prodigy

Description

@prodigy

User Story

As a FlowForge system, I want to securely clone and interact with Git repositories using both SSH and HTTPS authentication, so that user repositories can be accessed safely without exposing credentials.

Acceptance Criteria

  • Git client supports both SSH and HTTPS authentication methods
  • SSH keys are stored securely and never exposed in logs or errors
  • HTTPS credentials (PAT/username+password) are encrypted at rest
  • Git operations timeout after configurable duration (default: 5 minutes)
  • Repository URLs are validated against whitelist patterns
  • Failed authentication attempts are logged with rate limiting
  • Support for custom SSH configurations (port, known_hosts)
  • Credentials are isolated per job and cleaned up after execution
  • Git client respects proxy settings for corporate environments
  • All Git commands are executed with minimal required permissions

Technical Implementation

Git Client Service Structure

// pkg/git/client.go
package git

import (
    "context"
    "crypto/rsa"
    "github.com/go-git/go-git/v5"
    "github.com/go-git/go-git/v5/plumbing/transport"
    "github.com/go-git/go-git/v5/plumbing/transport/http"
    "github.com/go-git/go-git/v5/plumbing/transport/ssh"
)

type GitClient struct {
    config      *Config
    validator   *URLValidator
    credManager *CredentialManager
    logger      *Logger
}

type Config struct {
    Timeout           time.Duration
    MaxRetries        int
    AllowedURLPatterns []string
    ProxyURL          string
    SSHKeyPath        string
    KnownHostsPath    string
}

type Credentials struct {
    Type      CredentialType // SSH, HTTPS_TOKEN, HTTPS_BASIC
    SSHKey    *SSHKeyData
    HTTPSAuth *HTTPSAuthData
}

func (c *GitClient) Clone(ctx context.Context, url string, creds Credentials) (*git.Repository, error) {
    // Validate URL against whitelist
    if err := c.validator.ValidateURL(url); err != nil {
        return nil, fmt.Errorf("invalid repository URL: %w", err)
    }
    
    // Create auth method based on credentials
    auth, err := c.createAuthMethod(creds)
    if err != nil {
        return nil, fmt.Errorf("failed to create auth: %w", err)
    }
    
    // Clone with timeout
    cloneCtx, cancel := context.WithTimeout(ctx, c.config.Timeout)
    defer cancel()
    
    return git.PlainCloneContext(cloneCtx, path, false, &git.CloneOptions{
        URL:               url,
        Auth:              auth,
        Progress:          c.logger,
        SingleBranch:      true,
        Depth:             1,
        RecurseSubmodules: git.NoRecurseSubmodules,
    })
}

Credential Management

// pkg/git/credentials.go
type CredentialManager struct {
    encryption *EncryptionService
    storage    CredentialStorage
}

func (m *CredentialManager) StoreCredentials(jobID string, creds Credentials) error {
    encrypted, err := m.encryption.Encrypt(creds)
    if err != nil {
        return fmt.Errorf("encryption failed: %w", err)
    }
    
    // Store with TTL
    return m.storage.Store(jobID, encrypted, 30*time.Minute)
}

func (m *CredentialManager) GetCredentials(jobID string) (*Credentials, error) {
    encrypted, err := m.storage.Get(jobID)
    if err != nil {
        return nil, fmt.Errorf("credential not found: %w", err)
    }
    
    return m.encryption.Decrypt(encrypted)
}

// Clean up after job completion
func (m *CredentialManager) CleanupCredentials(jobID string) error {
    return m.storage.Delete(jobID)
}

Security Validation

// pkg/git/validator.go
type URLValidator struct {
    allowedPatterns []*regexp.Regexp
    blockedDomains  []string
}

func (v *URLValidator) ValidateURL(url string) error {
    // Parse URL
    parsed, err := giturl.Parse(url)
    if err != nil {
        return fmt.Errorf("invalid URL format: %w", err)
    }
    
    // Check against blocklist
    for _, blocked := range v.blockedDomains {
        if strings.Contains(parsed.Host, blocked) {
            return fmt.Errorf("blocked domain: %s", parsed.Host)
        }
    }
    
    // Check against allowlist patterns
    allowed := false
    for _, pattern := range v.allowedPatterns {
        if pattern.MatchString(url) {
            allowed = true
            break
        }
    }
    
    if !allowed {
        return fmt.Errorf("URL not in allowlist: %s", url)
    }
    
    return nil
}

Architecture References

Git Service Design

Reference: /docs/02-system-components.md:222-367

The Git Service implements secure repository operations with comprehensive validation:

class SecureGitService:
    def __init__(self, validation_service, security_scanner):
        self.validation = validation_service
        self.security = security_scanner
        self.conflict_resolver = ConflictResolver()
        
    async def clone_repository(self, url: str, branch: str, depth: int = 1) -> Repository:
        """Clone repository with shallow depth and security checks"""
        # Validate repository URL
        if not self.security.validate_git_url(url):
            raise SecurityException("Invalid or unauthorized repository URL")

Security Considerations

Reference: /docs/01-architecture-overview.md:153-172

The architecture emphasizes security-first design:

  • Encryption at rest for all sensitive data
  • TLS 1.3 for all communications
  • Temporary credentials for Git operations
  • Audit logging for compliance

Database Schema for Credentials

Reference: /docs/02-system-components.md:535-551

CREATE TABLE repositories (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    credentials JSONB, -- Encrypted
    settings JSONB DEFAULT '{}',
    ...
);

Dependencies

  • go-git/v5: Pure Go Git implementation
  • golang.org/x/crypto: For SSH key handling
  • PostgreSQL: Credential storage (encrypted)
  • Redis: Temporary credential caching

Definition of Done

  • Unit tests cover all authentication methods with 90%+ coverage
  • Integration tests verify Git operations against test repositories
  • Security scan passes with no high/critical vulnerabilities
  • Documentation includes credential setup guide
  • Performance tests show <2s for typical clone operations
  • Error messages don't expose sensitive information
  • Credentials are properly cleaned up in all code paths

Effort Estimate

8 Story Points - Complex security requirements and multiple auth methods

Labels

  • backend
  • git
  • security
  • priority-high
  • epic-5

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions