mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 13:40:27 +02:00
feat: Enhance security by adding user permissions in Dockerfile, parameterized queries in pronunciation_store, and secure filename handling in routes
This commit is contained in:
+6
-1
@@ -52,6 +52,11 @@ ENV ABOGEN_UPLOAD_ROOT=/data/uploads \
|
||||
HF_HOME=/data/huggingface \
|
||||
HUGGINGFACE_HUB_CACHE=/data/huggingface/hub
|
||||
|
||||
RUN mkdir -p /data/uploads /data/outputs /data/cache /data/voice-cache /data/huggingface
|
||||
# Create non-root user and setup permissions
|
||||
RUN useradd -m -u 1000 abogen \
|
||||
&& mkdir -p /data/uploads /data/outputs /data/cache /data/voice-cache /data/huggingface \
|
||||
&& chown -R abogen:abogen /data /app
|
||||
|
||||
USER abogen
|
||||
|
||||
CMD ["abogen"]
|
||||
|
||||
@@ -86,6 +86,7 @@ def load_overrides(language: str, tokens: Iterable[str]) -> Dict[str, Dict[str,
|
||||
normalized_tokens = {normalize_token(token) for token in tokens if token}
|
||||
if not normalized_tokens:
|
||||
return {}
|
||||
# Use parameterized queries to prevent SQL injection
|
||||
placeholders = ",".join("?" for _ in normalized_tokens)
|
||||
with _DB_LOCK:
|
||||
conn = _connect()
|
||||
|
||||
@@ -327,6 +327,9 @@ def create_process(cmd, stdin=None, text=True, capture_output=False):
|
||||
|
||||
# Determine shell usage: use shell only for string commands
|
||||
use_shell = isinstance(cmd, str)
|
||||
if use_shell:
|
||||
logger.warning("Security Warning: create_process called with string command. Prefer using a list of arguments to avoid shell injection risks.")
|
||||
|
||||
kwargs = {
|
||||
"shell": use_shell,
|
||||
"stdout": subprocess.PIPE,
|
||||
|
||||
+22
-2
@@ -8,7 +8,7 @@ from typing import Any, Optional
|
||||
|
||||
from flask import Flask
|
||||
|
||||
from abogen.utils import get_user_cache_path, get_user_output_path
|
||||
from abogen.utils import get_user_cache_path, get_user_output_path, get_user_settings_dir
|
||||
|
||||
from .conversion_runner import run_conversion_job
|
||||
from .service import build_service
|
||||
@@ -50,6 +50,26 @@ def _default_dirs() -> tuple[Path, Path]:
|
||||
return uploads, outputs
|
||||
|
||||
|
||||
def _get_secret_key() -> str:
|
||||
env_key = os.environ.get("ABOGEN_SECRET_KEY")
|
||||
if env_key:
|
||||
return env_key
|
||||
|
||||
try:
|
||||
settings_dir = Path(get_user_settings_dir())
|
||||
settings_dir.mkdir(parents=True, exist_ok=True)
|
||||
secret_file = settings_dir / ".secret_key"
|
||||
if secret_file.exists():
|
||||
return secret_file.read_text(encoding="utf-8").strip()
|
||||
|
||||
key = os.urandom(24).hex()
|
||||
secret_file.write_text(key, encoding="utf-8")
|
||||
return key
|
||||
except Exception:
|
||||
# Fallback if we can't write to settings dir
|
||||
return os.urandom(24).hex()
|
||||
|
||||
|
||||
def create_app(config: Optional[dict[str, Any]] = None) -> Flask:
|
||||
uploads_dir, outputs_dir = _default_dirs()
|
||||
|
||||
@@ -59,7 +79,7 @@ def create_app(config: Optional[dict[str, Any]] = None) -> Flask:
|
||||
template_folder="templates",
|
||||
)
|
||||
base_config = {
|
||||
"SECRET_KEY": os.environ.get("ABOGEN_SECRET_KEY", os.urandom(16)),
|
||||
"SECRET_KEY": _get_secret_key(),
|
||||
"UPLOAD_FOLDER": str(uploads_dir),
|
||||
"OUTPUT_FOLDER": str(outputs_dir),
|
||||
"MAX_CONTENT_LENGTH": 1024 * 1024 * 400, # 400 MB uploads
|
||||
|
||||
@@ -4511,6 +4511,7 @@ def enqueue_job() -> ResponseReturnValue:
|
||||
original_name: str
|
||||
|
||||
if file and file.filename:
|
||||
# secure_filename prevents path traversal attacks
|
||||
filename = secure_filename(file.filename)
|
||||
if not filename:
|
||||
return redirect(url_for("web.index"))
|
||||
|
||||
Reference in New Issue
Block a user