mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-05-26 05:53:33 +00:00
Fix three remaining CodeQL path-injection and info-exposure alerts
- plugin_loader.py: resolve plugin_dir with strict=True and validate
marker_path with relative_to() before any filesystem writes, giving
CodeQL the positive sanitization pattern it requires (py/path-injection)
- api_v3.py _safe_backup_path: replace substring negative checks with a
strict positive regex (^[a-zA-Z0-9][a-zA-Z0-9._-]{0,200}\.zip$) that
CodeQL recognises as sanitising the user-supplied filename
(py/path-injection)
- api_v3.py backup_validate: whitelist known-safe manifest fields before
returning JSON, preventing any exception strings captured inside
validate_backup() from reaching the HTTP response (py/stack-trace-exposure)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -147,8 +147,19 @@ class PluginLoader:
|
|||||||
if not requirements_file.exists():
|
if not requirements_file.exists():
|
||||||
return True # No dependencies needed
|
return True # No dependencies needed
|
||||||
|
|
||||||
|
# Resolve and validate plugin_dir before constructing derived paths from it
|
||||||
|
try:
|
||||||
|
plugin_dir_resolved = plugin_dir.resolve(strict=True)
|
||||||
|
except OSError:
|
||||||
|
self.logger.error("Plugin directory does not exist: %s", plugin_dir)
|
||||||
|
return False
|
||||||
|
marker_path = plugin_dir_resolved / ".dependencies_installed"
|
||||||
|
try:
|
||||||
|
marker_path.relative_to(plugin_dir_resolved)
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
|
||||||
# Check if already installed
|
# Check if already installed
|
||||||
marker_path = plugin_dir / ".dependencies_installed"
|
|
||||||
if marker_path.exists():
|
if marker_path.exists():
|
||||||
self.logger.debug("Dependencies already installed for %s", plugin_id)
|
self.logger.debug("Dependencies already installed for %s", plugin_id)
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -7048,7 +7048,7 @@ _BACKUP_EXPORT_DIR = PROJECT_ROOT / "config" / "backups" / "exports"
|
|||||||
def _safe_backup_path(filename: str) -> Path:
|
def _safe_backup_path(filename: str) -> Path:
|
||||||
"""Resolve a filename to an absolute path inside the export dir,
|
"""Resolve a filename to an absolute path inside the export dir,
|
||||||
rejecting any traversal attempts. Returns None if unsafe."""
|
rejecting any traversal attempts. Returns None if unsafe."""
|
||||||
if not filename or '/' in filename or '\\' in filename or filename.startswith('.'):
|
if not filename or not re.match(r'^[a-zA-Z0-9][a-zA-Z0-9._-]{0,200}\.zip$', filename):
|
||||||
return None
|
return None
|
||||||
path = (_BACKUP_EXPORT_DIR / filename).resolve()
|
path = (_BACKUP_EXPORT_DIR / filename).resolve()
|
||||||
try:
|
try:
|
||||||
@@ -7124,7 +7124,18 @@ def backup_validate():
|
|||||||
if not ok:
|
if not ok:
|
||||||
logger.warning("Backup validation failed: %s", err_msg)
|
logger.warning("Backup validation failed: %s", err_msg)
|
||||||
return jsonify({'status': 'error', 'message': 'Invalid or corrupted backup file'}), 400
|
return jsonify({'status': 'error', 'message': 'Invalid or corrupted backup file'}), 400
|
||||||
return jsonify({'status': 'success', 'data': manifest})
|
safe_manifest = {
|
||||||
|
'schema_version': manifest.get('schema_version'),
|
||||||
|
'created_at': manifest.get('created_at'),
|
||||||
|
'ledmatrix_version': manifest.get('ledmatrix_version'),
|
||||||
|
'hostname': manifest.get('hostname'),
|
||||||
|
'contents': manifest.get('contents', []),
|
||||||
|
'detected_contents': manifest.get('detected_contents', []),
|
||||||
|
'plugins': manifest.get('plugins', []),
|
||||||
|
'total_uncompressed': manifest.get('total_uncompressed'),
|
||||||
|
'file_count': manifest.get('file_count'),
|
||||||
|
}
|
||||||
|
return jsonify({'status': 'success', 'data': safe_manifest})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("backup_validate failed: %s", e, exc_info=True)
|
logger.error("backup_validate failed: %s", e, exc_info=True)
|
||||||
return jsonify({'status': 'error', 'message': 'An internal error occurred; see logs for details'}), 500
|
return jsonify({'status': 'error', 'message': 'An internal error occurred; see logs for details'}), 500
|
||||||
|
|||||||
Reference in New Issue
Block a user