fix: honour a base URL path prefix, reject unusable server URLs, test wear in CI#35
Merged
Conversation
… wear in CI
BaseUrlInterceptor copied scheme/host/port from the configured base URL and
dropped everything else. Endpoint paths are absolute ("/v1/members"), so an
instance hosted at https://example.org/sheaf/ had every request sent to
https://example.org/v1/... instead: whatever happens to live at that domain's
root. Only origin-root deployments ever worked. The rewrite now carries the
base URL's path prefix through, extracted as a pure applyBaseUrl() with tests
(query params were already fine).
Cleartext: the app told users to "type the http:// prefix explicitly" for a
plaintext server, normalizeBaseUrl accepted it, and then targetSdk 35's default
(cleartextTrafficPermitted=false) blocked every request and surfaced it as a
generic network failure. The affordance we advertised only ever worked in debug,
against loopback, which is the only thing the debug network-security-config
whitelists. Validate the address when it is entered instead: non-loopback http
is refused with an explanation, debug builds still allow the loopback set, and
the misleading copy in both the login flow and Settings > Server is gone. Also
rejects a bare "https://", which normalizeBaseUrl otherwise mangled into
something that parses.
CI: the release workflows ran :app:test only, so every wear release (which they
build and ship) went out without its tests ever running. Both now run
:app:test :wear:test and collect the wear reports.
SiteRelEnby
enabled auto-merge
July 14, 2026 14:27
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.
Third and last PR from this review round (security is #33, correctness is #34).
Self-hosting behind a path prefix never worked
Endpoint paths are absolute (
/v1/members), and Retrofits placeholder base ishttp://localhost/`, sothe interceptor only ever had to swap the origin.
baseUrl.encodedPathwas never read. Configurehttps://example.org/sheaf/and every request goes tohttps://example.org/v1/members- i.e. to whateverlives at that domain`s root, which is not us. Only instances at the root of a domain have ever worked.
Now the path prefix is carried through. Pulled the rewrite out into a pure
applyBaseUrl(original, base)so it can be tested: origin-root, prefix, trailing slash, nested prefix, non-default port, query params,
pre-encoded segments, cleartext loopback. (Query params were never broken, despite the report - the
original builder preserves them.)
We advertised plaintext servers and then blocked them
targetSdkis 35, so the platform default iscleartextTrafficPermitted="false", and the onlynetwork-security-config in the tree is the debug one (loopback + 10.0.2.2). Meanwhile the login screen
said "use http:// explicitly only for plaintext servers", Settings said "To use plaintext (e.g. a local
dev server), type the http:// prefix explicitly", and
normalizeBaseUrlhappily persisted it.So on a release build a self-hoster could follow our own instructions, save
http://my-server.local:8080,and get an opaque network error on every request forever.
Rather than opening cleartext up app-wide in release (Android has no dynamic per-domain allowlist, so that
would permit cleartext to any host for every user), the address is now validated where it is entered:
http://is refused, with a message that says whyAlso rejects a bare
https://, whichnormalizeBaseUrlwas turning intohttps://https:- which,delightfully, parses.
CI never ran the wear tests
tagged-release.ymlanddev-release.ymlboth ran./gradlew :app:test. Both workflows build and ship:wear:. So the one wear test we have has never run on a release, only on PRs. Both now run:app:test :wear:testand upload the wear reports.Verification
:app:assemblePlayRelease :wear:assembleRelease :app:testPlayReleaseUnitTest :wear:testPlayReleaseUnitTestgreen.and confirm the message; confirm an existing https config still works untouched.
Note for self-hosters
If anyone is already running behind a path prefix, they have been unable to use the Android app at all, so
there is no migration concern: it starts working, it does not change behaviour.