fxsab commited on
Commit
93d3cb6
·
1 Parent(s): 891f22a

fix: secure sessions behind HTTPS proxies

Browse files
.env.example CHANGED
@@ -7,6 +7,7 @@ ADMIN_EMAIL=admin@localhost
7
  ADMIN_PASSWORD=
8
  RETENTION_DAYS=30
9
  SESSION_MAX_AGE_SECONDS=28800
 
10
  HUGGINGFACEHUB_API_TOKEN=
11
  HF_CHAT_MODEL=Qwen/Qwen2.5-7B-Instruct
12
  INFERENCE_PROVIDER=fallback
 
7
  ADMIN_PASSWORD=
8
  RETENTION_DAYS=30
9
  SESSION_MAX_AGE_SECONDS=28800
10
+ SESSION_HTTPS_ONLY=false
11
  HUGGINGFACEHUB_API_TOKEN=
12
  HF_CHAT_MODEL=Qwen/Qwen2.5-7B-Instruct
13
  INFERENCE_PROVIDER=fallback
README.md CHANGED
@@ -194,6 +194,7 @@ ADMIN_EMAIL=reviewer@example.com
194
  ADMIN_PASSWORD=<strong-secret-manager-value>
195
  RETENTION_DAYS=30
196
  SESSION_MAX_AGE_SECONDS=28800
 
197
  ```
198
 
199
  Production behavior includes authenticated, owner-scoped analyses; signed HTTP-only session cookies; CSRF validation; explicit upload consent; automatic retention expiry; redacted audit events; protected history, details, reports, and review APIs; analysis rate/concurrency limits; CSP and HSTS headers; separate liveness/readiness endpoints; and a non-root container health check.
 
194
  ADMIN_PASSWORD=<strong-secret-manager-value>
195
  RETENTION_DAYS=30
196
  SESSION_MAX_AGE_SECONDS=28800
197
+ SESSION_HTTPS_ONLY=true
198
  ```
199
 
200
  Production behavior includes authenticated, owner-scoped analyses; signed HTTP-only session cookies; CSRF validation; explicit upload consent; automatic retention expiry; redacted audit events; protected history, details, reports, and review APIs; analysis rate/concurrency limits; CSP and HSTS headers; separate liveness/readiness endpoints; and a non-root container health check.
data/benchmark_runs/latest.json CHANGED
@@ -6,7 +6,7 @@
6
  "provenance": "Repository-authored synthetic OCR text for regression testing; not clinician-adjudicated.",
7
  "created_at": "2026-07-11"
8
  },
9
- "generated_at": "2026-07-12T04:19:11.682606+00:00",
10
  "case_count": 8,
11
  "success_count": 8,
12
  "failure_count": 0,
 
6
  "provenance": "Repository-authored synthetic OCR text for regression testing; not clinician-adjudicated.",
7
  "created_at": "2026-07-11"
8
  },
9
+ "generated_at": "2026-07-12T04:36:50.992895+00:00",
10
  "case_count": 8,
11
  "success_count": 8,
12
  "failure_count": 0,
simpliscribe/config.py CHANGED
@@ -24,6 +24,7 @@ class Settings:
24
  auth_required: bool = os.environ.get("AUTH_REQUIRED", "").strip().lower() in {"1", "true", "yes", "on"}
25
  retention_days: int = int(os.environ.get("RETENTION_DAYS", "30"))
26
  session_max_age_seconds: int = int(os.environ.get("SESSION_MAX_AGE_SECONDS", "28800"))
 
27
  india_medicine_dataset: Path = BASE_DIR / "A_Z_medicines_dataset_of_India.csv"
28
  medicine_database_dataset: Path = BASE_DIR / "all_medicine databased.csv"
29
  max_upload_mb: int = int(os.environ.get("MAX_UPLOAD_MB", "10"))
@@ -56,6 +57,10 @@ class Settings:
56
  def authentication_enabled(self) -> bool:
57
  return self.production or self.auth_required
58
 
 
 
 
 
59
  def validate_runtime(self) -> None:
60
  errors: list[str] = []
61
  if self.production:
 
24
  auth_required: bool = os.environ.get("AUTH_REQUIRED", "").strip().lower() in {"1", "true", "yes", "on"}
25
  retention_days: int = int(os.environ.get("RETENTION_DAYS", "30"))
26
  session_max_age_seconds: int = int(os.environ.get("SESSION_MAX_AGE_SECONDS", "28800"))
27
+ session_https_only: bool = os.environ.get("SESSION_HTTPS_ONLY", "").strip().lower() in {"1", "true", "yes", "on"}
28
  india_medicine_dataset: Path = BASE_DIR / "A_Z_medicines_dataset_of_India.csv"
29
  medicine_database_dataset: Path = BASE_DIR / "all_medicine databased.csv"
30
  max_upload_mb: int = int(os.environ.get("MAX_UPLOAD_MB", "10"))
 
57
  def authentication_enabled(self) -> bool:
58
  return self.production or self.auth_required
59
 
60
+ @property
61
+ def secure_transport(self) -> bool:
62
+ return self.production or self.session_https_only
63
+
64
  def validate_runtime(self) -> None:
65
  errors: list[str] = []
66
  if self.production:
simpliscribe/main.py CHANGED
@@ -40,7 +40,7 @@ async def protect_health_data_responses(request: Request, call_next):
40
  response.headers.setdefault("X-Frame-Options", "DENY")
41
  response.headers.setdefault("Permissions-Policy", "camera=(), microphone=(), geolocation=()")
42
  response.headers.setdefault("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: blob:; connect-src 'self'")
43
- if settings.production:
44
  response.headers.setdefault("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
45
  return response
46
 
@@ -52,7 +52,7 @@ app.add_middleware(
52
  secret_key=settings.session_secret,
53
  max_age=settings.session_max_age_seconds,
54
  same_site="lax",
55
- https_only=settings.production,
56
  )
57
 
58
 
 
40
  response.headers.setdefault("X-Frame-Options", "DENY")
41
  response.headers.setdefault("Permissions-Policy", "camera=(), microphone=(), geolocation=()")
42
  response.headers.setdefault("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.tailwindcss.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' data: blob:; connect-src 'self'")
43
+ if settings.secure_transport:
44
  response.headers.setdefault("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
45
  return response
46
 
 
52
  secret_key=settings.session_secret,
53
  max_age=settings.session_max_age_seconds,
54
  same_site="lax",
55
+ https_only=settings.secure_transport,
56
  )
57
 
58