Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## 2025-05-15 - [Securing HMAC Secrets and CORS]
**Vulnerability:** Hardcoded HMAC secret key and overly permissive CORS policy.
**Learning:** Hardcoding production-level secrets in multiple files (`main.py` and `DivineoBunker.py`) creates a high risk of exposure. Permissive CORS (`"*"`) is acceptable for rapid prototyping but should be configurable for deployment to prevent unauthorized cross-origin access.
**Prevention:** Use environment variables (via `os.getenv` and `python-dotenv`) for all sensitive credentials. Implement configurable CORS origins through environment variables, defaulting to specific origins in production.
3 changes: 2 additions & 1 deletion backend/DivineoBunker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import hashlib
import time
import json
import os

class DivineoBunker:
def __init__(self):
# πŸ›‘οΈ ConfiguraciΓ³n Maestra (abvetos.com)
self.secret_key = "LVT_SECRET_PROD_091228222"
self.secret_key = os.getenv("LVT_SECRET_KEY", "LVT_DEV_SECRET_DO_NOT_USE_IN_PROD")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This change correctly removes the hardcoded secret. However, this file DivineoBunker.py appears to contain a significant amount of duplicated logic and data that also exists in main.py and models.py. For instance:

  • self.secret_key is also defined in main.py.
  • The _verify_auth method is nearly identical to verify_auth in main.py.
  • self.shopify_inventory duplicates SHOPIFY_INVENTORY from models.py.
  • _calculate_fit is a copy of calculate_fit from main.py.
    This duplication increases maintenance overhead and risk of inconsistencies. Consider refactoring to remove this duplicated code, possibly by removing this class if it's unused or consolidating the logic into a single place.

self.patent = "PCT/EP2025/067317"
self.algorithm_v = "V10_Divineo_Shopify_Final"

Expand Down
Binary file added backend/__pycache__/jules_engine.cpython-312.pyc
Binary file not shown.
Binary file added backend/__pycache__/main.cpython-312.pyc
Binary file not shown.
Binary file added backend/__pycache__/models.cpython-312.pyc
Binary file not shown.
20 changes: 17 additions & 3 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,31 @@
import hashlib
import time
import json
import os
from dotenv import load_dotenv
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
from fastapi.middleware.cors import CORSMiddleware
from models import UserScan, SHOPIFY_INVENTORY
from jules_engine import get_jules_advice

load_dotenv()

app = FastAPI(title="Divineo Bunker Backend")

# πŸ”’ Security: In production, configure LVT_ALLOWED_ORIGINS in .env
ALLOWED_ORIGINS = os.getenv("LVT_ALLOWED_ORIGINS", "*").split(",")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using an environment variable for CORS origins is a great security improvement. However, defaulting to "*" if the LVT_ALLOWED_ORIGINS environment variable is not set re-introduces the risk of an overly permissive CORS policy in production environments. To enforce a secure configuration, it's better to make this environment variable mandatory. By using os.environ instead of os.getenv with a default, the application will fail to start if the variable is not set, preventing an insecure deployment.

Suggested change
ALLOWED_ORIGINS = os.getenv("LVT_ALLOWED_ORIGINS", "*").split(",")
ALLOWED_ORIGINS = os.environ["LVT_ALLOWED_ORIGINS"].split(",")


app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_origins=ALLOWED_ORIGINS,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

# πŸ›‘οΈ ConfiguraciΓ³n Maestra (abvetos.com)
SECRET_KEY = "LVT_SECRET_PROD_091228222"
SECRET_KEY = os.getenv("LVT_SECRET_KEY", "LVT_DEV_SECRET_DO_NOT_USE_IN_PROD")
PATENT = "PCT/EP2025/067317"

def verify_auth(user_id: str, token: str) -> bool:
Expand Down Expand Up @@ -68,7 +75,14 @@ async def recommend_garment(scan: UserScan, garment_id: str = "BALMAIN_SS26_SLIM
# Usamos Jules para el toque de estilo
styling_advice = get_jules_advice(scan, item)
except Exception as e:
styling_advice = f"Divineo confirmado con {item['name']}."
return JSONResponse(
status_code=503,
content={
"status": "error",
"code": 503,
"message": "Jules AI Engine is currently recalibrating or unavailable. Please try again."
}
)
Comment on lines 77 to +85
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The except Exception block correctly returns a 503 error, but it swallows the original exception details. This makes debugging difficult because the root cause of the failure in the get_jules_advice function is hidden. It's important to log the exception to aid in troubleshooting issues with the AI engine. You will need to add import logging at the top of the file.

Suggested change
except Exception as e:
styling_advice = f"Divineo confirmado con {item['name']}."
return JSONResponse(
status_code=503,
content={
"status": "error",
"code": 503,
"message": "Jules AI Engine is currently recalibrating or unavailable. Please try again."
}
)
except Exception as e:
logging.error(f"Jules AI Engine failed: {e}", exc_info=True)
return JSONResponse(
status_code=503,
content={
"status": "error",
"code": 503,
"message": "Jules AI Engine is currently recalibrating or unavailable. Please try again."
}
)


if is_divineo and item['stock'] > 0:
return {
Expand Down
Binary file not shown.
16 changes: 13 additions & 3 deletions backend/tests/test_main.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import pytest
import hmac
import hashlib
import time
from fastapi.testclient import TestClient
from backend.main import app
from backend.main import app, SECRET_KEY

client = TestClient(app)

def generate_test_token(user_id: str):
ts = str(int(time.time()))
sig = hmac.new(SECRET_KEY.encode(), f"{user_id}:{ts}".encode(), hashlib.sha256).hexdigest()
return f"{ts}.{sig}"

def test_recommend_garment_engine_failure(monkeypatch):
"""
Test that the /api/recommend endpoint correctly handles failures
Expand All @@ -18,9 +26,11 @@ def mock_get_jules_advice(*args, **kwargs):
monkeypatch.setattr("backend.main.get_jules_advice", mock_get_jules_advice)

# 2. Prepare the request payload
user_id = "test_user_123"
payload = {
"height": 175.0,
"weight": 68.0,
"user_id": user_id,
"token": generate_test_token(user_id),
"waist": 72.0,
"event_type": "Gala"
}

Expand Down