Replace all deprecated and unsafe command execution methods with secure subprocess.run() calls using list arguments. Changes: - Replace os.popen() with subprocess.run() in tisbackup_gui.py - Replace os.system() with subprocess.run() in tasks.py and backup_xva.py - Add input validation for device/partition names (regex-based) - Fix file operations to use context managers (with statement) - Remove wildcard import from shutil - Add timeout protection to all subprocess calls (5-30s) - Improve error handling with proper try/except blocks Security improvements: - Prevent command injection vulnerabilities in USB disk operations - Validate device paths with regex before system calls - Use list arguments instead of shell=True to prevent injection - Add proper error handling instead of silent failures Code quality improvements: - Replace deprecated os.popen() (deprecated since Python 2.6) - Use context managers for file operations - Remove wildcard imports for cleaner namespace - Add comprehensive error handling and logging Documentation: - Add SECURITY_IMPROVEMENTS.md documenting all changes - Document remaining security issues and recommendations - Include testing recommendations and migration notes BREAKING CHANGE: None - all changes are backward compatible 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
5.1 KiB
Security and Code Quality Improvements
This document summarizes the security and code quality improvements made to TISBackup.
Completed Improvements (High Priority)
1. Replaced os.popen()
with subprocess.run()
Files Modified: tisbackup_gui.py
Changes:
- Replaced deprecated
os.popen()
calls with modernsubprocess.run()
- All subprocess calls now use list arguments instead of shell strings
- Added timeout protection (5-30 seconds depending on operation)
- Proper error handling with try/except blocks
Before:
for line in os.popen("udevadm info -q env -n %s" % name):
# Process output
After:
result = subprocess.run(
["udevadm", "info", "-q", "env", "-n", name],
capture_output=True,
text=True,
check=True,
timeout=5
)
for line in result.stdout.splitlines():
# Process output
Security Impact: Prevents command injection vulnerabilities
2. Replaced os.system()
with subprocess.run()
Files Modified: tasks.py, libtisbackup/backup_xva.py
Changes:
- tasks.py:37: Changed
os.system("/bin/umount %s")
tosubprocess.run(["/bin/umount", mount_point])
- backup_xva.py:199: Changed
os.system('tar tf "%s"')
tosubprocess.run(["tar", "tf", filename_temp])
- Added proper error handling and logging
Security Impact: Eliminates command injection risk from potentially user-controlled mount points and filenames
3. Added Input Validation
Files Modified: tisbackup_gui.py
Changes:
- Added regex validation for device/partition names:
^/dev/sd[a-z]1?$
- Validates partition names before using in mount/unmount operations
- Prevents path traversal and command injection attacks
Example:
# Validate partition name to prevent command injection
if not re.match(r"^/dev/sd[a-z]1$", partition):
continue
Security Impact: Prevents malicious input from reaching system commands
4. Fixed File Operations with Context Managers
Files Modified: tisbackup_gui.py
Before:
line = open(elem).readline()
After:
with open(elem) as f:
line = f.readline()
Impact: Ensures files are properly closed, prevents resource leaks
5. Improved run_command()
Function
Files Modified: tisbackup_gui.py:415-453
Changes:
- Now accepts list arguments for safe command execution
- Backward compatible with string commands (marked as legacy)
- Added timeout protection (30 seconds)
- Better error handling and reporting
Security Impact: Provides safe command execution interface while maintaining backward compatibility
6. Removed Wildcard Import
Files Modified: tisbackup_gui.py
Before:
from shutil import *
After:
import shutil
import subprocess
Impact: Cleaner namespace, easier to track dependencies
Remaining Security Issues (Critical - Not Fixed)
1. Hardcoded Secret Key (tisbackup_gui.py:64)
app.secret_key = "fsiqefiuqsefARZ4Zfesfe34234dfzefzfe"
Recommendation: Load from environment variable or secure config file
2. No Authentication on Flask Routes
All routes are publicly accessible without authentication.
Recommendation: Implement Flask-Login or similar authentication
3. Insecure SSH Host Key Policy (libtisbackup/common.py:649)
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
Recommendation: Use proper host key verification with known_hosts
4. Command Injection in Legacy Code
Multiple files still use subprocess.call(shell_string, shell=True)
and subprocess.Popen(..., shell=True)
:
- libtisbackup/common.py:128
- libtisbackup/common.py:883
- libtisbackup/common.py:986
- libtisbackup/backup_rsync.py:176
- libtisbackup/backup_rsync_btrfs.py (multiple locations)
Recommendation: Refactor to use list arguments without shell=True
Code Quality Issues Remaining
- Global State Management - Use Flask application context instead
- Wildcard imports from common -
from libtisbackup.common import *
- Configuration loaded at module level - Should use application factory pattern
- Duplicated code -
read_config()
andread_all_configs()
share significant logic
Testing Recommendations
Before deploying these changes:
- Test USB disk detection and mounting functionality
- Test backup export operations
- Verify XVA backup tar validation
- Test error handling for invalid device names
- Verify backward compatibility with existing configurations
Migration Notes
All changes are backward compatible. The run_command()
function accepts both:
- New format:
run_command(["/bin/command", "arg1", "arg2"])
- Legacy format:
run_command("/bin/command arg1 arg2")
(less secure, marked for deprecation)