Skip to content

Commit 2e29bc4

Browse files
committed
Validate import URLs
1 parent d114178 commit 2e29bc4

4 files changed

Lines changed: 35 additions & 7 deletions

File tree

src/api/gi/wishes_import/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use utoipa::{OpenApi, ToSchema};
1616
use crate::{
1717
api::{
1818
banner_helpers::{self, GI_STANDARD},
19-
ApiResult,
19+
validate_import_url, ApiResult,
2020
},
2121
database, GiGachaType,
2222
};
@@ -106,6 +106,7 @@ struct WishesImport {
106106
request_body = WishesImportParams,
107107
responses(
108108
(status = 200, description = "WishesImport", body = WishesImport),
109+
(status = 400, description = "Invalid URL"),
109110
)
110111
)]
111112
#[post("/api/gi/wishes-import")]
@@ -115,7 +116,10 @@ async fn post_gi_wishes_import(
115116
wishes_import_infos: web::Data<WishesImportInfos>,
116117
pool: web::Data<PgPool>,
117118
) -> ApiResult<impl Responder> {
118-
let url = Url::parse(&params.url)?;
119+
let url = match validate_import_url(&params.url) {
120+
Ok(url) => url,
121+
Err(response) => return Ok(response),
122+
};
119123

120124
let query = url.query_pairs().filter(|(name, _)| {
121125
matches!(

src/api/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ use std::env;
2727

2828
use crate::app_config::AppConfig;
2929
use actix_multipart::form::{tempfile::TempFile, MultipartForm};
30-
use actix_web::{guard, web};
30+
use actix_web::{guard, web, HttpResponse};
3131
use serde::{Deserialize, Serialize};
3232
use sqlx::PgPool;
3333
use std::sync::Arc;
3434
use strum::{Display, EnumString};
35+
use url::Url;
3536
use utoipa::{
3637
openapi::security::{ApiKey, ApiKeyValue, SecurityScheme},
3738
IntoParams, Modify, OpenApi, ToSchema,
@@ -41,6 +42,18 @@ use crate::{Difficulty, GachaType, GiGachaType, Language, ZzzGachaType};
4142

4243
type ApiResult<T> = Result<T, Box<dyn std::error::Error>>;
4344

45+
pub(crate) fn validate_import_url(raw_url: &str) -> Result<Url, HttpResponse> {
46+
let Ok(url) = Url::parse(raw_url) else {
47+
return Err(HttpResponse::BadRequest().body("Invalid URL"));
48+
};
49+
50+
if !matches!(url.scheme(), "http" | "https") || url.host_str().is_none() {
51+
return Err(HttpResponse::BadRequest().body("Invalid URL"));
52+
}
53+
54+
Ok(url)
55+
}
56+
4457
#[derive(OpenApi)]
4558
#[openapi(tags((name = "pinned")), components(schemas(Language, GachaType, ZzzGachaType, GiGachaType, File, Difficulty)), modifiers(&PrivateAddon))]
4659
struct ApiDoc;

src/api/warps_import/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use utoipa::{OpenApi, ToSchema};
1515
use crate::{
1616
api::{
1717
banner_helpers::{self, HSR_STANDARD},
18-
ApiResult,
18+
validate_import_url, ApiResult,
1919
},
2020
database, mihomo, GachaType, Language,
2121
};
@@ -106,6 +106,7 @@ struct WarpsImport {
106106
request_body = WarpsImportParams,
107107
responses(
108108
(status = 200, description = "WarpsImport", body = WarpsImport),
109+
(status = 400, description = "Invalid URL"),
109110
)
110111
)]
111112
#[post("/api/warps-import")]
@@ -115,7 +116,10 @@ async fn post_warps_import(
115116
warps_import_infos: web::Data<WarpsImportInfos>,
116117
pool: web::Data<PgPool>,
117118
) -> ApiResult<impl Responder> {
118-
let original_url = Url::parse(&params.url)?;
119+
let original_url = match validate_import_url(&params.url) {
120+
Ok(url) => url,
121+
Err(response) => return Ok(response),
122+
};
119123

120124
let mut uid = None;
121125

src/api/zzz/signals_import/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ use strum::IntoEnumIterator;
1212
use url::Url;
1313
use utoipa::{OpenApi, ToSchema};
1414

15-
use crate::{api::ApiResult, database, ZzzGachaType};
15+
use crate::{
16+
api::{validate_import_url, ApiResult},
17+
database, ZzzGachaType,
18+
};
1619

1720
#[derive(OpenApi)]
1821
#[openapi(
@@ -98,6 +101,7 @@ struct SignalsImport {
98101
request_body = SignalsImportParams,
99102
responses(
100103
(status = 200, description = "SignalsImport", body = SignalsImport),
104+
(status = 400, description = "Invalid URL"),
101105
)
102106
)]
103107
#[post("/api/zzz/signals-import")]
@@ -107,7 +111,10 @@ async fn post_zzz_signals_import(
107111
signals_import_infos: web::Data<SignalsImportInfos>,
108112
pool: web::Data<PgPool>,
109113
) -> ApiResult<impl Responder> {
110-
let url = Url::parse(&params.url)?;
114+
let url = match validate_import_url(&params.url) {
115+
Ok(url) => url,
116+
Err(response) => return Ok(response),
117+
};
111118

112119
let query = url.query_pairs().filter(|(name, _)| {
113120
matches!(

0 commit comments

Comments
 (0)