diff --git a/Cargo.lock b/Cargo.lock index f63504a..70fb569 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -367,6 +367,26 @@ dependencies = [ "virtue", ] +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.10.5", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex 1.3.0", + "syn", +] + [[package]] name = "bitflags" version = "2.11.1" @@ -521,7 +541,7 @@ dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex", + "shlex 2.0.1", ] [[package]] @@ -530,6 +550,15 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.4" @@ -614,6 +643,17 @@ dependencies = [ "inout 0.2.2", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.60" @@ -725,6 +765,15 @@ dependencies = [ "typewit", ] +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -1317,6 +1366,12 @@ dependencies = [ "url", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "foldhash" version = "0.1.5" @@ -2158,6 +2213,16 @@ version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + [[package]] name = "libm" version = "0.2.16" @@ -2914,6 +2979,15 @@ dependencies = [ "wnaf", ] +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -3317,6 +3391,7 @@ dependencies = [ "either", "indexmap 2.14.0", "rquickjs-core", + "rquickjs-macro", ] [[package]] @@ -3335,12 +3410,30 @@ dependencies = [ "rquickjs-sys", ] +[[package]] +name = "rquickjs-macro" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7204086521740ca03f33036579801871aa5b714a0e7f68b4c2e98d68b0380bfb" +dependencies = [ + "convert_case", + "fnv", + "ident_case", + "indexmap 2.14.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "rquickjs-core", + "syn", +] + [[package]] name = "rquickjs-sys" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eef73520804cf5aa4876097ac5733058d628c769eba9678e0f4dba5a4ef79703" dependencies = [ + "bindgen", "cc", ] @@ -3789,6 +3882,12 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "shlex" version = "2.0.1" @@ -4230,7 +4329,7 @@ dependencies = [ "indexmap 2.14.0", "serde_core", "serde_spanned", - "toml_datetime", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", "winnow 0.7.15", @@ -4245,6 +4344,27 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.13+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + [[package]] name = "toml_parser" version = "1.1.2+spec-1.1.0" @@ -4368,6 +4488,12 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + [[package]] name = "unicode-width" version = "0.2.2" @@ -5107,6 +5233,9 @@ name = "winnow" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] [[package]] name = "wit-bindgen" diff --git a/Cargo.toml b/Cargo.toml index 4a1daef..a11eedb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,7 +96,7 @@ p256 = { version = "0.14.0", optional = true } pbkdf2 = { version = "0.13.0", optional = true } rand = { version = "0.10.2", optional = true } regex = "1" -rquickjs = { version = "0.12.1", default-features = false, features = ["std", "chrono", "loader", "dyn-load", "either", "indexmap", "futures"], optional = true } +rquickjs = { version = "0.12.1", default-features = false, features = ["std", "chrono", "loader", "dyn-load", "either", "indexmap", "futures", "bindgen"], optional = true } rsa = { version = "0.9.10", features = ["sha2"], optional = true } scraper = { version = "0.27.0", optional = true } sha1 = { version = "0.11.0", optional = true } diff --git a/src/profile_avatar_pack.rs b/src/profile_avatar_pack.rs index fb428b6..ee92aa5 100644 --- a/src/profile_avatar_pack.rs +++ b/src/profile_avatar_pack.rs @@ -150,10 +150,14 @@ pub(crate) fn profile_avatar_pack_json(request_json: &str) -> Option { let mut avatars = Vec::new(); let mut seen_urls = HashSet::new(); for image in images { - let Some(url) = image.get("url").and_then(Value::as_str).map(str::trim) else { + let Some(raw_url) = image.get("url").and_then(Value::as_str).map(str::trim) else { continue; }; - if !is_https_url(url) || !seen_urls.insert(url.to_string()) { + if !is_https_url(raw_url) { + continue; + } + let url = normalize_avatar_url(raw_url); + if !seen_urls.insert(url.clone()) { continue; } let name = image @@ -246,6 +250,34 @@ fn percent_encode(value: &str) -> String { .collect() } +fn normalize_avatar_url(url: &str) -> String { + let Some(rest) = url.strip_prefix("https://github.com/") else { + return url.to_string(); + }; + let mut blob_segments = rest.splitn(2, "/blob/"); + let Some(owner_repo) = blob_segments.next() else { + return url.to_string(); + }; + let Some(ref_and_path) = blob_segments.next() else { + return url.to_string(); + }; + let mut owner_repo_parts = owner_repo.splitn(2, '/'); + let (Some(owner), Some(repo)) = (owner_repo_parts.next(), owner_repo_parts.next()) else { + return url.to_string(); + }; + if owner.is_empty() || repo.is_empty() { + return url.to_string(); + } + let ref_and_path = ref_and_path.split('?').next().unwrap_or(ref_and_path); + let Some((reference, path)) = ref_and_path.split_once('/') else { + return url.to_string(); + }; + if reference.is_empty() || path.is_empty() { + return url.to_string(); + } + format!("https://raw.githubusercontent.com/{owner}/{repo}/{reference}/{path}") +} + fn is_https_url(value: &str) -> bool { let value = value.trim(); let Some(rest) = value.strip_prefix("https://") else { @@ -320,4 +352,24 @@ mod tests { assert_eq!(output["avatars"].as_array().unwrap().len(), 1); assert_eq!(output["avatars"][0]["name"], "A"); } + + #[test] + fn pack_parser_rewrites_github_blob_urls_to_raw() { + let output: Value = serde_json::from_str( + &profile_avatar_pack_json( + r#"{ + "manifestUrl":"https://example.com/pack.json", + "pack":{"title":"Hell's Paradise","images":[ + {"name":"Choubei","url":"https://github.com/eueueue292/Fusion-Profile-Avatars/blob/main/Hells%20Paradise/Choubei.PNG?raw=true&v=3"} + ]} + }"#, + ) + .unwrap(), + ) + .unwrap(); + assert_eq!( + output["avatars"][0]["url"], + "https://raw.githubusercontent.com/eueueue292/Fusion-Profile-Avatars/main/Hells%20Paradise/Choubei.PNG" + ); + } }