Fix nine reported issues and defects found while auditing around them - #2303
Open
MOMO0302-02 wants to merge 9 commits into
Open
Fix nine reported issues and defects found while auditing around them#2303MOMO0302-02 wants to merge 9 commits into
MOMO0302-02 wants to merge 9 commits into
Conversation
…code Reported issues - Additional rules were emitted in reverse UI order, inverting their priority so they appeared to have no effect (chen08209#2256, chen08209#2251, chen08209#2255). - Exported profiles had no .yaml extension (chen08209#2241). - The run-time counter was clamped at 99:59:59 and could render impossible values such as 99:99:59 (chen08209#2214, chen08209#2191). - The quick-settings tile showed the inverse state: start failures and failed binds were reported as the opposite of reality (chen08209#2260). - Script-mode overwrites failed on Linux because the QuickJS bridge was never bundled (chen08209#2242). - Reading the Wi-Fi SSID could block startup on macOS and silently failed on Windows, leaving exclude-SSID rules inert with no hint (chen08209#2188, chen08209#2286). - Custom geox-url was dropped (wrong yaml keys) and geo auto-update ran even when disabled (chen08209#2215). - Auto-start enabled the system proxy before the core was ready and never rolled back when setup failed (chen08209#2239). - Shutting Windows down left a stale 127.0.0.1 system proxy behind (chen08209#2276). Security - Reject backup archive entries that escape the restore directory. - Verify TLS certificates for non-loopback hosts. Note: self-signed WebDAV servers and subscriptions now fail instead of being accepted silently. Correctness and data safety - Rewrite rule parsing: logic rules, payloads containing "src" or "no-resolve", two-segment rules and single-segment input were corrupted. - Restore with the default compatible strategy no longer replaces local scripts, rules, links and proxy groups; merged rows get order keys allocated after the existing ones. - Delete profile dependents explicitly, since SQLite foreign keys are off. - Preserve excludeSSIDs on restore and the WebDAV file name when editing credentials. - Validate side-loaded providers before overwriting the file on disk. - Fix connection list rendering only half its rows, case-sensitive app search, inverted permission and quick-fill conditions, dead delete button and inert rule switches, duplicate proxy group names, and the double-encoded profile preview. Crashes, hangs and leaks - Core: return owned strings from getTraffic/getTotalTraffic instead of freed memory, guard the event listener with a lock, apply allow-lan, and report side-load and config-validation errors. - Rust IPC: release the state lock before joining, which deadlocked stop and restart whenever the core was connected. - Report core start failures instead of a stale connected state, and time out the shutdown wait. - Fix range errors in the proxy list and tab when a search filter is active, and a format exception in the port dialog. - Cancel the previous poll timer on core restart, batch delay tests lazily, stop the dashboard memory poll after dispose, and dispose the JavaScript runtime after each evaluation. Platform - macOS: do not cancel system logout or shutdown; read the SSID off the platform thread; disable the proxy on every network service. - Android: promote the service to foreground even with the screen off, make VPN start failures observable, and keep the SSID and package caches thread-safe. - Linux: bundle the QuickJS bridge and fix GVariant lifetime handling in the SSID lookup. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Drop two redundant awaits in returns and use the null-aware element marker in Rule.rawValue. Reported by flutter analyze. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The notes pointed at a .fvmrc that no longer exists and named a CI version that does not match the workflow. Verified against .github/workflows/build.yaml and by resolving dependencies: app_links requires Dart 3.12, so the declared 3.8.0 lower bound in pubspec.yaml is not the effective minimum. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ith tests Verifying certificates broke self-hosted WebDAV servers and subscription URLs that present a self-signed or private-CA certificate, with no way to keep using them. Add an application setting, off by default, that accepts them again; loopback stays trusted unconditionally either way. Also extract the archive entry check from the restore task so it can be tested directly, and cover it, the order-key allocation a merge restore depends on, and the options dialog's dismiss path. Drop the now-unused Service.getRunTime wrapper. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
run_build_tool.sh writes its own location into the generated runner
pubspec as a path dependency, but under Git Bash `pwd` yields a POSIX
path ("/f/..."), and the native Dart SDK that resolves it cannot. The
Android build therefore fails on Windows with "could not find package
build_tool". Convert the path with cygpath when it is available, which
leaves Linux and macOS untouched.
Found while building the APK to verify the Kotlin and cgo changes in this
branch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mihomo's log.Errorln is printf-style (log.Errorln -> newLog ->
fmt.Sprintf), so `log.Errorln("TUN:", err)` rendered the reason through
fmt's surplus-operand marker as `TUN:%!(EXTRA *fmt.wrapError=...)`. The
reason was always in the log, just wrapped in that marker: use real
format verbs so the message reads cleanly and go vet's printf check
passes.
This is a readability and lint fix, not a functional one - it does not
resolve any reported TUN startup failure, so please do not backport it
as such.
Found by go vet once the Android toolchain made the cgo-only packages
analysable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both were changed in this branch but had no coverage: the layout delegate now handles a child larger than its viewport, where clamp used to be given an upper bound below its lower one, and safeCopy reports a missing source instead of creating an empty file in its place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Extracting the entry check moved the join to a per-segment form, which made `C:/evil.txt` slip through: it is not posix-absolute and carries no `..`, so it became an ordinary segment and produced a path inside the restore directory that Windows cannot create. Writing it threw an uncaught FileSystemException and aborted the whole restore, where the previous inline code simply skipped the entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
MOMO0302-02
force-pushed
the
fix/reported-issues
branch
from
August 13, 2026 14:47
8f34508 to
a9064f2
Compare
Upstream advanced nine commits (375 files, +27756/-10126) that rewrote the Android service layer, the desktop core transport and the core message layer. Forty files conflicted; each fix was re-checked against the new code rather than replayed mechanically. Dropped because upstream fixed the same defect, independently or by removing the code that carried it: - the whole Android tile/service set (State.kt, Service.kt, CommonService.kt, RemoteService.kt, ModuleLoader.kt) - ServiceState/ServiceController settle the run state from the actual start result, ServiceModules is synchronous, and ProxyService.start rethrows - AppPlugin package enumeration (now PackageResolver, lazy + immutable) and the VPN consent path (denied consent now reports false) - VpnService uid map, NotificationModule foreground promotion, core.cpp scoped_string, CoreLib withTimeout, external-provider lock, the traffic double-free, DAV ping generation guard, WebDAV file name preservation, proxy delay-test batching, the proxies tab/list index mismatch guard, GeoResource config keys, the loading widget, memory info lifecycle, start-button width measurement, macOS location permission mapping, and the Rust IPC deadlock (listener is now non-blocking, so the stop path joins) Kept because the defect is still live on the new code, re-applied against the new APIs: - OptionsDialog reports a dismissed dialog as picking null, which resets the app language to "follow system"; the null delivered by a toggleable radio is now treated as no selection - restoring a backup drops excludeSSIDs, a top-level Config field - quitting skips the debounced config flush that the minimize path performs - a desktop core crash leaves the system proxy pointed at a dead port - deleting a profile orphans groups, links and rules: the declared cascade is inert because sqlite3 disables foreign keys and beforeOpen does not enable them - proxy group delete by name deletes the wrong group mid-rename; renaming onto an existing name is not rejected; a new group's order key is not persisted - the zip-slip guard on backup restore - eventListener is read from core goroutines while the platform thread replaces and releases it - macOS system-proxy teardown stops at the first failed network service - Windows leaves the system proxy set across shutdown/logoff - Windows reports Wi-Fi permission as granted regardless, and forwards non-UTF-8 SSIDs that the Dart side cannot decode - a disposed connections view writes to its released notifier - new profiles are inserted at the top and then jump to the bottom - an offline update check is reported as "already up to date" - a geo update error is announced as a successful update first Verified: flutter analyze clean, 740 of 741 tests pass, go build and go vet clean, cgo cross-build for android/arm64 clean. The one failing test, task_test's makeRealProfileTask case, fails identically on pristine origin/main under Windows because it asserts POSIX separators against a path built with package:path join; it is untouched by this branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This branch collects fixes for reported issues plus a set of defects found while auditing the surrounding code. It is deliberately submitted as one branch so the history stays reviewable; happy to split it into per-issue pull requests on request.
Please read the Verification and Behaviour changes sections before merging.
Already fixed on main — dropped from this branch
These were part of the original submission and are no longer here, because
mainnow fixes the same defect, either independently or by removing the code that carried it. Listed so you do not go looking for them:queryAddedRulesnow orders both terms ascending onmain.999:59:59instead of99:59:59.ServiceStatesettles the run state from the actual start result,ServiceModulesis synchronous,ProxyService.startrethrows, and a denied VPN consent now reportsfalse._RunRequestmodel covers the auto-start rollback.getTraffic/getTotalTrafficdouble free, thestop_ipc_serverdeadlock (the listener is non-blocking now, so the stop path joins cleanly),CoreLib.withTimeout, the external-provider lock, the DAV ping generation guard, the WebDAV file-name preservation, proxy delay-test batching, the proxies tab index mismatch, theGeoResourceconfig keys, the loading widget, memory-info lifecycle, start-button width measurement, the Android package enumeration race, the uid map, and notification foreground promotion.Reported issues still addressed here
flutter_jsexports its prebuilt QuickJS bridge under a misspelled variable (flutter_qjs_bundled_libraries), so Flutter's plugin bundling never picks it up. Bundled explicitly inlinux/CMakeLists.txt.geox-urlkey half of this issue is already fixed onmain.checkPermissionto "granted" while the WLAN API returnsERROR_ACCESS_DENIED, so exclude-SSID rules are inert with no hint. It also forwards raw 802.11 SSID octets, which the standard codec cannot decode when they are not valid UTF-8.WM_QUERYENDSESSION/WM_ENDSESSIONat shutdown, notWM_CLOSE, so the Dart quit path never runs and the per-user WinINET proxy setting survives the reboot.Other defects found while auditing
Security
../, an absolute path, or a drive qualifier (zip slip). A crafted or MITM'd backup could write anywhere the user can write, and a drive-qualified entry aborts the whole restore instead of skipping one file.badCertificateCallbackreturnstruefor every host, so subscription downloads, WebDAV backups and update checks accept any forged certificate. Now restricted to loopback. See Behaviour changes.Data loss and corruption
Rule.parsesplits on commas with no awareness of logic rules, drops any segment merely containingsrc/no-resolve, aliases a two-segment rule's payload to its target, and throws aRangeErroron single-segment input. Corrupted rules are then persisted by the v1→v2 migration and by quick-fill. Rewritten with a bracket-aware split and exact trailing-parameter matching;test/models/rule_test.dartadded.beforeOpendoes not enable them, so the declared cascades never fire; profile dependents are now deleted explicitly.excludeSSIDsis a top-levelConfigfield and is dropped on restore.Crashes, hangs, leaks
FormatExceptionin the port dialog after collapsing an emptied field — collapsed fields leave the form, so their validators never run.ValueNotifierafterdisposehas released it.SuperGridcaptures a slot index, so a card deleted while the list changes removes the wrong one; anddidUpdateWidgetresets the controller mid-animation, soonDeletenever fires at all.eventListeneris read from core goroutines while the platform thread replaces and releases it — a data race over a JNI global reference.UI and logic
WeChatmatches nothing.no-resolve/srcswitches have no handler.nullback, which resets the app language to "follow system"; the radios are toggleable, so re-tapping the current option does the same.Platform
run_build_tool.shwrites its own location into the generated runner pubspec, but under Git Bashpwdyields a POSIX path the native Dart SDK cannot resolve, so the build fails with "could not find package build_tool".log.Errorlntakes a format string, solog.Errorln("TUN:", err)renders the reason through fmt's surplus-operand marker asTUN:%!(EXTRA *fmt.wrapError=...). The reason was always there, just wrapped in that marker — this is a readability andgo vetfix, not a functional one, and it resolves no reported TUN failure.applicationShouldTerminatealways returns.terminateCancel, which aborts system logout and shutdown.stopProxygives up on the first failingnetworksetupcall, leaving the remaining services proxied; the default network service name is truncated at the first space, breakingThunderbolt Bridgeand similar.checkIsAdminshell-escapes an argv element, so any install path containing a space always re-prompts for credentials.g_variant_iter_loopvalues leak on early exit, and the connman branch uses strings after the iterator has released them.Verification
Re-run in full after the merge, against Flutter 3.44.9 / Dart 3.12.2:
flutter analyze— no issues.flutter test— 740 of 741 pass. The one failure,task_test's makeRealProfileTask normalizes runtime config and added rules, fails identically on a pristineorigin/mainworktree under Windows: it asserts a POSIX prefix (/profiles/providers/7/proxies/) against a path built withpackage:path's platform-awarejoin. It is untouched by this branch, and it passes on your Linux CI. Happy to make that assertion platform-agnostic in a separate pull request if you want it.plugins/proxyandplugins/wifi_ssidanalysed and tested separately, since the rootanalysis_options.yamlexcludesplugins/**— clean, 21 and 5 tests respectively.go build ./...andgo vet ./...pass for the core, both in the default build and cross-compiled forandroid/arm64withCGO_ENABLED=1through the NDK, which is what bringslib.go,bride.go,tun/andplatform/into analysis at all.Not compiled: the macOS Swift change needs a macOS host, the Linux C change needs a Linux one, and the Windows C++ changes need MSVC — none of which I have, so all three were reviewed by hand rather than built.
build.yamlonly triggers onv*tags, so opening this pull request runs no automated build either. Please treat those three with corresponding scepticism, and tell me if you would rather I drop them from this branch.There are no longer any Kotlin or Rust changes in this branch, so the APK build and
cargo checkthat the earlier revision relied on are no longer load-bearing.Behaviour changes worth calling out
null. Tapping the glyph of an already-selected radio now closes without changing anything; picking thenulloption is still reachable by tapping its row.