T09 · Insecure Skill Coding Practices
- Location
backend/src/parsers/mod.rs:154- Finding
Substring-Based Host Validation Enables Server-Side Request Forgery
- Content
View full analysis
Option { if url.contains("redfin.") { Some(ListingSite::Redfin) } else if url.contains("rew.ca") { Some(ListingSite::Rew) } else if url.contains("zillow.com") { Some(ListingSite::Zillow) } else if url.contains("realtor.ca") { Some(ListingSite::Realtor) } else { None } } ``` The result is treated as a validated listing URL: ```rust pub(crate) fn parse_listing_url(input: &str) -> Option { let cleaned = strip_url_query(input); let u = Url::parse(&cleaned).ok()?; match u.scheme() { "http" | "https" => { let site = crate::parsers::ListingSite::from_url(&cleaned)?; Some(ListingUrl { url: u, site }) } _ => None, } } ``` The accepted URL is then requested by the backend: ```rust async fn fetch_html_direct(client: &Client, url: &Url) -> Result { let mut headers = HeaderMap::new(); // Header construction omitted. let resp = client.get(url.as_str()).headers(headers).send().await?; resp.error_for_status_ref()?; resp.text().await } ``` ### Technical Analysis The code checks whether the complete URL string contains a trusted-site substring. It does not compare the parsed hostname to an explicit allowlist. Consequently, attacker-controlled hosts such as the following can pass validation: ```text https://redfin.attacker.example/listing https://rew.ca.attacker.example/listing https://zillow.com.attacker.example/listing ``` An attacker-controlled accepted hostname can resolve to a loopback, private, link-local, or othe ...[truncated 1601 chars]- Remediation
View remediation
