forked from didirus/AstralRinth
* fix(app-lib, labrinth): stricter mrpack file path validation * chore: run `cargo fmt` * tweak: reject reserved Windows device names in mrpacks too
116 lines
3.4 KiB
Rust
116 lines
3.4 KiB
Rust
use crate::{
|
|
models::v2::projects::LegacySideType, util::env::parse_strings_from_var,
|
|
};
|
|
use path_util::SafeRelativeUtf8UnixPathBuf;
|
|
use serde::{Deserialize, Serialize};
|
|
use validator::Validate;
|
|
|
|
#[derive(Serialize, Deserialize, Validate, Eq, PartialEq, Debug)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct PackFormat {
|
|
pub game: String,
|
|
pub format_version: i32,
|
|
#[validate(length(min = 1, max = 512))]
|
|
pub version_id: String,
|
|
#[validate(length(min = 1, max = 512))]
|
|
pub name: String,
|
|
#[validate(length(max = 2048))]
|
|
pub summary: Option<String>,
|
|
#[validate(nested)]
|
|
pub files: Vec<PackFile>,
|
|
pub dependencies: std::collections::HashMap<PackDependency, String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Validate, Eq, PartialEq, Debug, Clone)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct PackFile {
|
|
pub path: SafeRelativeUtf8UnixPathBuf,
|
|
pub hashes: std::collections::HashMap<PackFileHash, String>,
|
|
pub env: Option<std::collections::HashMap<EnvType, LegacySideType>>, // TODO: Should this use LegacySideType? Will probably require a overhaul of mrpack format to change this
|
|
#[validate(custom(function = "validate_download_url"))]
|
|
pub downloads: Vec<String>,
|
|
pub file_size: u32,
|
|
}
|
|
|
|
fn validate_download_url(
|
|
values: &[String],
|
|
) -> Result<(), validator::ValidationError> {
|
|
for value in values {
|
|
let url = url::Url::parse(value)
|
|
.ok()
|
|
.ok_or_else(|| validator::ValidationError::new("invalid URL"))?;
|
|
|
|
if url.as_str() != value {
|
|
return Err(validator::ValidationError::new("invalid URL"));
|
|
}
|
|
|
|
let domains = parse_strings_from_var("WHITELISTED_MODPACK_DOMAINS")
|
|
.unwrap_or_default();
|
|
if !domains.contains(
|
|
&url.domain()
|
|
.ok_or_else(|| validator::ValidationError::new("invalid URL"))?
|
|
.to_string(),
|
|
) {
|
|
return Err(validator::ValidationError::new(
|
|
"File download source is not from allowed sources",
|
|
));
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Eq, PartialEq, Hash, Debug, Clone)]
|
|
#[serde(rename_all = "camelCase", from = "String")]
|
|
pub enum PackFileHash {
|
|
Sha1,
|
|
Sha512,
|
|
Unknown(String),
|
|
}
|
|
|
|
impl From<String> for PackFileHash {
|
|
fn from(s: String) -> Self {
|
|
match s.as_str() {
|
|
"sha1" => PackFileHash::Sha1,
|
|
"sha512" => PackFileHash::Sha512,
|
|
_ => PackFileHash::Unknown(s),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Eq, PartialEq, Hash, Debug, Clone)]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub enum EnvType {
|
|
Client,
|
|
Server,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Clone, Hash, PartialEq, Eq, Debug)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
pub enum PackDependency {
|
|
Forge,
|
|
Neoforge,
|
|
FabricLoader,
|
|
QuiltLoader,
|
|
Minecraft,
|
|
}
|
|
|
|
impl std::fmt::Display for PackDependency {
|
|
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
fmt.write_str(self.as_str())
|
|
}
|
|
}
|
|
|
|
impl PackDependency {
|
|
// These are constant, so this can remove unnecessary allocations (`to_string`)
|
|
pub fn as_str(&self) -> &'static str {
|
|
match self {
|
|
PackDependency::Forge => "forge",
|
|
PackDependency::Neoforge => "neoforge",
|
|
PackDependency::FabricLoader => "fabric-loader",
|
|
PackDependency::Minecraft => "minecraft",
|
|
PackDependency::QuiltLoader => "quilt-loader",
|
|
}
|
|
}
|
|
}
|