Branch: maintenance/dependency-audit
Date: 2026-06-15
Base: hawkff/NekoBoxForAndroid (fork of starifly/NekoBoxForAndroid, itself a fork of MatsuriDayo/NekoBoxForAndroid)
Scope: documentation only — no functional changes. This file identifies update targets and risky areas so that follow-up branches can be small and reversible.
"Latest safe target" cells marked
VERIFYcould not be confirmed from inside this environment (no package-index access at audit time). Confirm the newest stable release before bumping, then record the exact version in the update branch PR.
Source: nb4a.properties, buildSrc/src/main/kotlin/Helpers.kt
| Field | Value | Source |
|---|---|---|
| Package name | com.nb4a |
nb4a.properties:1 |
| Version name | 1.4.2-mod-12 |
nb4a.properties:2 |
| Pre-release version name | pre-1.4.2-20260214-1 |
nb4a.properties:3 |
| Version code (raw) | 46 |
nb4a.properties:4 |
| Version code (effective) | 230 (raw × 5) |
Helpers.kt:146 |
| Android namespace | io.nekohasekai.sagernet |
app/build.gradle.kts:30 |
| Debug app id suffix | .debug |
Helpers.kt:97 |
| ABI splits | armeabi-v7a, arm64-v8a, x86, x86_64 (no universal APK) | Helpers.kt:169-177 |
| Product flavors | oss, fdroid, play, preview | Helpers.kt:180-191 |
- Release keystore is
release.keystoreat repo root, configured only whenKEYSTORE_PASSis present (Helpers.kt:118-139). - Credentials come from
local.propertiesor env (KEYSTORE_PASS,ALIAS_NAME,ALIAS_PASS). - Both release and debug build types are signed with the release key when it exists (
Helpers.kt:133-138). - CI injects
local.propertiesvia base64LOCAL_PROPERTIESsecret and passes keystore secrets per build (.github/workflows/release.yml:64-68). - Caveat (inherited from starifly): package name
com.nb4a(upstream ismoe.nb4a) and a different signing key mean APKs from this fork will not update over upstream/other-fork installs.
Sources: gradle/wrapper/gradle-wrapper.properties, buildSrc/build.gradle.kts, build.gradle.kts, Helpers.kt, .github/workflows/*, buildScript/init/*
| Component | Current | Latest safe target | Risk | Branch |
|---|---|---|---|---|
| Gradle wrapper | 8.10.2 | VERIFY (8.10.x/8.11.x) | Low | update-build-toolchain |
| Android Gradle Plugin | 8.8.1 | VERIFY | Medium (AGP majors change DSL) | update-build-toolchain |
| Kotlin | 2.0.21 | VERIFY | Medium (KSP must match) | update-build-toolchain |
| KSP | 2.0.21-1.0.27 | VERIFY (must pair with Kotlin) | Medium | update-build-toolchain |
| compileSdk / targetSdk | 35 / 35 | 35 (or VERIFY 36) | Medium (targetSdk bump = behavior changes) | update-build-toolchain |
| minSdk | 21 | keep 21 | n/a | — |
| buildToolsVersion | 35.0.1 | VERIFY | Low | update-build-toolchain |
| NDK | 25.0.8775105 | VERIFY (pinned in CI + env) | Medium (core build) | update-build-toolchain / core branch |
| Java (source/target) | 1.8 | keep 1.8 (desugar enabled) | Low | — |
| Java (CI runner / Go build) | 17 implied via runners | VERIFY | Low | update-build-toolchain |
| core library desugaring | desugar_jdk_libs 2.0.3 | VERIFY | Low | update-build-toolchain |
NDK is pinned in three places that must stay in sync: .github/workflows/build.yml:63, .github/workflows/release.yml:65 (ndk/25.0.8775105) and buildScript/init/env_ndk.sh:13.
Source: app/build.gradle.kts:41-87
| Dependency | Current | Target | Reason | Risk |
|---|---|---|---|---|
| kotlinx-coroutines-android | 1.6.4 | VERIFY (1.8.x+) | Old; aligns with Kotlin 2.0 | Medium |
| androidx.core:core-ktx | 1.9.0 | VERIFY | Routine | Low |
| androidx.recyclerview | 1.3.0 | VERIFY | Routine | Low |
| androidx.activity:activity-ktx | 1.10.1 | VERIFY | Already fairly new | Low |
| androidx.fragment:fragment-ktx | 1.5.6 | VERIFY | Lags activity | Low |
| androidx.browser | 1.5.0 | VERIFY | Routine | Low |
| androidx.swiperefreshlayout | 1.1.0 | (latest stable) | Routine | Low |
| androidx.constraintlayout | 2.1.4 | VERIFY | Routine | Low |
| androidx.navigation:*-ktx | 2.5.3 | VERIFY | Routine | Low |
| androidx.preference:preference-ktx | 1.2.0 | VERIFY | Routine | Low |
| androidx.appcompat | 1.6.1 | VERIFY | Routine | Low |
| androidx.work:work-* | 2.8.1 | VERIFY | Routine | Low |
| com.google.android.material | 1.8.0 | VERIFY | Routine | Low |
| com.google.code.gson | 2.9.0 | 2.11.0 (VERIFY) | Maintenance/security | Low |
| zxing-lite | 2.1.1 | VERIFY | QR scanning | Low |
| blacksquircle editorkit/language-* | 2.6.0 | VERIFY | Config editor | Low |
| com.squareup.okhttp3:okhttp | 5.0.0-alpha.3 | 5.0.0 stable (VERIFY) | On an alpha; move to stable line | Medium |
| org.yaml:snakeyaml | 1.30 | 2.x (VERIFY) | Security (see below) | Medium |
| material-about-library | 3.2.0-rc01 | VERIFY | About screen | Low |
| process-phoenix | 2.1.2 | VERIFY | Process restart | Low |
| kryo | 5.2.1 | VERIFY (hold — profile serialization) | Do NOT bump casually; kryo wire format backs profile blobs | Medium |
| guava | 31.0.1-android | VERIFY | Routine | Low |
| ini4j | 0.5.4 | VERIFY | Config parse | Low |
| recyclerview-fastscroll | 2.0.1 | VERIFY | UI | Low |
| androidx.room:room-* | 2.6.1 | VERIFY (2.6.x) | DB; bump cautiously | Medium |
| Roomigrant (MatrixDev) | 0.3.4 | hold | Schema migration generator | Medium |
| desugar_jdk_libs | 2.0.3 | VERIFY | Desugaring | Low |
Notes:
kryoandRoomtouch persisted user data. Treat both as migration-sensitive, not routine bumps.- okhttp is on an alpha (
5.0.0-alpha.3); prefer moving to a stable 5.0.0 once verified, in its own commit, because the API surface changed across the 5.0 alphas.
Sources: libcore/go.mod, buildScript/lib/core/get_source.sh, buildScript/lib/core/get_source_env.sh, .github/workflows/*
The core is not a normal Go-module update. libcore/go.mod replaces sing-box and libneko with local checkouts, and get_source.sh clones pinned commits of starifly/sing-box and starifly/libneko.
go.mod replaces (libcore/go.mod:93-97):
github.com/matsuridayo/libneko => ../../libneko
github.com/sagernet/sing-box => ../../sing-box
github.com/sagernet/sing-vmess => github.com/starifly/sing-vmess v0.2.8-mod.1
| Component | Current | Target | Reason | Risk |
|---|---|---|---|---|
| starifly/sing-box commit | 4998428a136368500428a6a35cdd466a7042c75b |
VERIFY (commit with HY2 Gecko) | Required for Gecko obfs | High |
| starifly/libneko commit | 1c47a3af71990a7b2192e03292b4d246c308ef0b |
hold unless needed | Keep separate from sing-box bump | High |
| starifly/sing-vmess | v0.2.8-mod.1 |
hold | Fork pin | Medium |
| Go toolchain (go.mod) | go 1.24.7 / toolchain 1.24.9 | VERIFY | Mismatch: go.mod declares 1.24.x, CI uses ^1.25 (see next row) | Medium |
| Go toolchain (CI) | ^1.25 (build.yml:31, release.yml:33) |
align with go.mod | Mismatch: CI uses ^1.25, go.mod declares 1.24.x | Medium |
| sagernet/sing | v0.7.18 | follow sing-box | Transitive via fork | Medium |
| sagernet/quic-go | v0.52.0-sing-box-mod.3 | follow sing-box | QUIC/Hysteria path | High |
| sagernet/sing-quic | v0.5.2 | follow sing-box | Hysteria2 lives here | High |
| sagernet/sing-tun | v0.7.10 | follow sing-box | TUN | Medium |
| dyhkwong/sing-juicity | v0.0.3 | hold | Juicity protocol | Medium |
| reF1nd/sing-snell | v0.0.6 | hold | Snell protocol | Medium |
| anytls/sing-anytls | v0.0.11 | hold | AnyTLS | Medium |
| metacubex/mihomo | v1.19.13 | hold | Clash compat | Medium |
Gecko finding (confirmed): at the pinned commit
4998428…,option/hysteria2.godefinesHysteria2Obfswith onlyTypeandPasswordfields — nomin_packet_size/max_packet_size. Gecko obfs is therefore not supported by the current core. This validates the plan:core/singbox-gecko-basemust land (pin bump or backport) before any Android Gecko UI work.
app/build.gradle.kts:69—org.yaml:snakeyaml:1.30.app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt:255-259— uses defaultYaml().loadAs(text, Map::class.java)on untrusted remote subscription content (fetched atRawUpdater.kt:80).- The default constructor allows arbitrary Java type instantiation (CVE-2022-1471 class of gadget). The
Mapcast happens after dangerous construction, so it does not mitigate. - Fix direction (separate security branch): use
Yaml(SafeConstructor(LoaderOptions()))and/or upgrade to SnakeYAML 2.x with a safe loader. Not part of the toolchain branch.
- Keys:
Constants.kt:184-187. Storage:DataStore.kt:302-316—webdavPasswordstored as plain string inconfigurationStore(no EncryptedSharedPreferences/Keystore). - Basic auth sent at
BackupFragment.kt:237-239,267-269,287-289,361-363;WebDAVSettingsActivity.kt:142-144,180-182. http://accepted:BackupFragment.kt:212-213,342-343(scheme check accepts both);WebDAVSettingsActivity.kt:130usesURL(server)with no scheme restriction.- Risk: an
http://endpoint transmits Basic-auth creds and the full secrets-bearing backup in cleartext, with no warning. - Fix direction: warn/deny on non-TLS; consider encrypting stored creds.
ConfigBuilder.kt:143—bind = if (!forTest && DataStore.allowAccess) "0.0.0.0" else 127.0.0.1.- Inbound auth gate
DataStore.kt:155-160—mixedInboundNeedsAuthis false whenappendHttpProxyis active on Android Q+. - Risk:
allowAccess=true(bind0.0.0.0) andappendHttpProxyon Q+ ⇒ proxy exposed to LAN with no auth. The bind decision and auth decision are independent/uncoupled. - Fix direction: force auth whenever bind is
0.0.0.0, regardless ofappendHttpProxy. Note: starifly already added anappendHttpProxyconfirmation prompt; this is a separate, deeper coupling fix.
DataStore.kt:135-136:rulesGeositeUrldefault →…/sing-geoip/releases/latest/download/geoip.db(wrong; points at geoip)rulesGeoipUrldefault →…/sing-geosite/releases/latest/download/geosite.db(wrong; points at geosite)
- The two defaults are swapped; resetting to custom defaults would populate geosite with geoip data and vice-versa, breaking routing.
- Fix direction: swap the two default URLs back. Small, isolated fix — good candidate for its own tiny PR.
DataStore.kt:remoteDnshttps://dns.google/dns-query(:124),directDnshttps://223.5.5.5/dns-query(:125),enableDnsRouting=true(:126),enableFakeDns=true(:127),ipv6Mode=DISABLE(:177),bypass=true(:181),strictRoute=true(:188). Consumed inConfigBuilder.kt:144-153.
- Data model: single
HysteriaBean.obfuscationString (HysteriaBean.java:24); no obfs-type field. kryo serialization version is7(HysteriaBean.java:85) → bumping requires deserialize handling. - Emission point:
HysteriaFmt.kt:328-333hardcodestype = "salamander". - URI parse/build:
HysteriaFmt.kt:95-97(parseobfs-password),:159-162(emitobfs=salamander). - UI:
hysteria_preferences.xml:30-35+HysteriaSettingsActivity.kt— single password field, no type selector. - SingBoxOptions Java mirror:
SingBoxOptions.java:588-594Hysteria2Obfs{ type, password }— would needmin_packet_size/max_packet_sizeadded once core supports them. - Blocker: core pin lacks Gecko (see Core section). Do
core/singbox-gecko-basefirst.
- Existing:
MieruBean.java,MieruFmt.kt(buildMieruConfigemits standalone mieru JSON),MieruSettingsActivity.kt,mieru_preferences.xml. - Launch path: external plugin
mieru-plugin/ packagemoe.matsuri.exe.mieru(PluginEntry.kt:17-26). - Native goal = replace the external plugin with a bundled binary/sidecar reusing the existing
buildMieruConfigJSON. Bean fields today:protocol(TCP/UDP),username,password,mtu— fewer than the mbox outbound schema; extend as needed.
- Not present in the codebase; greenfield. Plan as a SOCKS5 sidecar (new
MasterDnsVpnBean+ config generator + process runner + packaged binaries).
maintenance/update-build-toolchain— Gradle/AGP/Kotlin/KSP/AndroidX/CI only. No core, no protocol, no DB/serialization. Also fix the CI/go.mod Go-version mismatch here (or in the core branch).fix/geo-fallback-urls— swap the two default URLs inDataStore.kt:135-136. Tiny, isolated.security/yaml-safe-load— SnakeYAML safe loader + version bump.security/webdav-tls-and-secrets— warn/deny http://, consider credential encryption.security/inbound-auth-coupling— require auth when binding 0.0.0.0.core/singbox-gecko-base— bump or backport sing-box for HY2 Gecko (blocks Gecko UI).feature/hy2-gecko-obfs— model/parse/export/UI/config-builder (depends on #6).feature/mieru-native-sidecar— bundle mieru binary, drop external-plugin requirement.feature/masterdnsvpn-client-sidecar— new SOCKS5 sidecar client.
- No functional app changes (docs-only).
- Update targets identified; risky areas flagged.
- Core-source pins documented separately from Gradle/Kotlin/app deps.
- Gecko core gap confirmed against the actual pinned commit.
- CI still builds (run after pushing; this branch only adds a markdown file).
- Exact latest-stable versions for every
VERIFYcell (no index access at audit time). - Whether targetSdk 36 is required by any store deadline.
- The specific starifly/sing-box commit (or upstream sing-box ≥ 1.14.0) that introduces HY2 Gecko
min_packet_size/max_packet_size. - Resolve CI Go
^1.25vsgo.modgo 1.24.7mismatch.