Merge pull request #211 from BillionClaw/clawoss/fix/csp-session-loop

fix(auth): resolve CSP blocking inline styles and fix session refresh loop
This commit is contained in:
Dionisio Pozo
2026-03-16 23:17:19 +01:00
committed by GitHub
2 changed files with 28 additions and 16 deletions
+22 -13
View File
@@ -30,8 +30,10 @@ pub const CSRF_HEADER: &str = "x-csrf-token";
/// Resolution order: /// Resolution order:
/// 1. `OXICLOUD_COOKIE_SECURE=true|false` — explicit override. /// 1. `OXICLOUD_COOKIE_SECURE=true|false` — explicit override.
/// 2. `OXICLOUD_BASE_URL` starts with `https` → `true`. /// 2. `OXICLOUD_BASE_URL` starts with `https` → `true`.
/// 3. **Default: `true`** (safe-by-default). Set `OXICLOUD_COOKIE_SECURE=false` /// 3. `OXICLOUD_BASE_URL` starts with `http` → `false`.
/// explicitly for plain-HTTP development environments. /// 4. **Default: `false`** for compatibility with HTTP deployments
/// (Docker, local development). Set `OXICLOUD_COOKIE_SECURE=true`
/// explicitly for production HTTPS environments.
fn cookie_secure() -> bool { fn cookie_secure() -> bool {
if let Ok(v) = std::env::var("OXICLOUD_COOKIE_SECURE") { if let Ok(v) = std::env::var("OXICLOUD_COOKIE_SECURE") {
let secure = v == "true" || v == "1"; let secure = v == "true" || v == "1";
@@ -44,18 +46,25 @@ fn cookie_secure() -> bool {
} }
return secure; return secure;
} }
// Auto-detect from base URL, defaulting to secure when unset // Auto-detect from base URL, defaulting to insecure for compatibility
let secure = std::env::var("OXICLOUD_BASE_URL") match std::env::var("OXICLOUD_BASE_URL") {
.map(|u| u.starts_with("https")) Ok(url) if url.starts_with("https") => true,
.unwrap_or(true); Ok(url) if url.starts_with("http://") => {
if !secure { tracing::info!(
tracing::warn!( "OXICLOUD_BASE_URL is HTTP — cookie Secure flag is OFF. \
"OXICLOUD_BASE_URL does not start with https — \ Set OXICLOUD_COOKIE_SECURE=true to override if your proxy terminates TLS."
cookie Secure flag is OFF. Set OXICLOUD_COOKIE_SECURE=true \ );
to override if your proxy terminates TLS." false
); }
_ => {
// Default to false for compatibility with HTTP deployments
tracing::info!(
"OXICLOUD_BASE_URL not set — defaulting to non-secure cookies \
for HTTP compatibility. Set OXICLOUD_COOKIE_SECURE=true for HTTPS deployments."
);
false
}
} }
secure
} }
/// Build a `Set-Cookie` header value. /// Build a `Set-Cookie` header value.
+6 -3
View File
@@ -416,15 +416,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
app = app app = app
.layer(SetResponseHeaderLayer::overriding( .layer(SetResponseHeaderLayer::overriding(
HeaderName::from_static("content-security-policy"), HeaderName::from_static("content-security-policy"),
// All inline scripts and styles have been migrated to external // Note: 'unsafe-inline' is required for style-src because the
// files, so 'unsafe-inline' is no longer needed. // frontend JavaScript dynamically sets inline styles (e.g.,
// element.style.display = 'none'). This is a common pattern
// for UI state management and cannot be easily migrated to
// external CSS classes without significant refactoring.
// frame-src: '*' only matches network schemes, so 'blob:' must be // frame-src: '*' only matches network schemes, so 'blob:' must be
// listed explicitly for inline PDF/document viewers. // listed explicitly for inline PDF/document viewers.
// media-src: needed for blob: video/audio playback. // media-src: needed for blob: video/audio playback.
HeaderValue::from_static( HeaderValue::from_static(
"default-src 'self'; \ "default-src 'self'; \
script-src 'self'; \ script-src 'self'; \
style-src 'self'; \ style-src 'self' 'unsafe-inline'; \
img-src 'self' data: blob:; \ img-src 'self' data: blob:; \
media-src 'self' blob:; \ media-src 'self' blob:; \
connect-src 'self'; \ connect-src 'self'; \