Why
Security audit from rounds 56-60 identified critical vulnerabilities requiring immediate remediation. Prioritized action plan focuses on highest-severity issues first.
What
Implement prioritized security fixes in following order:
- Fix plaintext password storage vulnerability (CRITICAL)
- Add SQL injection prevention (CRITICAL)
- Add path traversal prevention (HIGH)
- Fix user deletion race condition (HIGH)
- Add connection pool limits (HIGH)
Where
pkg/store/database/user.go - User password storage
pkg/store/database/repo.go - Repository operations
pkg/backend/user.go - User management
pkg/db/db.go - Database connection management
pkg/utils/utils.go - Input sanitization
pkg/web/auth.go - Authentication flow
- Various input endpoints across
pkg/web/ and pkg/ssh/cmd/
Plan
Phase 1: Fix Plaintext Password Storage (CRITICAL) ✅
Status: PR #866 created and open in dvrd/soft-serve
File: pkg/store/database/user.go
Changes:
- Added
validateBcryptHash() helper function to check password format (60 chars, starts with $2a$ or $2b$)
- Updated
SetUserPassword() to validate password before storage
- Updated
SetUserPasswordByUsername() to validate password before storage
- Added unit tests for bcrypt hash validation
Test: Unit tests verify bcrypt hash validation works correctly
Risk: Database backup files could contain plaintext passwords
PR: https://github.com/dvrd/soft-serve/pull/866
Phase 2: SQL Injection Prevention (CRITICAL)
File: pkg/store/database/repo.go
Changes:
- Find all instances of string concatenation in SQL queries
- Replace with parameterized queries using
tx.Rebind() or tx.RebindNamed():
// Replace this pattern:
tx.Rebind("SELECT * FROM repos WHERE name = $1", sql.Named("repo", name))
// With proper handling:
repo := struct{Name string}
if err := tx.Get(&repo, sql.Named("repo", $1)); err != nil {
return err
}
Test: Attempt SQL injection via malicious repository name
Risk: Full database compromise via SQL injection
PR: security/fix-sql-injection
Phase 3: Path Traversal Prevention (HIGH)
File: pkg/utils/utils.go
Changes:
-
Enhance SanitizeRepo() to prevent ../ sequences:
func SanitizeRepo(repo string) string {
repo = Sanitize(repo)
// Remove leading slashes
repo = strings.TrimPrefix(repo, "/")
// Prevent path traversal - check for ../
if strings.Contains(repo, "../") {
return "", fmt.Errorf("invalid repository name: path sequences not allowed")
}
// Existing sanitization
repo = "/" + repo
repo = path.Clean(repo)
repo = strings.TrimSuffix(repo, ".git")
return strings.TrimPrefix(repo, "/")
}
-
Update call sites in pkg/web/blob.go and pkg/web/git.go to use SanitizeRepo() before using repo parameter
Test: Attempt to access ../../etc/passwd via malicious repo name
Risk: File system access bypass via path traversal
PR: security/fix-path-traversal
Phase 4: Fix User Deletion Race (HIGH)
File: pkg/backend/user.go
Changes:
Move repo deletion inside a database transaction:
return dbx.TransactionContext(ctx, func(tx *db.Tx) error {
// Fetch repos to delete first
repos, err := store.GetRepositoriesByUserID(ctx, tx, userID)
if err != nil {
return db.WrapError(err)
}
// Delete repos
for _, repo := range repos {
if err := store.DeleteRepository(ctx, tx, repo.ID()); err != nil {
return db.WrapError(err)
}
}
return nil
})
Test: Concurrent user deletion attempts
Risk: Orphaned repositories and database inconsistency
PR: security/fix-user-deletion-race
Phase 5: Connection Pool Limits (HIGH)
File: pkg/db/db.go
Changes:
- Add connection limit constants:
const (
maxOpenConnections = 25
maxIdleConnections = 10
)
// In Open() function, configure pool:
db.SetMaxOpenConns(maxOpenConnections)
db.SetMaxIdleConns(maxIdleConnections)
db.SetConnMaxLifetime(30 * time.Minute)
Test: Load test with 25 concurrent connections
Risk: DoS via connection exhaustion under load
PR: security/fix-connection-pool-limits
Testing Strategy
- Unit tests for each fix
- Integration test for concurrent operations
- Manual security review for each PR
- Load testing for connection limits
Order of Implementation
Phase 1 → Phase 2 → Phase 3 → Phase 4 → Phase 5
- Each phase: implement, test, create PR in dvrd/soft-serve
- Merge each PR into main after tests pass
- Document test results in PR description
Success Criteria
- All 5 PRs created in dvrd/soft-serve
- All unit tests passing
- Integration tests passing
- No security regressions
- Connection limits functioning correctly
Notes
This is a CRITICAL security remediation project. Each fix should be:
- Implemented with comprehensive tests
- Reviewed by security expert (internal or external)
- Tested in staging environment before production
- Backed by canary plan (optional rollback strategy)
Why
Security audit from rounds 56-60 identified critical vulnerabilities requiring immediate remediation. Prioritized action plan focuses on highest-severity issues first.
What
Implement prioritized security fixes in following order:
Where
pkg/store/database/user.go- User password storagepkg/store/database/repo.go- Repository operationspkg/backend/user.go- User managementpkg/db/db.go- Database connection managementpkg/utils/utils.go- Input sanitizationpkg/web/auth.go- Authentication flowpkg/web/andpkg/ssh/cmd/Plan
Phase 1: Fix Plaintext Password Storage (CRITICAL) ✅
Status: PR #866 created and open in dvrd/soft-serve
File:
pkg/store/database/user.goChanges:
validateBcryptHash()helper function to check password format (60 chars, starts withSetUserPassword()to validate password before storageSetUserPasswordByUsername()to validate password before storageTest: Unit tests verify bcrypt hash validation works correctly
Risk: Database backup files could contain plaintext passwords
PR: https://github.com/dvrd/soft-serve/pull/866
Phase 2: SQL Injection Prevention (CRITICAL)
File:
pkg/store/database/repo.goChanges:
tx.Rebind()ortx.RebindNamed():Test: Attempt SQL injection via malicious repository name
Risk: Full database compromise via SQL injection
PR: security/fix-sql-injection
Phase 3: Path Traversal Prevention (HIGH)
File:
pkg/utils/utils.goChanges:
Enhance
SanitizeRepo()to prevent../sequences:Update call sites in
pkg/web/blob.goandpkg/web/git.goto useSanitizeRepo()before using repo parameterTest: Attempt to access
../../etc/passwdvia malicious repo nameRisk: File system access bypass via path traversal
PR: security/fix-path-traversal
Phase 4: Fix User Deletion Race (HIGH)
File:
pkg/backend/user.goChanges:
Move repo deletion inside a database transaction:
Test: Concurrent user deletion attempts
Risk: Orphaned repositories and database inconsistency
PR: security/fix-user-deletion-race
Phase 5: Connection Pool Limits (HIGH)
File:
pkg/db/db.goChanges:
Test: Load test with 25 concurrent connections
Risk: DoS via connection exhaustion under load
PR: security/fix-connection-pool-limits
Testing Strategy
Order of Implementation
Phase 1→ Phase 2 → Phase 3 → Phase 4 → Phase 5Success Criteria
Notes
This is a CRITICAL security remediation project. Each fix should be: