fix: validate cafe URL scheme on create and update
Make is_valid_url_scheme pub(crate) and use it to filter cafe website URLs on both create and update, matching roaster validation behavior. Add UpdateCafe::normalize() method for consistent sanitization.
This commit is contained in:
parent
b5d5949adf
commit
ff2c602139
3 changed files with 22 additions and 2 deletions
|
|
@ -192,6 +192,7 @@ pub(crate) async fn update_cafe(
|
||||||
) -> Result<Response, ApiError> {
|
) -> Result<Response, ApiError> {
|
||||||
let (submission, source) = payload.into_parts();
|
let (submission, source) = payload.into_parts();
|
||||||
let (update, image_data_url) = submission.into_parts();
|
let (update, image_data_url) = submission.into_parts();
|
||||||
|
let update = update.normalize();
|
||||||
|
|
||||||
validate_update(&update, image_data_url.as_ref())?;
|
validate_update(&update, image_data_url.as_ref())?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::domain::ids::CafeId;
|
use crate::domain::ids::CafeId;
|
||||||
use crate::domain::listing::{SortDirection, SortKey};
|
use crate::domain::listing::{SortDirection, SortKey};
|
||||||
|
use crate::domain::roasters::is_valid_url_scheme;
|
||||||
use crate::domain::timeline::{NewTimelineEvent, TimelineEventDetail};
|
use crate::domain::timeline::{NewTimelineEvent, TimelineEventDetail};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|
@ -62,7 +63,8 @@ impl NewCafe {
|
||||||
self.name = self.name.trim().to_string();
|
self.name = self.name.trim().to_string();
|
||||||
self.city = self.city.trim().to_string();
|
self.city = self.city.trim().to_string();
|
||||||
self.country = self.country.trim().to_string();
|
self.country = self.country.trim().to_string();
|
||||||
self.website = normalize_optional_field(self.website);
|
self.website =
|
||||||
|
normalize_optional_field(self.website).filter(|url| is_valid_url_scheme(url));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,6 +96,23 @@ pub struct UpdateCafe {
|
||||||
pub created_at: Option<DateTime<Utc>>,
|
pub created_at: Option<DateTime<Utc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl UpdateCafe {
|
||||||
|
pub fn normalize(mut self) -> Self {
|
||||||
|
self.website = self
|
||||||
|
.website
|
||||||
|
.and_then(|w| {
|
||||||
|
let trimmed = w.trim().to_string();
|
||||||
|
if trimmed.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(trimmed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(|url| is_valid_url_scheme(url));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum CafeSortKey {
|
pub enum CafeSortKey {
|
||||||
CreatedAt,
|
CreatedAt,
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ fn normalize_optional_field(value: Option<String>) -> Option<String> {
|
||||||
|
|
||||||
/// Returns `true` if the URL starts with `http://` or `https://`.
|
/// Returns `true` if the URL starts with `http://` or `https://`.
|
||||||
/// Rejects `javascript:`, `data:`, and other potentially dangerous schemes.
|
/// Rejects `javascript:`, `data:`, and other potentially dangerous schemes.
|
||||||
fn is_valid_url_scheme(url: &str) -> bool {
|
pub(crate) fn is_valid_url_scheme(url: &str) -> bool {
|
||||||
let lower = url.trim().to_lowercase();
|
let lower = url.trim().to_lowercase();
|
||||||
lower.starts_with("http://") || lower.starts_with("https://")
|
lower.starts_with("http://") || lower.starts_with("https://")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue