slack status without the slack status.zzstoatzz.io/
quickslice

Address code review feedback

- Fix host header injection vulnerability by removing host header check
- Add uses_separate_auth_domain() helper method for clarity
- Validate URLs at startup to fail fast on misconfiguration
- Simplify redirect logic using the new helper method

Security fix: No longer trusts Host header from requests, instead uses
configured URLs to determine redirect behavior.

Changed files
+30 -24
src
+8 -22
src/api/auth.rs
··· 111 111 Some(did) => { 112 112 session.insert("did", did).unwrap(); 113 113 // Redirect back to main app domain after successful auth 114 - let redirect_to = if config.oauth_redirect_base != config.app_url { 114 + let redirect_to = if config.uses_separate_auth_domain() { 115 115 config.app_url.clone() 116 116 } else { 117 117 "/".to_string() ··· 143 143 144 144 /// Takes you to the login page 145 145 #[get("/login")] 146 - pub async fn login( 147 - request: HttpRequest, 148 - config: web::Data<config::Config>, 149 - ) -> Result<HttpResponse> { 150 - // Check if we're on the main app domain and redirect to auth domain 151 - if let Some(host) = request.headers().get("host") { 152 - if let Ok(host_str) = host.to_str() { 153 - // Extract just the host from the app_url 154 - if let Ok(app_url) = url::Url::parse(&config.app_url) { 155 - if let Some(app_host) = app_url.host_str() { 156 - if host_str.starts_with(app_host) 157 - && config.oauth_redirect_base != config.app_url 158 - { 159 - let redirect_url = format!("{}/login", config.oauth_redirect_base); 160 - return Ok(HttpResponse::Found() 161 - .append_header(("Location", redirect_url)) 162 - .finish()); 163 - } 164 - } 165 - } 166 - } 146 + pub async fn login(config: web::Data<config::Config>) -> Result<HttpResponse> { 147 + // If we're using a separate auth domain, redirect to it 148 + if config.uses_separate_auth_domain() { 149 + let redirect_url = format!("{}/login", config.oauth_redirect_base); 150 + return Ok(HttpResponse::Found() 151 + .append_header(("Location", redirect_url)) 152 + .finish()); 167 153 } 168 154 169 155 let html = LoginTemplate {
+22 -2
src/config.rs
··· 40 40 } 41 41 42 42 impl Config { 43 + /// Check if we're using a separate auth domain 44 + pub fn uses_separate_auth_domain(&self) -> bool { 45 + self.oauth_redirect_base != self.app_url 46 + } 47 + 43 48 /// Load configuration from environment variables with sensible defaults 44 49 pub fn from_env() -> Result<Self, env::VarError> { 45 50 // Admin DID is intentionally hardcoded as discussed 46 51 let admin_did = "did:plc:xbtmt2zjwlrfegqvch7fboei".to_string(); 47 52 48 - Ok(Config { 53 + let config = Config { 49 54 admin_did, 50 55 owner_handle: env::var("OWNER_HANDLE").unwrap_or_else(|_| "zzstoatzz.io".to_string()), 51 56 database_url: env::var("DATABASE_URL") ··· 69 74 .unwrap_or(false), 70 75 // Default to static/emojis for local dev; override in prod to /data/emojis 71 76 emoji_dir: env::var("EMOJI_DIR").unwrap_or_else(|_| "static/emojis".to_string()), 72 - }) 77 + }; 78 + 79 + // Validate critical URLs at startup 80 + if url::Url::parse(&config.oauth_redirect_base).is_err() { 81 + log::error!( 82 + "Invalid OAUTH_REDIRECT_BASE URL: {}", 83 + config.oauth_redirect_base 84 + ); 85 + panic!("Invalid OAUTH_REDIRECT_BASE URL configuration"); 86 + } 87 + if url::Url::parse(&config.app_url).is_err() { 88 + log::error!("Invalid APP_URL: {}", config.app_url); 89 + panic!("Invalid APP_URL configuration"); 90 + } 91 + 92 + Ok(config) 73 93 } 74 94 }