fix: security audit — patch vulnerabilities V-02 through V-16

- V-02: XSS via innerHTML in profile.js — wrap err.message in escapeHtml()
- V-03: IDOR upload to other users' folders — add folder ownership check
- V-04: IDOR create folders in other users' trees — add parent ownership check
- V-06: Content-Disposition header injection — RFC 5987 percent-encoding
- V-08: WebDAV MOVE/COPY destination without ownership — add assert_owner checks
- V-09: .gitignore missing cert/key patterns — add *.pem, *.key, *.p12, etc.
- V-11: Username accepts XSS payloads — restrict to [a-zA-Z0-9._-]
- V-12: Minimal email validation — reject forbidden chars, require domain dot
- V-13: admin_reset_password doesn't invalidate sessions — revoke all sessions
- V-14: Rate limiting bypassable via X-Forwarded-For — gate behind OXICLOUD_TRUST_PROXY_HEADERS
- V-15: Cookie Secure flag off by default — default to true (safe-by-default)
- V-16: LIKE wildcard injection in searches — add like_escape() helper across 9 sites
This commit is contained in:
Dionisio
2026-03-05 14:52:11 +01:00
parent b503e08384
commit 33cfb0faef
14 changed files with 265 additions and 58 deletions
+26 -6
View File
@@ -26,16 +26,36 @@ pub const CSRF_COOKIE: &str = "oxicloud_csrf";
pub const CSRF_HEADER: &str = "x-csrf-token";
/// Whether the `Secure` flag should be set on cookies.
/// Auto-detected from `OXICLOUD_BASE_URL` (if it starts with `https`)
/// or overridden with `OXICLOUD_COOKIE_SECURE=true|false`.
///
/// Resolution order:
/// 1. `OXICLOUD_COOKIE_SECURE=true|false` — explicit override.
/// 2. `OXICLOUD_BASE_URL` starts with `https` → `true`.
/// 3. **Default: `true`** (safe-by-default). Set `OXICLOUD_COOKIE_SECURE=false`
/// explicitly for plain-HTTP development environments.
fn cookie_secure() -> bool {
if let Ok(v) = std::env::var("OXICLOUD_COOKIE_SECURE") {
return v == "true" || v == "1";
let secure = v == "true" || v == "1";
if !secure {
tracing::warn!(
"OXICLOUD_COOKIE_SECURE is explicitly disabled — \
cookies will be sent over plain HTTP. \
Do NOT use this in production."
);
}
return secure;
}
// Auto-detect from base URL
std::env::var("OXICLOUD_BASE_URL")
// Auto-detect from base URL, defaulting to secure when unset
let secure = std::env::var("OXICLOUD_BASE_URL")
.map(|u| u.starts_with("https"))
.unwrap_or(false)
.unwrap_or(true);
if !secure {
tracing::warn!(
"OXICLOUD_BASE_URL does not start with https — \
cookie Secure flag is OFF. Set OXICLOUD_COOKIE_SECURE=true \
to override if your proxy terminates TLS."
);
}
secure
}
/// Build a `Set-Cookie` header value.