diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 09fe471..fd4fc69 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,35 +99,19 @@ jobs: - uses: dtolnay/rust-toolchain@stable - run: cargo install cargo-audit --locked - # Two advisory clusters are ignored, each with its reasoning and a tracking - # issue. They are ignored rather than tolerated as a red check because a job - # that is permanently failing teaches everyone to stop reading it -- at which - # point the NEXT advisory, the one that does matter, goes unnoticed too. + # Run with no exemptions at all. rmcp 2 cleared RUSTSEC-2026-0189 and the + # oauth2 5 / reqwest 0.12 upgrade cleared the rustls-webpki cluster, so + # every actionable advisory is genuinely fixed rather than suppressed. + # Only the unmaintained gtk-rs warnings remain, which cargo audit reports + # without failing on. # - # Re-litigate both when the upgrades land. Neither is a permanent exemption. + # Keep it exemption-free. A bare invocation is self-enforcing: adding an + # --ignore is a visible diff to this file, and a job that is permanently + # red is a job everyone stops reading -- at which point the NEXT advisory, + # the one that matters, goes unnoticed too. - name: cargo audit working-directory: src-tauri - run: | - cargo audit \ - --ignore RUSTSEC-2026-0099 \ - --ignore RUSTSEC-2026-0098 \ - --ignore RUSTSEC-2026-0104 - - # RUSTSEC-2026-0189 (rmcp) is NO LONGER IGNORED -- rmcp 2.x cleared it. - # - # RUSTSEC-2026-0099 / -0098 / -0104 (rustls-webpki 0.101.7) - # Transitive: rustls-webpki <- rustls 0.21 <- reqwest 0.11 <- oauth2 4.4. - # Clearing them requires upgrading oauth2 to 5.0 and reqwest to 0.12+ - # together, since oauth2 4.4 pins reqwest 0.11. Both are breaking changes. - - name: Advisory exemptions must stay documented - run: | - set -euo pipefail - # Guards against an ignore being added without a reason next to it. - for id in RUSTSEC-2026-0099 RUSTSEC-2026-0098 RUSTSEC-2026-0104; do - grep -q "$id" .github/workflows/ci.yml || { - echo "::error::$id is ignored but has no documented justification"; exit 1; } - done - echo "OK: every advisory exemption carries its reasoning" + run: cargo audit # ThinkUtils tells users which packages to install and which commands to run. # Getting the distro wrong hands someone a command that cannot work. diff --git a/.husky/pre-commit b/.husky/pre-commit index 4e47893..9c0dbd6 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,14 +1,9 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" - echo "🔍 Running pre-commit checks..." echo "" # Run lint-staged to format and lint only staged files echo "📝 Formatting and linting staged files..." -npx lint-staged - -if [ $? -ne 0 ]; then +if ! npx lint-staged; then echo "" echo "❌ Linting failed. Please fix the issues and try again." exit 1 diff --git a/build/gui-test-out/deb_ubuntu_24.04/ocr.txt b/build/gui-test-out/deb_ubuntu_24.04/ocr.txt deleted file mode 100644 index e96095b..0000000 --- a/build/gui-test-out/deb_ubuntu_24.04/ocr.txt +++ /dev/null @@ -1,27 +0,0 @@ -Permissions Required -One-time setup -ThinkUtils needs permission to access system files for -controlling your laptop's hardware. This is a one-time -setup that will allow the app to manage CPU, fan, and -battery settings. -WHAT WILL BE CONFIGURED: -y CPU frequency and governor control -¥ Fan speed control -¥ Battery charge thresholds -¥ Turbo boost settings -You'll be prompted for your password once. After setup, no -password will be required. -Skip for Now Setup Permissions -Permissions Required -One-time setup -ThinkUtils needs permission to access system files for -controlling your laptop's hardware. This is a one-time -setup that will allow the app to manage CPU, fan, and -battery settings. -WHAT WILL BE CONFIGURED: -vy CPU frequency and governor control -vy Fan speed control -v Battery charge thresholds -vy Turbo boost settings -You'll be prompted for your password once. After setup, no -password will be required \ No newline at end of file diff --git a/build/gui-test-out/deb_ubuntu_24.04/shot.png b/build/gui-test-out/deb_ubuntu_24.04/shot.png deleted file mode 100644 index c322839..0000000 Binary files a/build/gui-test-out/deb_ubuntu_24.04/shot.png and /dev/null differ diff --git a/build/gui-test-out/deb_ubuntu_24.04/shot_root.png b/build/gui-test-out/deb_ubuntu_24.04/shot_root.png deleted file mode 100644 index 69bdca8..0000000 Binary files a/build/gui-test-out/deb_ubuntu_24.04/shot_root.png and /dev/null differ diff --git a/build/gui-test-out/rpm_fedora_41/ocr.txt b/build/gui-test-out/rpm_fedora_41/ocr.txt deleted file mode 100644 index ee45e7e..0000000 --- a/build/gui-test-out/rpm_fedora_41/ocr.txt +++ /dev/null @@ -1,26 +0,0 @@ -Permissions Required -One-time setup -ThinkUtils needs permission to access system files for -controlling your laptop's hardware. This is a one-time setup -that will allow the app to manage CPU, fan, and battery -settings. -WHAT WILL BE CONFIGURED: -y CPU frequency and governor control -vy Fan speed control -Y Battery charge thresholds -Y Turbo boost settings -You'll be prompted for your password once. After setup, no password -will be required. -Permissions Required -One-time setup -ThinkUtils needs permission to access system files for -controlling your laptop's hardware. This is a one-time setup -that will allow the app to manage CPU, fan, and battery -settings. -WHAT WILL BE CONFIGURED: -CPU frequency and governor control -v Fan speed control -v Battery charge thresholds -Y Turbo boost settings -You'll be prompted for your password once. After setup, no password -will be required. \ No newline at end of file diff --git a/build/gui-test-out/rpm_fedora_41/shot.png b/build/gui-test-out/rpm_fedora_41/shot.png deleted file mode 100644 index 3dfc0d6..0000000 Binary files a/build/gui-test-out/rpm_fedora_41/shot.png and /dev/null differ diff --git a/build/gui-test-out/rpm_fedora_41/shot_root.png b/build/gui-test-out/rpm_fedora_41/shot_root.png deleted file mode 100644 index 2b8b910..0000000 Binary files a/build/gui-test-out/rpm_fedora_41/shot_root.png and /dev/null differ diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 098501a..70ba18a 100755 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -229,10 +229,10 @@ dependencies = [ "bytes", "form_urlencoded", "futures-util", - "http 1.4.2", - "http-body 1.1.0", + "http", + "http-body", "http-body-util", - "hyper 1.10.1", + "hyper", "hyper-util", "itoa", "matchit", @@ -244,7 +244,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tower", "tower-layer", @@ -260,23 +260,17 @@ checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", "futures-core", - "http 1.4.2", - "http-body 1.1.0", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", - "sync_wrapper 1.0.2", + "sync_wrapper", "tower-layer", "tower-service", "tracing", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.7" @@ -517,6 +511,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" + [[package]] name = "chacha20" version = "0.10.1" @@ -947,7 +947,7 @@ dependencies = [ "rustc_version", "toml 1.1.3+spec-1.1.0", "vswhom", - "winreg 0.55.0", + "winreg", ] [[package]] @@ -1386,9 +1386,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", + "wasm-bindgen", ] [[package]] @@ -1541,16 +1543,16 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.27" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", - "http 0.2.12", + "http", "indexmap 2.14.0", "slab", "tokio", @@ -1604,17 +1606,6 @@ dependencies = [ "markup5ever", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.4.2" @@ -1625,17 +1616,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.1.0" @@ -1643,7 +1623,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", - "http 1.4.2", + "http", ] [[package]] @@ -1654,8 +1634,8 @@ checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ "bytes", "futures-core", - "http 1.4.2", - "http-body 1.1.0", + "http", + "http-body", "pin-project-lite", ] @@ -1671,30 +1651,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "hyper" -version = "0.14.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.5.10", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.10.1" @@ -1705,8 +1661,9 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "http 1.4.2", - "http-body 1.1.0", + "h2", + "http", + "http-body", "httparse", "httpdate", "itoa", @@ -1718,29 +1675,34 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.32", + "http", + "hyper", + "hyper-util", "rustls", "tokio", "tokio-rustls", + "tower-service", + "webpki-roots", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper 0.14.32", + "http-body-util", + "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] @@ -1753,17 +1715,19 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.4.2", - "http-body 1.1.0", - "hyper 1.10.1", + "http", + "http-body", + "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.5", + "socket2", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -2174,6 +2138,12 @@ version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "markup5ever" version = "0.38.0" @@ -2340,16 +2310,16 @@ dependencies = [ [[package]] name = "oauth2" -version = "4.4.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c38841cdd844847e3e7c8d29cef9dcfed8877f8f56f9071f77843ecf3baf937f" +checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" dependencies = [ - "base64 0.13.1", + "base64 0.22.1", "chrono", "getrandom 0.2.17", - "http 0.2.12", + "http", "rand 0.8.7", - "reqwest 0.11.27", + "reqwest 0.12.28", "serde", "serde_json", "serde_path_to_error", @@ -2925,6 +2895,62 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.19", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" +dependencies = [ + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand 0.10.2", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.19", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + [[package]] name = "quote" version = "1.0.47" @@ -2993,6 +3019,15 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -3070,46 +3105,48 @@ checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.32", + "http", + "http-body", + "http-body-util", + "hyper", "hyper-rustls", "hyper-tls", - "ipnet", + "hyper-util", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", + "quinn", "rustls", - "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", + "sync_wrapper", "tokio", "tokio-native-tls", "tokio-rustls", + "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", "webpki-roots", - "winreg 0.50.0", ] [[package]] @@ -3122,10 +3159,10 @@ dependencies = [ "bytes", "futures-core", "futures-util", - "http 1.4.2", - "http-body 1.1.0", + "http", + "http-body", "http-body-util", - "hyper 1.10.1", + "hyper", "hyper-util", "js-sys", "log", @@ -3133,7 +3170,7 @@ dependencies = [ "pin-project-lite", "serde", "serde_json", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tokio-util", "tower", @@ -3171,8 +3208,8 @@ dependencies = [ "bytes", "chrono", "futures", - "http 1.4.2", - "http-body 1.1.0", + "http", + "http-body", "http-body-util", "pastey", "pin-project-lite", @@ -3234,32 +3271,36 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.12" +version = "0.23.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" dependencies = [ - "log", + "once_cell", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] -name = "rustls-pemfile" -version = "1.0.4" +name = "rustls-pki-types" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ - "base64 0.21.7", + "web-time", + "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] @@ -3364,16 +3405,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "security-framework" version = "3.7.0" @@ -3658,16 +3689,6 @@ version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.5" @@ -3734,7 +3755,7 @@ checksum = "39f24a9b78c40b90817bbcd1821c74ddfd74916aadd29403d001532a9195532d" dependencies = [ "bytes", "futures-util", - "http-body 1.1.0", + "http-body", "http-body-util", "pin-project-lite", ] @@ -3775,6 +3796,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "swift-rs" version = "1.0.7" @@ -3818,12 +3845,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.2" @@ -3846,20 +3867,20 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.13.1", "core-foundation 0.9.4", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -3951,7 +3972,7 @@ dependencies = [ "glob", "gtk", "heck 0.5.0", - "http 1.4.2", + "http", "jni", "libc", "log", @@ -4127,7 +4148,7 @@ dependencies = [ "cookie", "dpi", "gtk", - "http 1.4.2", + "http", "jni", "objc2", "objc2-ui-kit", @@ -4150,7 +4171,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f" dependencies = [ "gtk", - "http 1.4.2", + "http", "jni", "log", "objc2", @@ -4182,7 +4203,7 @@ dependencies = [ "dom_query", "dunce", "glob", - "http 1.4.2", + "http", "infer", "json-patch", "log", @@ -4251,7 +4272,7 @@ dependencies = [ "oauth2", "rand 0.8.7", "regex", - "reqwest 0.11.27", + "reqwest 0.12.28", "rmcp", "schemars 0.8.22", "serde", @@ -4386,7 +4407,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.5", + "socket2", "tokio-macros", "windows-sys 0.61.2", ] @@ -4414,9 +4435,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ "rustls", "tokio", @@ -4575,7 +4596,7 @@ dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 1.0.2", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -4591,8 +4612,8 @@ dependencies = [ "bitflags 2.13.1", "bytes", "futures-util", - "http 1.4.2", - "http-body 1.1.0", + "http", + "http-body", "pin-project-lite", "tower", "tower-layer", @@ -4947,6 +4968,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "web_atoms" version = "0.2.5" @@ -5005,9 +5036,12 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "webview2-com" @@ -5194,6 +5228,17 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + [[package]] name = "windows-result" version = "0.3.4" @@ -5239,15 +5284,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -5299,21 +5335,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -5371,12 +5392,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -5395,12 +5410,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -5419,12 +5428,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -5455,12 +5458,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -5479,12 +5476,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -5503,12 +5494,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -5527,12 +5512,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -5569,16 +5548,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winreg" version = "0.55.0" @@ -5617,7 +5586,7 @@ dependencies = [ "dunce", "gdkx11", "gtk", - "http 1.4.2", + "http", "javascriptcore-rs", "jni", "libc", @@ -5791,6 +5760,12 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + [[package]] name = "zerotrie" version = "0.2.4" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 85508de..a7767b2 100755 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -26,10 +26,10 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["full"] } regex = "1" -reqwest = { version = "0.11", features = ["json", "blocking"] } +reqwest = { version = "0.12", features = ["json", "blocking"] } chrono = { version = "0.4", features = ["serde"] } base64 = "0.21" -oauth2 = "4.4" +oauth2 = "5" url = "2.5" tiny_http = "0.12" rand = "0.8" diff --git a/src-tauri/src/sync.rs b/src-tauri/src/sync.rs index 84c1df3..da5519d 100755 --- a/src-tauri/src/sync.rs +++ b/src-tauri/src/sync.rs @@ -1,6 +1,5 @@ use chrono::Utc; use oauth2::basic::BasicClient; -use oauth2::reqwest::async_http_client; use oauth2::{ AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge, RedirectUrl, Scope, TokenResponse, TokenUrl, @@ -161,7 +160,29 @@ fn write_private(path: &std::path::Path, content: &str) -> Result<(), String> { Ok(()) } -fn create_oauth_client() -> Result { +/// The HTTP client oauth2 5.x uses for token exchange. +/// +/// Redirects are disabled deliberately, per the oauth2 crate's own guidance: +/// following them on a token endpoint opens the client to SSRF. In 4.x this was +/// hidden inside the crate's `async_http_client`; 5.x hands the caller the +/// choice, so the choice has to be made explicitly. +fn oauth_http_client() -> Result { + reqwest::ClientBuilder::new() + .redirect(reqwest::redirect::Policy::none()) + .build() + .map_err(|e| format!("Failed to build HTTP client: {}", e)) +} + +fn create_oauth_client() -> Result< + oauth2::basic::BasicClient< + oauth2::EndpointSet, + oauth2::EndpointNotSet, + oauth2::EndpointNotSet, + oauth2::EndpointNotSet, + oauth2::EndpointSet, + >, + String, +> { let client_id = ClientId::new( GOOGLE_CLIENT_ID .filter(|s| !s.is_empty()) @@ -181,13 +202,17 @@ fn create_oauth_client() -> Result { let token_url = TokenUrl::new("https://oauth2.googleapis.com/token".to_string()) .map_err(|e| format!("Invalid token URL: {}", e))?; - Ok( - BasicClient::new(client_id, Some(client_secret), auth_url, Some(token_url)) - .set_redirect_uri( - RedirectUrl::new(REDIRECT_URI.to_string()) - .map_err(|e| format!("Invalid redirect URL: {}", e))?, - ), - ) + // 5.x replaces the four-argument constructor with a builder, and encodes + // which endpoints are configured in the type -- hence the EndpointSet + // parameters on the return type above. + Ok(BasicClient::new(client_id) + .set_client_secret(client_secret) + .set_auth_uri(auth_url) + .set_token_uri(token_url) + .set_redirect_uri( + RedirectUrl::new(REDIRECT_URI.to_string()) + .map_err(|e| format!("Invalid redirect URL: {}", e))?, + )) } // Initiate Google OAuth flow @@ -291,10 +316,11 @@ async fn exchange_code_for_token(code: String, state: String) -> Result<(), Stri let client = create_oauth_client()?; + let http_client = oauth_http_client()?; let token_result = client .exchange_code(AuthorizationCode::new(code)) .set_pkce_verifier(oauth2::PkceCodeVerifier::new(pkce_verifier)) - .request_async(async_http_client) + .request_async(&http_client) .await .map_err(|e| format!("Token exchange failed: {}", e))?; @@ -686,4 +712,74 @@ mod tests { let _ = fs::remove_file(&path); } + + /// The redirect policy on the OAuth client is the SSRF guard, and nothing in + /// the type system holds it in place: swapping `oauth_http_client()` back to + /// `reqwest::Client::new()` compiles cleanly and silently restores the hole. + /// oauth2 4.x hid this decision inside the crate; 5.x made it the caller's, + /// which means it is now ours to regress. + /// + /// So assert the behaviour rather than the construction — serve a 302 and + /// require that it comes back *as* a 302, with the redirect target never + /// requested. + #[tokio::test] + async fn oauth_client_does_not_follow_redirects() { + use std::io::{Read, Write}; + use std::sync::mpsc; + + let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("bind loopback"); + let addr = listener.local_addr().unwrap(); + let (tx, rx) = mpsc::channel::(); + + // Detached: if the client correctly declines to follow, the second + // accept() blocks forever. The test never joins it, and the harness + // does not wait on detached threads at exit. + std::thread::spawn(move || { + for stream in listener.incoming() { + let Ok(mut stream) = stream else { break }; + let mut buf = [0u8; 1024]; + let n = stream.read(&mut buf).unwrap_or(0); + let path = String::from_utf8_lossy(&buf[..n]) + .lines() + .next() + .and_then(|l| l.split_whitespace().nth(1)) + .unwrap_or("?") + .to_string(); + if tx.send(path.clone()).is_err() { + break; + } + let response = if path == "/token" { + "HTTP/1.1 302 Found\r\nLocation: /followed\r\nContent-Length: 0\r\n\r\n" + } else { + "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n" + }; + let _ = stream.write_all(response.as_bytes()); + } + }); + + let response = oauth_http_client() + .expect("client builds") + .get(format!("http://{}/token", addr)) + .send() + .await + .expect("request reaches the local server"); + + assert_eq!( + response.status().as_u16(), + 302, + "the 302 was consumed instead of returned, so redirects are being followed" + ); + + assert_eq!( + rx.recv_timeout(std::time::Duration::from_secs(5)).unwrap(), + "/token", + "server should have seen the initial request" + ); + assert!( + rx.recv_timeout(std::time::Duration::from_millis(500)) + .is_err(), + "client followed the Location header — a token endpoint that redirects \ + can now point this client at an arbitrary host" + ); + } }