Implements automatic fix capability for bd doctor with user confirmation and security hardening. Features: - Organizes fix implementations under doctor/fix/ directory structure - Shows all fixable issues and prompts for confirmation (Y/n) before applying - Provides clear output about what was fixed and any errors encountered - Re-runs diagnostics after fixes to show updated state - Each fix is idempotent and safe to run multiple times Automatic fixes implemented: - Git hooks (runs bd hooks install) - Daemon health issues (runs bd daemons killall) - DB-JSONL sync problems (runs bd sync --import-only) - File permissions (fixes .beads/ and database permissions) - Database version mismatches (runs bd migrate) - Schema compatibility issues (runs bd migrate) - Gitignore updates (writes canonical template) Security improvements: - Prevents command injection by using os.Executable() instead of PATH lookup - Prevents path traversal attacks with workspace validation - Fixes race conditions by using cmd.Dir instead of os.Chdir() - Corrects file permission logic (proper bit masking) - Validates all operations run in beads workspaces only Files changed: - cmd/bd/doctor.go: Enhanced applyFixes() with confirmation and better UX - cmd/bd/doctor/gitignore.go: Fixed permissions (0600 → 0644) - cmd/bd/doctor/fix/common.go: Security helpers (getBdBinary, validateBeadsWorkspace) - cmd/bd/doctor/fix/hooks.go: Git hooks fix - cmd/bd/doctor/fix/daemon.go: Daemon health fix - cmd/bd/doctor/fix/sync.go: DB-JSONL sync fix - cmd/bd/doctor/fix/permissions.go: File permissions fix - cmd/bd/doctor/fix/migrate.go: Database migration fixes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
50 lines
1.2 KiB
Go
50 lines
1.2 KiB
Go
package fix
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
)
|
|
|
|
// getBdBinary returns the path to the bd binary to use for fix operations.
|
|
// It prefers the current executable to avoid command injection attacks.
|
|
func getBdBinary() (string, error) {
|
|
// Prefer current executable for security
|
|
exe, err := os.Executable()
|
|
if err == nil {
|
|
// Resolve symlinks to get the real binary path
|
|
realPath, err := filepath.EvalSymlinks(exe)
|
|
if err == nil {
|
|
return realPath, nil
|
|
}
|
|
return exe, nil
|
|
}
|
|
|
|
// Fallback to PATH lookup with validation
|
|
bdPath, err := exec.LookPath("bd")
|
|
if err != nil {
|
|
return "", fmt.Errorf("bd binary not found in PATH: %w", err)
|
|
}
|
|
|
|
return bdPath, nil
|
|
}
|
|
|
|
// validateBeadsWorkspace ensures the path is a valid beads workspace before
|
|
// attempting any fix operations. This prevents path traversal attacks.
|
|
func validateBeadsWorkspace(path string) error {
|
|
// Convert to absolute path
|
|
absPath, err := filepath.Abs(path)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid path: %w", err)
|
|
}
|
|
|
|
// Check for .beads directory
|
|
beadsDir := filepath.Join(absPath, ".beads")
|
|
if _, err := os.Stat(beadsDir); os.IsNotExist(err) {
|
|
return fmt.Errorf("not a beads workspace: .beads directory not found at %s", absPath)
|
|
}
|
|
|
|
return nil
|
|
}
|