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:
@@ -37,3 +37,4 @@ build/
|
|||||||
dist/
|
dist/
|
||||||
.old/
|
.old/
|
||||||
test_assets/
|
test_assets/
|
||||||
|
dev_notes/
|
||||||
|
|||||||
+6
-1
@@ -52,6 +52,11 @@ ENV ABOGEN_UPLOAD_ROOT=/data/uploads \
|
|||||||
HF_HOME=/data/huggingface \
|
HF_HOME=/data/huggingface \
|
||||||
HUGGINGFACE_HUB_CACHE=/data/huggingface/hub
|
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"]
|
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}
|
normalized_tokens = {normalize_token(token) for token in tokens if token}
|
||||||
if not normalized_tokens:
|
if not normalized_tokens:
|
||||||
return {}
|
return {}
|
||||||
|
# Use parameterized queries to prevent SQL injection
|
||||||
placeholders = ",".join("?" for _ in normalized_tokens)
|
placeholders = ",".join("?" for _ in normalized_tokens)
|
||||||
with _DB_LOCK:
|
with _DB_LOCK:
|
||||||
conn = _connect()
|
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
|
# Determine shell usage: use shell only for string commands
|
||||||
use_shell = isinstance(cmd, str)
|
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 = {
|
kwargs = {
|
||||||
"shell": use_shell,
|
"shell": use_shell,
|
||||||
"stdout": subprocess.PIPE,
|
"stdout": subprocess.PIPE,
|
||||||
|
|||||||
+22
-2
@@ -8,7 +8,7 @@ from typing import Any, Optional
|
|||||||
|
|
||||||
from flask import Flask
|
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 .conversion_runner import run_conversion_job
|
||||||
from .service import build_service
|
from .service import build_service
|
||||||
@@ -50,6 +50,26 @@ def _default_dirs() -> tuple[Path, Path]:
|
|||||||
return uploads, outputs
|
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:
|
def create_app(config: Optional[dict[str, Any]] = None) -> Flask:
|
||||||
uploads_dir, outputs_dir = _default_dirs()
|
uploads_dir, outputs_dir = _default_dirs()
|
||||||
|
|
||||||
@@ -59,7 +79,7 @@ def create_app(config: Optional[dict[str, Any]] = None) -> Flask:
|
|||||||
template_folder="templates",
|
template_folder="templates",
|
||||||
)
|
)
|
||||||
base_config = {
|
base_config = {
|
||||||
"SECRET_KEY": os.environ.get("ABOGEN_SECRET_KEY", os.urandom(16)),
|
"SECRET_KEY": _get_secret_key(),
|
||||||
"UPLOAD_FOLDER": str(uploads_dir),
|
"UPLOAD_FOLDER": str(uploads_dir),
|
||||||
"OUTPUT_FOLDER": str(outputs_dir),
|
"OUTPUT_FOLDER": str(outputs_dir),
|
||||||
"MAX_CONTENT_LENGTH": 1024 * 1024 * 400, # 400 MB uploads
|
"MAX_CONTENT_LENGTH": 1024 * 1024 * 400, # 400 MB uploads
|
||||||
|
|||||||
@@ -4511,6 +4511,7 @@ def enqueue_job() -> ResponseReturnValue:
|
|||||||
original_name: str
|
original_name: str
|
||||||
|
|
||||||
if file and file.filename:
|
if file and file.filename:
|
||||||
|
# secure_filename prevents path traversal attacks
|
||||||
filename = secure_filename(file.filename)
|
filename = secure_filename(file.filename)
|
||||||
if not filename:
|
if not filename:
|
||||||
return redirect(url_for("web.index"))
|
return redirect(url_for("web.index"))
|
||||||
|
|||||||
Reference in New Issue
Block a user