fix(mobile): allow self-hosted plain-HTTP servers on Android release builds by premsai2030 · Pull Request #15559 · toeverything/AFFiNE · GitHub
Skip to content

fix(mobile): allow self-hosted plain-HTTP servers on Android release builds - #15559

Draft
premsai2030 wants to merge 1 commit into
toeverything:canaryfrom
premsai2030:fix(mobile)--allow-self-hosted-plain-HTTP-servers-on-Android-release-builds
Draft

fix(mobile): allow self-hosted plain-HTTP servers on Android release builds#15559
premsai2030 wants to merge 1 commit into
toeverything:canaryfrom
premsai2030:fix(mobile)--allow-self-hosted-plain-HTTP-servers-on-Android-release-builds

Conversation

@premsai2030

@premsai2030 premsai2030 commented Sep 2, 2026

Copy link
Copy Markdown

PR #15118 tightened release builds to block cleartext traffic and mixed content, which broke connecting to self-hosted instances served over plain HTTP (#15438): the WebView fetch to /graphql fails and the app shows a generic "Network error", while browsers load the same server fine.

Restore the behavior established by #13279 and #13435:

  • manifest: always allow cleartext traffic
  • WebView: always allow mixed content; the only page loaded is the bundled local app, and every other request targets the user-configured server, which may be plain HTTP for self-hosted instances

Fixes #15438

Summary by CodeRabbit

  • Changes
    • Android app now allows cleartext network traffic in all build variants.
    • Web content can load mixed HTTP and HTTPS resources, including in release builds.

…builds

