Date: August 15, 2025
Reviewer: Claude Code
Repository: tiger-cli
This comprehensive code review identified 12 significant issues across security, architecture, performance, and code quality categories. The codebase demonstrates good overall architecture patterns and secure credential handling practices.
Risk Distribution:
- Critical: 0 issues
- High: 1 issue (under review) ⬅️ 3 RESOLVED
- Medium: 6 issues (security, concurrency, error handling, API design)
- Low: 2 issues (testing, code quality)
Previous Critical Issue Corrected: The originally identified "credential exposure in process arguments" was a false positive. Upon detailed analysis, Tiger CLI correctly implements secure credential handling:
- Connection string: Contains only
postgresql://username@host:port/database(no password) - Password handling: Uses PostgreSQL's standard
PGPASSWORDenvironment variable - Process visibility: Command line arguments are safe (
psql postgresql://tsdbadmin@host:5432/tsdb) - Security: Follows PostgreSQL best practices for credential separation
File: /Users/cevian/Development/tiger-cli/internal/tiger/cmd/auth.go:27-33
Severity: High → RESOLVED
Problem: Test detection logic previously relied on binary name suffix and command line arguments, which could be easily bypassed.
Solution Implemented: Replaced complex detection logic with Go's built-in testing.Testing() function:
func getServiceName() string {
// Use Go's built-in testing detection
if testing.Testing() {
return "tiger-cli-test"
}
return serviceName
}Impact: ✅ Eliminated keyring pollution risk during testing and security bypass potential.
File: /Users/cevian/Development/tiger-cli/internal/tiger/config/config.go (previously line 30)
Severity: High → RESOLVED
Problem: Global config singleton previously created hidden dependencies and made testing difficult.
Solution Implemented: Removed global singleton pattern and implemented proper instantiation:
// Before: Global singleton with caching
var globalConfig *Config
func Load() (*Config, error) {
if globalConfig != nil {
return globalConfig, nil // Return cached instance
}
// ... cache new instance in globalConfig
}
// After: Fresh instance creation every time
func Load() (*Config, error) {
cfg := &Config{
ConfigDir: GetConfigDir(),
}
// Use viper to unmarshal fresh config each time
if err := viper.Unmarshal(cfg); err != nil {
return nil, fmt.Errorf("error unmarshaling config: %w", err)
}
return cfg, nil // Return new instance
}Impact: ✅ Eliminated race conditions, improved testability, removed hidden dependencies. Each config.Load() call now returns a fresh instance based on current viper state.
File: /Users/cevian/Development/tiger-cli/internal/tiger/cmd/db.go:338-353
Severity: High → UNDER REVIEW
Problem: User-provided psql arguments are passed directly without validation or sanitization.
func separateServiceAndPsqlArgs(cmd ArgsLenAtDashProvider, args []string) ([]string, []string) {
// No validation of psql arguments
serviceArgs := args[:argsLenAtDash]
psqlFlags := args[argsLenAtDash:]
return serviceArgs, psqlFlags
}Initial Assessment: Command injection vulnerabilities if malicious arguments are crafted.
- CLI runs with user privileges (no privilege escalation)
- Users who can inject malicious args can run malicious commands directly
- Main risk may be in scripted/automated usage or web service integrations
Potential Solutions:
- Whitelist allowed psql flags for defensive programming
- Add basic input sanitization to prevent accidents in automation
- Document safe usage patterns for integrations
Action Required: Please review whether this constitutes a genuine security vulnerability or should be downgraded to a code quality/defensive programming issue.
File: /Users/cevian/Development/tiger-cli/internal/tiger/api/client_util.go:22-48
Severity: High → RESOLVED
Problem: HTTP client previously had no connection pooling limits or cleanup, potentially leading to resource exhaustion.
Solution Implemented: Added singleton HTTP client with essential resource limits:
// Before: Basic client with no resource limits
httpClient := &http.Client{
Timeout: 30 * time.Second,
}
// After: Shared client with resource limits
func getHTTPClient() *http.Client {
httpClientOnce.Do(func() {
transport := http.DefaultTransport.(*http.Transport).Clone()
// Essential resource limits to prevent exhaustion
transport.MaxIdleConns = 100 // Limit total idle connections
transport.MaxIdleConnsPerHost = 10 // Limit per-host idle connections
transport.IdleConnTimeout = 90 * time.Second // Clean up idle connections
sharedHTTPClient = &http.Client{
Transport: transport,
Timeout: 30 * time.Second,
}
})
return sharedHTTPClient
}Impact: ✅ Eliminated resource exhaustion risk, improved performance through connection reuse, and implemented proper connection cleanup while maintaining Go's sensible defaults.
File: /Users/cevian/Development/tiger-cli/internal/tiger/cmd/db.go:380
Severity: Medium
Problem: PGPASSWORD is set in the process environment, which follows PostgreSQL's standard authentication practice but could be visible to debugging tools.
psqlCmd.Env = append(os.Environ(), "PGPASSWORD="+password)Impact: Limited risk - password visible to process debugging tools, but this is PostgreSQL's recommended practice.
Note: This is the standard and recommended way to pass passwords to PostgreSQL tools. Alternative approaches (pgpass file) are already supported.
File: /Users/cevian/Development/tiger-cli/internal/tiger/logging/logging.go:10
Severity: Medium
Problem: Global logger variable could cause race conditions if accessed concurrently.
var logger *zap.LoggerImpact: Potential race conditions, difficult testing.
Solution: Use dependency injection or ensure thread-safe initialization.
File: /Users/cevian/Development/tiger-cli/internal/tiger/cmd/auth.go:235
Severity: Medium
Problem: Keyring deletion errors are silently ignored.
keyring.Delete(getServiceName(), username) // Error ignoredImpact: Users may think credentials are removed when they're still present.
Solution: Log errors or inform users of partial cleanup failures.
File: /Users/cevian/Development/tiger-cli/internal/tiger/cmd/service.go:784-791
Severity: Medium
Problem: Password saving failures don't fail the overall command, leading to inconsistent state.
func handlePasswordSaving(service api.Service, initialPassword string, cmd *cobra.Command) {
if err := SavePasswordWithMessages(service, initialPassword, cmd.OutOrStdout()); err != nil {
// Error ignored - doesn't fail the command
}
}Impact: Users may think password was saved when it wasn't.
Solution: Make password save failures more visible or provide retry mechanisms.
File: /Users/cevian/Development/tiger-cli/internal/tiger/cmd/service.go:435-563
Severity: Medium
Problem: buildServiceUpdatePasswordCmd function is overly complex (128 lines) with multiple responsibilities.
Impact: Difficult to maintain, test, and debug.
Solution: Break into smaller, focused functions.
File: /Users/cevian/Development/tiger-cli/internal/tiger/cmd/service.go:285-434
Severity: Medium
Problem: buildServiceCreateCmd function is very long with multiple responsibilities.
Impact: Difficult to maintain and test.
Solution: Extract validation, API call, and response handling into separate functions.
File: Various test files
Severity: Low
Problem: Many test files don't adequately test error conditions and edge cases.
Impact: Reduced confidence in error handling robustness.
Solution: Implement comprehensive error condition testing.
File: /Users/cevian/Development/tiger-cli/internal/tiger/cmd/auth.go:186
Severity: Low
Problem: Unimplemented functionality with TODO comment.
// TODO: Make API call to get user informationImpact: Incomplete user experience.
Solution: Implement user information retrieval or remove the comment.
The codebase demonstrates several strong architectural patterns:
✅ Pure Functional Builder Pattern - Excellent command structure with zero global command state
✅ Comprehensive Testing - 71.6% test coverage with integration tests
✅ Good Error Handling - Proper exit codes and error propagation
✅ Security Awareness - Password masking and keyring integration
✅ Configuration Management - Layered config with proper precedence
✅ CLI Best Practices - Follows modern CLI conventions
- Implement input validation for user-provided arguments - Prevent command injection
Remove global config singleton✅ COMPLETED - Removed singleton pattern, implemented fresh instance creationConfigure HTTP client resource limits✅ COMPLETED - Added connection limits and cleanupImprove keyring service name detection✅ COMPLETED - Fixed usingtesting.Testing()
- Address silent password save failures - Make errors visible to users
- Fix global logger state - Ensure thread safety
- Break up complex functions - Improve maintainability
- Expand error condition test coverage
- Complete TODO implementations or remove them
Overall Security Rating: 🟡 Medium Risk
Security strengths:
- ✅ Secure credential handling - Uses PostgreSQL standard practices (PGPASSWORD env var)
- ✅ No credential exposure in process arguments
- ✅ Password masking in error messages and logs
- ✅ Multiple storage options - keyring, pgpass, none
Remaining security concerns:
⚠️ Potential command injection via psql argumentsWeak test environment detection✅ RESOLVED - Now usingtesting.Testing()
Recommended Actions:
- Add comprehensive input validation for user arguments
Improve test environment detection✅ COMPLETED- Consider additional security testing
Overall Architecture Rating: 🟡 Good with Issues
Strengths:
- Pure functional command builders
- Good separation of concerns in most areas
- Proper dependency structure
Issues:
- Global state patterns (config, logger)
- Some tightly coupled components
- Complex functions that should be decomposed
Overall Quality Rating: 🟢 Good
Strengths:
- Consistent code style
- Good naming conventions
- Comprehensive tests
- Proper error handling patterns
Areas for Improvement:
- Function complexity in some areas
- Some incomplete implementations
- Could benefit from more edge case testing
The Tiger CLI codebase demonstrates solid engineering practices and secure credential handling. The architecture shows mature CLI development with good security practices already in place.
Key Strengths:
- ✅ Secure credential implementation following PostgreSQL best practices
- ✅ Comprehensive testing with 71.6% coverage and integration tests
- ✅ Clean architecture with functional command builders and zero global command state
- ✅ Good error handling with proper exit codes
Areas for improvement focus on architectural refinements rather than security vulnerabilities:
- Global state management (config, logger)
- Input validation for user-provided arguments
- Code organization (some complex functions)
Recommendation: This codebase is closer to production-ready than initially assessed. Address the architectural improvements (global state, input validation) and continue with the current development approach. The security foundation is solid.