Summary
The application relied on a weak default SECRET_KEY and accepted file uploads without sufficient server-side validation. Together these create a high-risk path that can allow session tampering and unsafe or malformed files to be stored and processed.
Root cause
SECRET_KEY defaulted to a development placeholder ('dev') when not provided via environment or config. In production this allows forging or tampering with signed cookies and other signed data.
- The upload endpoint saved files using the original filename and did not verify the file content beyond a sanitized filename. This allowed non-image or malicious content to be persisted, risking downstream processing issues or exposure.
Files changed
dreamsApp/app/config.py — add SECRET_KEY, MAX_CONTENT_LENGTH, and ALLOWED_EXTENSIONS.
dreamsApp/app/__init__.py — require a secure SECRET_KEY in non-development environments and ensure MAX_CONTENT_LENGTH default.
dreamsApp/app/ingestion/routes.py — enforce allowed extensions, unique filenames, and verify uploaded files are valid images with Pillow (PIL.Image.verify).
Key code changes (representative excerpts)
config.py (added configuration variables):
# Security: SECRET_KEY should be provided via environment in production.
SECRET_KEY = os.getenv("SECRET_KEY", "dev")
# Max upload size (default 16 MiB)
MAX_CONTENT_LENGTH = int(os.getenv("MAX_CONTENT_LENGTH", 16 * 1024 * 1024))
# Allowed image extensions for uploads
ALLOWED_EXTENSIONS = set(x.strip().lower() for x in os.getenv("ALLOWED_EXTENSIONS", "png,jpg,jpeg,gif").split(','))
__init__.py (production check):
flask_env = os.getenv('FLASK_ENV', app.config.get('FLASK_ENV'))
secret = app.config.get('SECRET_KEY')
if flask_env != 'development' and (not secret or secret == 'dev'):
raise RuntimeError('SECRET_KEY must be set to a secure value in production')
ingestion/routes.py (upload validation):
# Validate extension against allowed set
allowed = current_app.config.get('ALLOWED_EXTENSIONS', {'png', 'jpg', 'jpeg', 'gif'})
if '.' not in filename or filename.rsplit('.', 1)[1].lower() not in allowed:
return jsonify({'error': 'Unsupported file extension'}), 400
# Make filename unique
unique_filename = f"{uuid.uuid4().hex}_{filename}"
image_path = os.path.join(upload_path, unique_filename)
image.save(image_path)
# Verify the saved file is a valid image
try:
with Image.open(image_path) as img:
img.verify()
except (UnidentifiedImageError, Exception):
os.remove(image_path)
return jsonify({'error': 'Uploaded file is not a valid image'}), 400
OSError: [Errno 28] No space left on device while pip downloading/writing wheels — dependency installation failed.
- Because installation was incomplete, tests aborted with missing modules:
ModuleNotFoundError: No module named 'flask', No module named 'bson', No module named 'networkx'.
- A binary compatibility error was also observed during partial import of compiled extensions:
ValueError: numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject — indicates mixed/incompatible binary wheels or mismatched binary packages for the Python interpreter.
Excerpt of key error lines (developer-safe):
ERROR: Could not install packages due to an OSError: [Errno 28] No space left on device
ModuleNotFoundError: No module named 'flask'
ValueError: numpy.dtype size changed, may indicate binary incompatibility.
Implication: the code changes are applied; CI/local validation is blocked by environment problems (disk space + binary dependency mismatch). The security risk remains urgent for any production environment that uses the previous defaults.
Test the upload endpoint manually (POST /upload) with a non-image file renamed to .jpg. The endpoint should reject it with 400 Uploaded file is not a valid image.
Validation checklist (what to verify before closing)
Summary
The application relied on a weak default
SECRET_KEYand accepted file uploads without sufficient server-side validation. Together these create a high-risk path that can allow session tampering and unsafe or malformed files to be stored and processed.Root cause
SECRET_KEYdefaulted to a development placeholder ('dev') when not provided via environment or config. In production this allows forging or tampering with signed cookies and other signed data.Files changed
dreamsApp/app/config.py— addSECRET_KEY,MAX_CONTENT_LENGTH, andALLOWED_EXTENSIONS.dreamsApp/app/__init__.py— require a secureSECRET_KEYin non-development environments and ensureMAX_CONTENT_LENGTHdefault.dreamsApp/app/ingestion/routes.py— enforce allowed extensions, unique filenames, and verify uploaded files are valid images with Pillow (PIL.Image.verify).Key code changes (representative excerpts)
config.py(added configuration variables):__init__.py(production check):ingestion/routes.py(upload validation):OSError: [Errno 28] No space left on devicewhile pip downloading/writing wheels — dependency installation failed.ModuleNotFoundError: No module named 'flask',No module named 'bson',No module named 'networkx'.ValueError: numpy.dtype size changed, may indicate binary incompatibility. Expected 96 from C header, got 88 from PyObject— indicates mixed/incompatible binary wheels or mismatched binary packages for the Python interpreter.Excerpt of key error lines (developer-safe):
ERROR: Could not install packages due to an OSError: [Errno 28] No space left on deviceModuleNotFoundError: No module named 'flask'ValueError: numpy.dtype size changed, may indicate binary incompatibility.Implication: the code changes are applied; CI/local validation is blocked by environment problems (disk space + binary dependency mismatch). The security risk remains urgent for any production environment that uses the previous defaults.
Test the upload endpoint manually (POST
/upload) with a non-image file renamed to.jpg. The endpoint should reject it with400 Uploaded file is not a valid image.Validation checklist (what to verify before closing)
SECRET_KEYin production environment (or secrets manager) and verify app no longer raises at startup.MAX_CONTENT_LENGTHis enforced for large uploads (server rejects large requests).SECRET_KEY='dev'in production modes) and add a dependency-scan step.