fix: Address critical security and concurrency issues

Security fixes:
- Remove hardcoded wallet address from CLI defaults (start.go, serve.go)
  Pool and wallet are now required flags or must be provided explicitly
- Change file permissions from 0644 to 0600 for sensitive config files
  Affects: xmrig config, profiles, settings, config cache
- Fix path traversal in untar() - now returns error instead of silently skipping

Concurrency fix:
- Fix race condition in GetStats() - was using RLock while writing m.FullStats
  Changed to Lock/Unlock in both xmrig_stats.go and ttminer_stats.go

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
snider 2025-12-31 00:50:06 +00:00
parent 584fe5cc2a
commit 006dd3712e
10 changed files with 21 additions and 21 deletions

View file

@ -91,16 +91,15 @@ var serveCmd = &cobra.Command{
switch command {
case "start":
if len(cmdArgs) < 1 {
fmt.Println("Error: start command requires miner type (e.g., 'start xmrig')")
if len(cmdArgs) < 3 {
fmt.Println("Usage: start <miner_type> <pool> <wallet>")
fmt.Println("Example: start xmrig stratum+tcp://pool.example.com:3333 YOUR_WALLET_ADDRESS")
} else {
minerType := cmdArgs[0]
// Use default pool and wallet for interactive shell for simplicity
config := &mining.Config{
Pool: "pool.hashvault.pro:443",
Wallet: "888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H", // Corrected wallet address
LogOutput: true, // Enable logging for interactive shell
// Add other default config values if necessary
Pool: cmdArgs[1],
Wallet: cmdArgs[2],
LogOutput: true,
}
miner, err := mgr.StartMiner(minerType, config)
if err != nil {

View file

@ -38,7 +38,8 @@ var startCmd = &cobra.Command{
func init() {
rootCmd.AddCommand(startCmd)
startCmd.Flags().StringVarP(&minerPool, "pool", "p", "pool.hashvault.pro", "Mining pool address")
startCmd.Flags().StringVarP(&minerWallet, "wallet", "w", "888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H", "Wallet address")
// Removed MarkFlagRequired as we now have default values
startCmd.Flags().StringVarP(&minerPool, "pool", "p", "", "Mining pool address (required)")
startCmd.Flags().StringVarP(&minerWallet, "wallet", "w", "", "Wallet address (required)")
_ = startCmd.MarkFlagRequired("pool")
_ = startCmd.MarkFlagRequired("wallet")
}

View file

@ -91,7 +91,7 @@ func SaveMinersConfig(cfg *MinersConfig) error {
return fmt.Errorf("failed to marshal miners config: %w", err)
}
if err := os.WriteFile(configPath, data, 0644); err != nil {
if err := os.WriteFile(configPath, data, 0600); err != nil {
return fmt.Errorf("failed to write miners config file: %w", err)
}
return nil

View file

@ -510,7 +510,7 @@ func (b *BaseMiner) untar(src, dest string) error {
target := filepath.Join(dest, header.Name)
if !strings.HasPrefix(target, filepath.Clean(dest)+string(os.PathSeparator)) {
continue
return fmt.Errorf("%s: illegal file path in archive", header.Name)
}
switch header.Typeflag {

View file

@ -78,7 +78,7 @@ func (pm *ProfileManager) saveProfiles() error {
return err
}
return os.WriteFile(pm.configPath, data, 0644)
return os.WriteFile(pm.configPath, data, 0600)
}
// CreateProfile adds a new profile and saves it.

View file

@ -228,7 +228,7 @@ func (s *Service) updateInstallationCache() (*SystemInfo, error) {
return nil, fmt.Errorf("could not marshal cache data: %w", err)
}
if err := os.WriteFile(configPath, data, 0644); err != nil {
if err := os.WriteFile(configPath, data, 0600); err != nil {
return nil, fmt.Errorf("could not write cache file: %w", err)
}

View file

@ -147,7 +147,7 @@ func (sm *SettingsManager) Save() error {
return err
}
return os.WriteFile(sm.settingsPath, data, 0644)
return os.WriteFile(sm.settingsPath, data, 0600)
}
// Get returns a copy of the current settings
@ -172,7 +172,7 @@ func (sm *SettingsManager) Update(fn func(*AppSettings)) error {
return err
}
return os.WriteFile(sm.settingsPath, data, 0644)
return os.WriteFile(sm.settingsPath, data, 0600)
}
// UpdateWindowState saves the current window state

View file

@ -9,8 +9,8 @@ import (
// GetStats retrieves performance metrics from the TT-Miner API.
func (m *TTMiner) GetStats() (*PerformanceMetrics, error) {
m.mu.RLock()
defer m.mu.RUnlock()
m.mu.Lock()
defer m.mu.Unlock()
if !m.Running {
return nil, errors.New("miner is not running")

View file

@ -255,5 +255,5 @@ func (m *XMRigMiner) createConfig(config *Config) error {
if err != nil {
return err
}
return os.WriteFile(m.ConfigPath, data, 0644)
return os.WriteFile(m.ConfigPath, data, 0600)
}

View file

@ -9,8 +9,8 @@ import (
// GetStats retrieves the performance statistics from the running XMRig miner.
func (m *XMRigMiner) GetStats() (*PerformanceMetrics, error) {
m.mu.RLock()
defer m.mu.RUnlock()
m.mu.Lock()
defer m.mu.Unlock()
if !m.Running {
return nil, errors.New("miner is not running")