PR toeverything#15118 tightened release builds to block cleartext traffic and mixed
content, which broke connecting to self-hosted instances served over
plain HTTP (toeverything#15438): the WebView fetch to
<server>/graphql fails and the app shows a generic "Network error",
while browsers load the same server fine.

Restore the behavior established by toeverything#13279 and toeverything#13435:
- manifest: always allow cleartext traffic
- WebView: always allow mixed content; the only page loaded is the
  bundled local app, and every other request targets the
  user-configured server, which may be plain HTTP for self-hosted
  instances

Fixes toeverything#15438
@coderabbitai

coderabbitai Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

@premsai2030
premsai2030 marked this pull request as ready for review September 2, 2026 18:44

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: f9f1aa16-eab0-4306-b4d8-a6681ab0f3ac

📥 Commits

Reviewing files that changed from the base of the PR and between f78e46f and dee4549.

📒 Files selected for processing (3)
  • packages/frontend/apps/android/App/app/build.gradle
  • packages/frontend/apps/android/App/app/src/main/AndroidManifest.xml
  • packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/MainActivity.kt
💤 Files with no reviewable changes (1)
  • packages/frontend/apps/android/App/app/build.gradle

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.

android:hardwareAccelerated="true"
android:supportsRtl="true"
android:usesCleartextTraffic="${usesCleartextTraffic}"
android:usesCleartextTraffic="true"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

app_root='packages/frontend/apps/android/App'
rg -n -C 5 \
  'usesCleartextTraffic|networkSecurityConfig|targetSdk|http://|https://|serverUrl\(|eventSource\(|Request\.Builder|Authorization|Cookie|token|session' \
  "$app_root"

Repository: toeverything/AFFiNE

Length of output: 50376


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo '== repository conventions and learnings =='
find /tmp/coderabbit-repo-knowledge/toeverything-affine-a85df9b5 \
  -maxdepth 2 -type f -name '*.md' -print

echo '== manifest and Android configuration =='
cat -n packages/frontend/apps/android/App/app/src/main/AndroidManifest.xml
sed -n '1,60p' packages/frontend/apps/android/App/app/build.gradle
sed -n '1,20p' packages/frontend/apps/android/App/gradle/libs.versions.toml

echo '== direct service definitions =='
rg -l --glob '*.kt' 'class[[:space:]]+(GraphQLService|SSEService)|interface[[:space:]]+(GraphQLService|SSEService)' \
  packages/frontend/apps/android/App/app/src/main/java

Repository: toeverything/AFFiNE

Length of output: 8119


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo '== applicable repository guidance =='
cat /tmp/coderabbit-repo-knowledge/toeverything-affine-a85df9b5/architecture/android-client.md
cat /tmp/coderabbit-repo-knowledge/toeverything-affine-a85df9b5/architecture/repo-guidelines.md
cat /tmp/coderabbit-repo-knowledge/toeverything-affine-a85df9b5/architecture/service-communication.md

echo '== service structure =='
ast-grep outline packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/service/GraphQLService.kt
ast-grep outline packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/service/SSEService.kt

echo '== service implementations =='
cat -n packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/service/GraphQLService.kt
cat -n packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/service/SSEService.kt

Repository: toeverything/AFFiNE

Length of output: 15337


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo '== shared HTTP client and server URL source =='
rg -l --glob '*.kt' \
  'object[[:space:]]+OkHttp|class[[:space:]]+OkHttp|getCurrentServerBaseUrl|fun[[:space:]]+getCurrentServerBaseUrl' \
  packages/frontend/apps/android/App/app/src/main/java

rg -n -C 8 --glob '*.kt' \
  'object[[:space:]]+OkHttp|class[[:space:]]+OkHttp|getCurrentServerBaseUrl|Authorization|Cookie|Bearer|accessToken|refreshToken' \
  packages/frontend/apps/android/App/app/src/main/java/app/affine/pro

Repository: toeverything/AFFiNE

Length of output: 50375


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo '== shared client and cookie policy =='
sed -n '21,120p' packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/service/OkHttp.kt

echo '== authentication requests using the configured endpoint =='
sed -n '320,375p' packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/plugin/AuthPlugin.kt

echo '== endpoint configuration bridge =='
rg -n -C 6 --glob '*.ts' --glob '*.tsx' --glob '*.kt' \
  'getCurrentServerBaseUrl|serverBaseUrl|self-hosted|selfHosted|serverUrl' \
  packages/frontend/apps/android/App packages/frontend/core

Repository: toeverything/AFFiNE

Length of output: 50375


Security Misconfiguration (CWE-319): Cleartext Transmission of Sensitive Information

Reachability: External · Exploitability: Moderate

Do not enable cleartext traffic application-wide.

usesCleartextTraffic="true" permits cleartext connections from every app component. GraphQLService and SSEService use the configured server with the shared OkHttp.client, which loads stored authentication cookies by host. Keep HTTPS as the default, and enforce a host-scoped HTTP opt-in with a warning before sending credentials or session cookies over HTTP.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

app_root='packages/frontend/apps/android/App'
rg -n -C 6 \
  'MIXED_CONTENT_|loadUrl\(|loadData|shouldInterceptRequest|shouldOverrideUrlLoading|http://|<script|<iframe|fetch\(|XMLHttpRequest|addJavascriptInterface|evaluateJavascript' \
  "$app_root"

Repository: toeverything/AFFiNE

Length of output: 41735


🏁 Script executed:

set -euo pipefail
knowledge_root=/tmp/coderabbit-repo-knowledge/toeverything-affine-a85df9b5
printf '%s\n' '--- knowledge files ---'
find "$knowledge_root" -maxdepth 2 -type f -name '*.md' -print
printf '%s\n' '--- MainActivity outline ---'
ast-grep outline packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/MainActivity.kt
printf '%s\n' '--- MainActivity relevant source ---'
sed -n '1,190p' packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/MainActivity.kt
printf '%s\n' '--- WebView and client definitions ---'
sed -n '1,180p' packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/AffineWebViewClient.kt
sed -n '1,180p' packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/AffineEditorWebView.kt
printf '%s\n' '--- bounded WebView/server references ---'
rg -n -C 5 'serverBaseUrl|currentServer|loadUrl|setWebViewClient|AffineWebViewClient|trustedOrigin|shouldInterceptRequest|MIXED_CONTENT|eventSource|serverUrl' \
  packages/frontend/apps/android/App/app/src/main/java/app/affine/pro \
  packages/frontend/apps/android/App/app/src/main/java/app/affine/pro/service

Repository: toeverything/AFFiNE

Length of output: 32383


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- Android architecture and repository guidance ---'
sed -n '1,220p' /tmp/coderabbit-repo-knowledge/toeverything-affine-a85df9b5/architecture/android-client.md
sed -n '1,180p' /tmp/coderabbit-repo-knowledge/toeverything-affine-a85df9b5/architecture/service-communication.md
sed -n '1,160p' /tmp/coderabbit-repo-knowledge/toeverything-affine-a85df9b5/architecture/repo-guidelines.md
printf '%s\n' '--- direct server URL callers and helpers ---'
rg -n -C 8 'getCurrentServerBaseUrl|class WebService|fun update\(|serverBaseUrl|currentServerBaseUrl|localUrl' \
  packages/frontend/apps/android/App/app/src/main/java
printf '%s\n' '--- Android bridge/plugin URL and WebView operations ---'
rg -n -C 8 'loadUrl|loadData|evaluateJavascript|addJavascriptInterface|removeJavascriptInterface|WebViewClient|shouldInterceptRequest|shouldOverrideUrlLoading|urlOverride|server' \
  packages/frontend/apps/android/App/app/src/main/java/app/affine/pro
printf '%s\n' '--- Android app assets and frontend configuration references ---'
rg -n -C 5 'serverUrl|server_url|serverBaseUrl|self-hosted|api/copilot|/graphql|EventSource|fetch\\(|XMLHttpRequest|http://' \
  packages/frontend/apps/android/App/app/src/main/assets packages/frontend/apps/android/App/app/src/main/java 2>/dev/null || true

Repository: toeverything/AFFiNE

Length of output: 48566


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- tracked Android app files relevant to WebView loading ---'
git ls-files packages/frontend/apps/android/App | rg '(^|/)(capacitor|assets|public|www|WebExt|AuthInitializer|MainActivity|AffineWebViewClient|AffineEditorWebView|build\.gradle|AndroidManifest)'
printf '%s\n' '--- all definitions and callers of getCurrentServerBaseUrl ---'
rg -n -C 6 'getCurrentServerBaseUrl' packages/frontend --glob '!**/node_modules/**' --glob '!**/dist/**'
printf '%s\n' '--- Capacitor configuration ---'
fd -i 'capacitor*' packages/frontend --type f --exclude node_modules --exclude dist --exec sh -c 'echo "--- $1"; sed -n "1,220p" "$1"' sh {}
printf '%s\n' '--- mobile server request patterns ---'
rg -n -C 4 'getCurrentServerBaseUrl|currentServerBaseUrl|serverBaseUrl|EventSource|fetch\\(|XMLHttpRequest|/graphql' \
  packages/frontend/core packages/frontend/apps packages/frontend/mobile \
  --glob '!**/node_modules/**' --glob '!**/dist/**' 2>/dev/null | head -n 500

Repository: toeverything/AFFiNE

Length of output: 26996


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- Android frontend native bridge URL function ---'
sed -n '210,275p' packages/frontend/apps/android/src/app.tsx
printf '%s\n' '--- Android runtime configuration and generated web bundle inputs ---'
rg -n -C 8 'CAP_SERVER_URL|server:|cleartext|webDir|baseUrl|endpoint|DefaultServerService|ServersService' \
  packages/frontend/apps/android packages/frontend/core \
  --glob '!**/node_modules/**' --glob '!**/dist/**' | head -n 600
printf '%s\n' '--- frontend HTTP client and endpoint construction ---'
rg -n -C 6 'fetch\\(|axios|XMLHttpRequest|graphql|baseUrl|endpoint.*url|url.*endpoint|server.*baseUrl' \
  packages/frontend/core packages/frontend/apps/android/src \
  --glob '!**/node_modules/**' --glob '!**/dist/**' | head -n 600

Repository: toeverything/AFFiNE

Length of output: 50375


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- cloud service definitions ---'
rg -l 'class (DefaultServerService|ServersService|ServerService)|DefaultServerService|ServersService' packages/frontend/core/src/modules/cloud packages/frontend/core/src | head -n 80
printf '%s\n' '--- cloud service outlines ---'
for f in $(rg -l 'class (DefaultServerService|ServersService|ServerService)' packages/frontend/core/src/modules/cloud packages/frontend/core/src | head -n 20); do
  echo "--- $f"
  ast-grep outline "$f"
done
printf '%s\n' '--- endpoint request implementations ---'
rg -l 'fetch\\(|graphql-request|ApolloClient|createClient|WebSocket|EventSource' packages/frontend/core/src/modules/cloud packages/frontend/core/src | head -n 80
printf '%s\n' '--- Android build and manifest network settings ---'
sed -n '1,220p' packages/frontend/apps/android/App/app/build.gradle
sed -n '1,180p' packages/frontend/apps/android/App/app/src/main/AndroidManifest.xml

Repository: toeverything/AFFiNE

Length of output: 6547


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- server selection and base URL definitions ---'
sed -n '1,220p' packages/frontend/core/src/modules/cloud/services/server.ts
sed -n '1,240p' packages/frontend/core/src/modules/cloud/services/default-server.ts
sed -n '1,280p' packages/frontend/core/src/modules/cloud/services/servers.ts
printf '%s\n' '--- cloud request clients ---'
rg -n -C 5 'fetch\\(|ApolloClient|graphql-request|EventSource|WebSocket|baseUrl' \
  packages/frontend/core/src/modules/cloud \
  packages/frontend/core/src/modules \
  --glob '!**/*.spec.*' | head -n 500
printf '%s\n' '--- Android frontend entry and build output configuration ---'
sed -n '1,120p' packages/frontend/apps/android/src/app.tsx
sed -n '1,100p' packages/frontend/apps/android/package.json

Repository: toeverything/AFFiNE

Length of output: 4720


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- cloud module files and URL-bearing code ---'
git ls-files packages/frontend/core/src/modules/cloud | sort
rg -n -C 5 'baseUrl|fetch\\(|XMLHttpRequest|graphql|subscribe|WebSocket|EventSource|socket' \
  packages/frontend/core/src/modules/cloud \
  --glob '!**/*.spec.*'
printf '%s\n' '--- server entity and scope ---'
rg -l 'class Server|interface ServerScope|serverScope' packages/frontend/core/src/modules/cloud
for f in $(rg -l 'class Server|interface ServerScope|serverScope' packages/frontend/core/src/modules/cloud | head -n 12); do
  echo "--- $f"
  sed -n '1,260p' "$f"
done
printf '%s\n' '--- frontend request primitives used by Android ---'
rg -n -C 4 'fetch\\(|graphql|WebSocket|EventSource' \
  packages/frontend/apps/android/src packages/frontend/core/src \
  --glob '!**/*.spec.*' --glob '!**/generated/**' \
  | head -n 500

Repository: toeverything/AFFiNE

Length of output: 5318


🏁 Script executed:

set -euo pipefail
for f in \
  packages/frontend/core/src/modules/cloud/entities/server.ts \
  packages/frontend/core/src/modules/cloud/scopes/server.ts \
  packages/frontend/core/src/modules/cloud/services/fetch.ts \
  packages/frontend/core/src/modules/cloud/services/graphql.ts \
  packages/frontend/core/src/modules/cloud/services/eventsource.ts \
  packages/frontend/core/src/modules/cloud/services/realtime.ts \
  packages/frontend/core/src/modules/cloud/realtime/live-query.ts; do
  echo "--- $f"
  sed -n '1,260p' "$f"
done
printf '%s\n' '--- direct request call sites ---'
rg -n -C 4 'FetchService|GraphQLService|EventSourceService|fetch\\(|new EventSource|WebSocket|baseUrl' \
  packages/frontend/core/src/modules/cloud \
  packages/frontend/apps/android/src \
  --glob '!**/*.spec.*' | head -n 700

Repository: toeverything/AFFiNE

Length of output: 16933


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- WebView-side HTTP and SSE call sites ---'
rg -n -C 5 'eventSource\\(|\.fetch\(|fetch\\(' \
  packages/frontend/core/src packages/frontend/apps/android/src \
  --glob '!**/*.spec.*' --glob '!**/generated/**' \
  | head -n 900
printf '%s\n' '--- response consumers that could create executable content ---'
rg -n -C 6 'text\\(\\)|blob\\(\\)|arrayBuffer\\(\\)|json\\(\\)|innerHTML|outerHTML|dangerouslySetInnerHTML|srcDoc|<iframe|<script|URL\\.createObjectURL' \
  packages/frontend/core/src packages/frontend/apps/android/src \
  --glob '!**/*.spec.*' --glob '!**/generated/**' \
  | head -n 900

Repository: toeverything/AFFiNE

Length of output: 311


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- WebView-side HTTP and SSE call sites ---'
rg -n -C 5 -F \
  -e 'eventSource(' \
  -e '.fetch(' \
  -e 'fetch(' \
  packages/frontend/core/src packages/frontend/apps/android/src \
  --glob '!**/*.spec.*' --glob '!**/generated/**' \
  | head -n 900
printf '%s\n' '--- response consumers that could create executable content ---'
rg -n -C 6 -F \
  -e '.text()' \
  -e '.blob()' \
  -e '.arrayBuffer()' \
  -e '.json()' \
  -e 'innerHTML' \
  -e 'outerHTML' \
  -e 'dangerouslySetInnerHTML' \
  -e 'srcDoc' \
  -e '<iframe' \
  -e '<script' \
  -e 'URL.createObjectURL' \
  packages/frontend/core/src packages/frontend/apps/android/src \
  --glob '!**/*.spec.*' --glob '!**/generated/**' \
  | head -n 900

Repository: toeverything/AFFiNE

Length of output: 50376


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- HTML preview implementation ---'
sed -n '1,230p' packages/frontend/core/src/blocksuite/view-extensions/code-block-preview/html-preview.ts
printf '%s\n' '--- HTML preview content and isolation controls ---'
rg -n -C 8 'HtmlPreview|html-preview|sandbox|srcdoc|contentWindow|document\\.write|innerHTML|setAttribute\\(' \
  packages/frontend/core/src/blocksuite/view-extensions/code-block-preview \
  packages/frontend/core/src \
  --glob '!**/*.spec.*' | head -n 700

Repository: toeverything/AFFiNE

Length of output: 4564


🏁 Script executed:

set -euo pipefail
printf '%s\n' '--- iframe linking implementation ---'
sed -n '1,260p' packages/frontend/core/src/blocksuite/view-extensions/code-block-preview/iframe-container.ts
printf '%s\n' '--- iframe-container references ---'
rg -n -C 8 -F \
  -e 'linkIframe(' \
  -e 'sandbox' \
  -e 'srcdoc' \
  -e 'contentWindow' \
  -e 'document.write' \
  packages/frontend/core/src/blocksuite/view-extensions/code-block-preview

Repository: toeverything/AFFiNE

Length of output: 4725


Security Misconfiguration (CWE-16)

Reachability: External · Exploitability: Moderate

Keep mixed content blocked for the WebView.

MIXED_CONTENT_ALWAYS_ALLOW also permits HTTP subresources, including scripts referenced by HTML rendered in the sandboxed preview iframe. If HTTP self-hosted servers remain supported, use native clients for API and SSE traffic, or enforce an origin and resource-type allowlist that prevents HTTP responses from becoming executable content.

@premsai2030
premsai2030 marked this pull request as draft September 2, 2026 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[Bug] Android app unable to connect to self-hosted instance

1 participant