Files
2025-11-29 18:18:51 +08:00

130 lines
3.1 KiB
Python

"""
Security utilities for Blender Toolkit addon
"""
import os
from pathlib import Path
from typing import Optional
def validate_file_path(file_path: str, allowed_root: Optional[str] = None) -> str:
"""
Validate file path to prevent path traversal attacks.
Args:
file_path: Path to validate
allowed_root: Optional allowed root directory. If None, only checks for dangerous patterns.
Returns:
Validated absolute path
Raises:
ValueError: If path is invalid or outside allowed directory
"""
if not file_path:
raise ValueError("File path cannot be empty")
# Resolve to absolute path
try:
abs_path = os.path.abspath(os.path.expanduser(file_path))
except Exception as e:
raise ValueError(f"Invalid file path: {e}")
# Check for null bytes (security risk)
if '\0' in file_path:
raise ValueError("File path contains null bytes")
# If allowed_root is specified, ensure path is within it
if allowed_root:
allowed_abs = os.path.abspath(os.path.expanduser(allowed_root))
# Resolve symlinks to prevent bypass
try:
real_path = os.path.realpath(abs_path)
real_root = os.path.realpath(allowed_abs)
except Exception:
# If realpath fails, use absolute paths
real_path = abs_path
real_root = allowed_abs
# Check if path is within allowed root
try:
Path(real_path).relative_to(real_root)
except ValueError:
raise ValueError(f"Path outside allowed directory: {file_path}")
return abs_path
def validate_port(port: int) -> int:
"""
Validate WebSocket port number.
Args:
port: Port number to validate
Returns:
Validated port number
Raises:
ValueError: If port is invalid
"""
if not isinstance(port, int):
raise ValueError("Port must be an integer")
if port < 1024 or port > 65535:
raise ValueError("Port must be between 1024 and 65535")
return port
# Whitelist for safe object attributes
ALLOWED_OBJECT_ATTRIBUTES = {
'location',
'rotation_euler',
'rotation_quaternion',
'rotation_axis_angle',
'scale',
'name',
'hide',
'hide_viewport',
'hide_render',
'hide_select',
}
# Whitelist for safe armature bone attributes
ALLOWED_BONE_ATTRIBUTES = {
'name',
'head',
'tail',
'roll',
'use_connect',
'use_deform',
'use_inherit_rotation',
'use_inherit_scale',
'use_local_location',
}
def validate_attribute_name(attr_name: str, allowed_attributes: set) -> str:
"""
Validate attribute name against whitelist.
Args:
attr_name: Attribute name to validate
allowed_attributes: Set of allowed attribute names
Returns:
Validated attribute name
Raises:
ValueError: If attribute is not allowed
"""
if not attr_name:
raise ValueError("Attribute name cannot be empty")
if attr_name not in allowed_attributes:
raise ValueError(f"Attribute '{attr_name}' is not allowed")
return attr_name