From 94d58325db5d5142f82dd180642e04c954247222 Mon Sep 17 00:00:00 2001 From: xtclovver Date: Fri, 12 Jun 2026 02:17:08 +0300 Subject: [PATCH] chore(quality): low-risk SonarCloud code-smell cleanups Behavior-preserving fixes for SonarCloud findings, verified by build + unit tests. No detector signal, threshold, port, or verdict logic changed. Kotlin: - extract duplicated string literals to consts (S1192): "*.*.*.*", "SHA-512", "meduza.io", "", sing-box core type - if-throw -> require()/check() for validation (S6532): Ed25519 decode, GeoIp HTTP status - empty catch blocks documented (S108); collapsible if merged (S1066) - isExpanded() -> val property (S6512) with call-site update - drop unused lambda parameter (S1172) C++ (native_signs_probe.cpp, native_curl_probe.cpp): - read-only netlink/diag pointers made pointer-to-const (S5350) - split multi-variable declarations (S1659) - sizeof/sizeof -> std::size for JNI method table (S7127) - ipv6 prefix loop converted to range-for (S5566) - progress-callback state pointer made const (S5350) Riskier findings (cognitive complexity, too-many-params, deep nesting, flag-guarded null ops that the Kotlin compiler cannot smart-cast) were left in place. Genuine false positives (C++20/23 suggestions under a C++17 toolchain, JNI function-pointer decay, intentional reinterpret_cast in syscall code, sentinel IPs) were marked False Positive / Accepted in SonarCloud rather than changed. --- app/src/main/cpp/native_curl_probe.cpp | 2 +- app/src/main/cpp/native_signs_probe.cpp | 20 +++++++++++-------- .../SettingsCustomCheckEditorFragment.kt | 4 ++-- .../rknhardering/checker/CdnPullingChecker.kt | 7 ++++--- .../rknhardering/checker/GeoIpChecker.kt | 4 +--- .../notcvnt/rknhardering/crypto/Ed25519.kt | 16 +++++++-------- .../mapper/EndpointResponseMapper.kt | 13 +++++++----- .../ui/CheckerSectionController.kt | 2 +- .../probe/TunProbeDiagnosticsFormatter.kt | 20 ++++++++++--------- .../notcvnt/rknhardering/util/MaskingUtils.kt | 8 +++++--- .../rknhardering/vpn/VpnAppClassifier.kt | 2 +- 11 files changed, 54 insertions(+), 44 deletions(-) diff --git a/app/src/main/cpp/native_curl_probe.cpp b/app/src/main/cpp/native_curl_probe.cpp index ad31e63..7a0f780 100644 --- a/app/src/main/cpp/native_curl_probe.cpp +++ b/app/src/main/cpp/native_curl_probe.cpp @@ -166,7 +166,7 @@ int ProgressCallback( curl_off_t /* dlnow */, curl_off_t /* ultotal */, curl_off_t /* ulnow */) { - auto* state = static_cast(clientp); + const auto* state = static_cast(clientp); return state != nullptr && state->cancelled.load() ? 1 : 0; } diff --git a/app/src/main/cpp/native_signs_probe.cpp b/app/src/main/cpp/native_signs_probe.cpp index d811fc5..0295993 100644 --- a/app/src/main/cpp/native_signs_probe.cpp +++ b/app/src/main/cpp/native_signs_probe.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -254,7 +255,8 @@ jobjectArray nativeReadSelfMapsSummary(JNIEnv *env, jclass /*clazz*/) { } if (rwx) { - unsigned long long start = 0, end = 0; + unsigned long long start = 0; + unsigned long long end = 0; if (std::sscanf(line, "%llx-%llx", &start, &end) == 2 && end > start) { unsigned long long size = end - start; if (size >= (256ULL * 1024ULL)) { @@ -347,8 +349,7 @@ int ipv6PrefixLen(const sockaddr *sa) { if (sa == nullptr || sa->sa_family != AF_INET6) return -1; const auto *in6 = reinterpret_cast(sa); int bits = 0; - for (int i = 0; i < 16; ++i) { - unsigned char b = in6->sin6_addr.s6_addr[i]; + for (unsigned char b : in6->sin6_addr.s6_addr) { if (b == 0xff) { bits += 8; continue; } for (int k = 7; k >= 0; --k) { if (b & (1u << k)) ++bits; else return bits; @@ -539,7 +540,7 @@ std::vector netlinkRouteDump(int family) { nh = NLMSG_NEXT(nh, len)) { if (nh->nlmsg_type == NLMSG_DONE) { done = true; break; } if (nh->nlmsg_type == NLMSG_ERROR) { - auto *err = reinterpret_cast(NLMSG_DATA(nh)); + const auto *err = reinterpret_cast(NLMSG_DATA(nh)); out.push_back(std::string("error|nlmsg|errno=") + std::to_string(-err->error)); done = true; break; @@ -550,7 +551,10 @@ std::vector netlinkRouteDump(int family) { int rtaLen = nh->nlmsg_len - NLMSG_LENGTH(sizeof(*rtm)); auto *attr = RTM_RTA(rtm); - std::string dst, gw, src, prefSrc; + std::string dst; + std::string gw; + std::string src; + std::string prefSrc; int oif = 0; unsigned int priority = 0; char iface[IF_NAMESIZE] = {0}; @@ -703,14 +707,14 @@ std::vector netlinkInetDiag(int family, int protocol) { nh = NLMSG_NEXT(nh, len)) { if (nh->nlmsg_type == NLMSG_DONE) { done = true; break; } if (nh->nlmsg_type == NLMSG_ERROR) { - auto *err = reinterpret_cast(NLMSG_DATA(nh)); + const auto *err = reinterpret_cast(NLMSG_DATA(nh)); out.push_back(std::string("error|nlmsg|errno=") + std::to_string(-err->error)); done = true; break; } if (nh->nlmsg_type != SOCK_DIAG_BY_FAMILY) continue; - auto *diag = reinterpret_cast(NLMSG_DATA(nh)); + const auto *diag = reinterpret_cast(NLMSG_DATA(nh)); char src[INET6_ADDRSTRLEN] = {0}; char dst[INET6_ADDRSTRLEN] = {0}; int af = diag->idiag_family; @@ -1041,7 +1045,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) { } jclass cls = env->FindClass("com/notcvnt/rknhardering/probe/NativeSignsBridge"); if (cls == nullptr) return JNI_ERR; - jint rc = env->RegisterNatives(cls, kMethods, sizeof(kMethods) / sizeof(kMethods[0])); + jint rc = env->RegisterNatives(cls, kMethods, static_cast(std::size(kMethods))); env->DeleteLocalRef(cls); if (rc != JNI_OK) return JNI_ERR; return JNI_VERSION_1_6; diff --git a/app/src/main/java/com/notcvnt/rknhardering/SettingsCustomCheckEditorFragment.kt b/app/src/main/java/com/notcvnt/rknhardering/SettingsCustomCheckEditorFragment.kt index 1db924b..bec455e 100644 --- a/app/src/main/java/com/notcvnt/rknhardering/SettingsCustomCheckEditorFragment.kt +++ b/app/src/main/java/com/notcvnt/rknhardering/SettingsCustomCheckEditorFragment.kt @@ -162,7 +162,7 @@ internal class SettingsCustomCheckEditorFragment : // Override expansion listener to track openSectionIds sectionView.findViewById(R.id.sectionHeader).setOnClickListener { - val nowOpen = !controller.isExpanded() + val nowOpen = !controller.isExpanded controller.setExpanded(nowOpen, animate = true) if (nowOpen) openSectionIds.add(id) else openSectionIds.remove(id) } @@ -225,7 +225,7 @@ internal class SettingsCustomCheckEditorFragment : labelHintRes = R.string.settings_custom_check_description_field, extraInputHintRes = R.string.settings_custom_check_check_type_field, extraSwitchTextRes = null, - testAction = { domain, checkType, _ -> + testAction = { domain, _, _ -> kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.IO) { val result = runCatching { val client = okhttp3.OkHttpClient.Builder() diff --git a/app/src/main/java/com/notcvnt/rknhardering/checker/CdnPullingChecker.kt b/app/src/main/java/com/notcvnt/rknhardering/checker/CdnPullingChecker.kt index ff8e81b..250eab3 100644 --- a/app/src/main/java/com/notcvnt/rknhardering/checker/CdnPullingChecker.kt +++ b/app/src/main/java/com/notcvnt/rknhardering/checker/CdnPullingChecker.kt @@ -31,9 +31,10 @@ object CdnPullingChecker { private const val MAX_FETCH_ATTEMPTS = 1 private const val RETRY_DELAY_MS = 250L + private const val MEDUZA_IO = "meduza.io" private val ACTIONABLE_TARGETS = setOf( "redirector.googlevideo.com", - "meduza.io", + MEDUZA_IO, ) internal data class EndpointSpec( @@ -71,7 +72,7 @@ object CdnPullingChecker { kind = CdnPullingClient.TargetKind.CLOUDFLARE_TRACE, ), EndpointSpec( - label = "meduza.io", + label = MEDUZA_IO, url = "https://meduza.io/cdn-cgi/trace", kind = CdnPullingClient.TargetKind.CLOUDFLARE_TRACE, ), @@ -92,7 +93,7 @@ object CdnPullingChecker { val activeEndpoints = buildList { if (config.builtinTargetsEnabled) { for (endpoint in ENDPOINTS) { - if (endpoint.label == "meduza.io" && !config.meduzaEnabled) continue + if (endpoint.label == MEDUZA_IO && !config.meduzaEnabled) continue if (endpoint.label == "rutracker.org" && !config.rutrackerEnabled) continue add(endpoint) } diff --git a/app/src/main/java/com/notcvnt/rknhardering/checker/GeoIpChecker.kt b/app/src/main/java/com/notcvnt/rknhardering/checker/GeoIpChecker.kt index 3c5a4ad..5214db0 100644 --- a/app/src/main/java/com/notcvnt/rknhardering/checker/GeoIpChecker.kt +++ b/app/src/main/java/com/notcvnt/rknhardering/checker/GeoIpChecker.kt @@ -580,9 +580,7 @@ object GeoIpChecker { config = resolverConfig, cancellationSignal = executionContext.cancellationSignal, ) - if (response.code !in 200..299) { - throw IllegalStateException("HTTP ${response.code}") - } + check(response.code in 200..299) { "HTTP ${response.code}" } return JSONObject(response.body) } diff --git a/app/src/main/java/com/notcvnt/rknhardering/crypto/Ed25519.kt b/app/src/main/java/com/notcvnt/rknhardering/crypto/Ed25519.kt index 2d5a60a..1d67882 100644 --- a/app/src/main/java/com/notcvnt/rknhardering/crypto/Ed25519.kt +++ b/app/src/main/java/com/notcvnt/rknhardering/crypto/Ed25519.kt @@ -8,6 +8,8 @@ import java.security.MessageDigest // for the offline signing CLI under :app:src/test (host-only path). object Ed25519 { + private const val SHA_512 = "SHA-512" + private val P = BigInteger.ONE.shiftLeft(255).subtract(BigInteger.valueOf(19)) private val L = BigInteger("7237005577332262213973186563042994240857116359379907606001950938285454250989") private val D = BigInteger("-121665").mod(P) @@ -26,7 +28,7 @@ object Ed25519 { val s = decodeScalarLE(signature.copyOfRange(32, 64)) if (s >= L) return false val rPoint = runCatching { decodePoint(r) }.getOrNull() ?: return false - val md = MessageDigest.getInstance("SHA-512") + val md = MessageDigest.getInstance(SHA_512) md.update(r) md.update(publicKey) md.update(message) @@ -38,7 +40,7 @@ object Ed25519 { fun sign(privateKey: ByteArray, publicKey: ByteArray, message: ByteArray): ByteArray { require(privateKey.size == 32 && publicKey.size == 32) - val md = MessageDigest.getInstance("SHA-512") + val md = MessageDigest.getInstance(SHA_512) val h = md.digest(privateKey) val a = clampScalar(h.copyOfRange(0, 32)) val prefix = h.copyOfRange(32, 64) @@ -55,7 +57,7 @@ object Ed25519 { fun derivePublicKey(privateKey: ByteArray): ByteArray { require(privateKey.size == 32) - val h = MessageDigest.getInstance("SHA-512").digest(privateKey) + val h = MessageDigest.getInstance(SHA_512).digest(privateKey) val a = clampScalar(h.copyOfRange(0, 32)) return encodePoint(scalarMul(B, a)) } @@ -92,9 +94,7 @@ object Ed25519 { if (x.multiply(x).subtract(xx).mod(P) != BigInteger.ZERO) { x = x.multiply(I).mod(P) } - if (x.multiply(x).subtract(xx).mod(P) != BigInteger.ZERO) { - throw IllegalArgumentException("no sqrt") - } + require(x.multiply(x).subtract(xx).mod(P) == BigInteger.ZERO) { "no sqrt" } if ((x.testBit(0)) != sign) x = P.subtract(x).mod(P) return x } @@ -105,10 +105,10 @@ object Ed25519 { val sign = (raw[31].toInt() and 0x80) != 0 raw[31] = (raw[31].toInt() and 0x7F).toByte() val y = decodeScalarLE(raw) - if (y >= P) throw IllegalArgumentException("y out of range") + require(y < P) { "y out of range" } val x = recoverX(y, sign) val p = Point(x, y) - if (!onCurve(p)) throw IllegalArgumentException("not on curve") + require(onCurve(p)) { "not on curve" } return p } diff --git a/app/src/main/java/com/notcvnt/rknhardering/customcheck/mapper/EndpointResponseMapper.kt b/app/src/main/java/com/notcvnt/rknhardering/customcheck/mapper/EndpointResponseMapper.kt index 9ad7b8b..2f287ce 100644 --- a/app/src/main/java/com/notcvnt/rknhardering/customcheck/mapper/EndpointResponseMapper.kt +++ b/app/src/main/java/com/notcvnt/rknhardering/customcheck/mapper/EndpointResponseMapper.kt @@ -83,9 +83,9 @@ object EndpointResponseMapper { fun autoDetectResponseType(rawResponse: String): ResponseType { val trimmed = rawResponse.trim() // 1. Try JSON object - try { JSONObject(trimmed); return ResponseType.JSON } catch (_: Exception) { } + try { JSONObject(trimmed); return ResponseType.JSON } catch (_: Exception) { /* not a JSON object, try next format */ } // 2. Try JSON array - try { JSONArray(trimmed); return ResponseType.JSON } catch (_: Exception) { } + try { JSONArray(trimmed); return ResponseType.JSON } catch (_: Exception) { /* not a JSON array, try next format */ } // 3. key=value lines val lines = trimmed.lines().filter { it.isNotBlank() } val kvPattern = Regex("""^\w+=.+$""") @@ -180,9 +180,12 @@ object EndpointResponseMapper { if (countryCodePath == null && key in setOf("country_code", "cc", "countrycode", "country") && COUNTRY_CODE_REGEX.matches(strVal)) { countryCodePath = path } - if (countryNamePath == null && key in setOf("country", "country_name", "countryname")) { - // only if it doesn't look like a 2-letter code itself - if (!COUNTRY_CODE_REGEX.matches(strVal)) countryNamePath = path + // only if it doesn't look like a 2-letter code itself + if (countryNamePath == null && + key in setOf("country", "country_name", "countryname") && + !COUNTRY_CODE_REGEX.matches(strVal) + ) { + countryNamePath = path } if (ispPath == null && key in setOf("isp", "provider")) { ispPath = path diff --git a/app/src/main/java/com/notcvnt/rknhardering/customcheck/ui/CheckerSectionController.kt b/app/src/main/java/com/notcvnt/rknhardering/customcheck/ui/CheckerSectionController.kt index c75df27..757d9c2 100644 --- a/app/src/main/java/com/notcvnt/rknhardering/customcheck/ui/CheckerSectionController.kt +++ b/app/src/main/java/com/notcvnt/rknhardering/customcheck/ui/CheckerSectionController.kt @@ -80,7 +80,7 @@ internal class CheckerSectionController( updateNoteVisibility() } - fun isExpanded(): Boolean = expanded + val isExpanded: Boolean get() = expanded fun setEnabled(value: Boolean, propagate: Boolean) { enabled = value diff --git a/app/src/main/java/com/notcvnt/rknhardering/probe/TunProbeDiagnosticsFormatter.kt b/app/src/main/java/com/notcvnt/rknhardering/probe/TunProbeDiagnosticsFormatter.kt index fe4e8fe..8c868e3 100644 --- a/app/src/main/java/com/notcvnt/rknhardering/probe/TunProbeDiagnosticsFormatter.kt +++ b/app/src/main/java/com/notcvnt/rknhardering/probe/TunProbeDiagnosticsFormatter.kt @@ -13,6 +13,8 @@ import java.util.Locale object TunProbeDiagnosticsFormatter { + private const val NONE = "" + fun format( diagnostics: TunProbeDiagnostics, settings: CheckSettings, @@ -109,8 +111,8 @@ object TunProbeDiagnosticsFormatter { builder.appendLine("available: true") builder.appendLine("interfaceName: ${path.interfaceName ?: ""}") builder.appendLine("selectedMode: ${path.selectedMode?.name ?: "NONE"}") - builder.appendLine("selectedIp: ${path.selectedIp?.let(::maskIp) ?: ""}") - builder.appendLine("selectedError: ${path.selectedError?.let(::maskIpsInText) ?: ""}") + builder.appendLine("selectedIp: ${path.selectedIp?.let(::maskIp) ?: NONE}") + builder.appendLine("selectedError: ${path.selectedError?.let(::maskIpsInText) ?: NONE}") builder.appendLine("dnsPathMismatch: ${path.dnsPathMismatch}") appendAttempt(builder, "strict", path.strict) appendAttempt(builder, "curl-compatible", path.curlCompatible) @@ -122,8 +124,8 @@ object TunProbeDiagnosticsFormatter { attempt: TunProbeAttemptDiagnostics, ) { builder.appendLine("$label.status: ${attempt.status}") - builder.appendLine("$label.ip: ${attempt.ip?.let(::maskIp) ?: ""}") - builder.appendLine("$label.error: ${attempt.error?.let(::maskIpsInText) ?: ""}") + builder.appendLine("$label.ip: ${attempt.ip?.let(::maskIp) ?: NONE}") + builder.appendLine("$label.error: ${attempt.error?.let(::maskIpsInText) ?: NONE}") appendTransportDiagnostics(builder, label, attempt.transportDiagnostics) builder.appendLine("$label.endpoints:") if (attempt.endpointAttempts.isEmpty()) { @@ -138,9 +140,9 @@ object TunProbeDiagnosticsFormatter { private fun formatEndpointAttempt(attempt: TunEndpointAttempt): String { val result = when (attempt.status) { - PublicIpProbeStatus.SUCCEEDED -> "ip=${attempt.ip?.let(::maskIp) ?: ""}" + PublicIpProbeStatus.SUCCEEDED -> "ip=${attempt.ip?.let(::maskIp) ?: NONE}" PublicIpProbeStatus.FAILED, - PublicIpProbeStatus.SKIPPED -> "error=${attempt.error?.let(::maskIpsInText) ?: ""}" + PublicIpProbeStatus.SKIPPED -> "error=${attempt.error?.let(::maskIpsInText) ?: NONE}" } return "${attempt.endpoint} [${attempt.familyHint}] -> ${attempt.status} ($result)" } @@ -191,14 +193,14 @@ object TunProbeDiagnosticsFormatter { DnsResolverMode.DIRECT -> { val servers = settings.resolverConfig.effectiveDirectServers() .joinToString(", ") { maskIp(it) } - .ifBlank { "" } + .ifBlank { NONE } "DIRECT preset=${settings.resolverConfig.preset.name} servers=$servers" } DnsResolverMode.DOH -> { val bootstrap = settings.resolverConfig.effectiveDohBootstrapHosts() .joinToString(", ") { maskIp(it) } - .ifBlank { "" } - "DOH preset=${settings.resolverConfig.preset.name} url=${settings.resolverConfig.effectiveDohUrl() ?: ""} bootstrap=$bootstrap" + .ifBlank { NONE } + "DOH preset=${settings.resolverConfig.preset.name} url=${settings.resolverConfig.effectiveDohUrl() ?: NONE} bootstrap=$bootstrap" } } } diff --git a/app/src/main/java/com/notcvnt/rknhardering/util/MaskingUtils.kt b/app/src/main/java/com/notcvnt/rknhardering/util/MaskingUtils.kt index c4df323..0fdff50 100644 --- a/app/src/main/java/com/notcvnt/rknhardering/util/MaskingUtils.kt +++ b/app/src/main/java/com/notcvnt/rknhardering/util/MaskingUtils.kt @@ -7,19 +7,21 @@ import java.net.Inet4Address import java.net.Inet6Address import java.net.InetAddress +private const val IP_MASK_PLACEHOLDER = "*.*.*.*" + fun maskIp(ip: String): String { val normalized = ip.trim() val parsed = when { normalized.contains('.') -> { val parts = normalized.split('.') if (parts.size != 4 || parts.any { (it.toIntOrNull() ?: -1) !in 0..255 }) { - return "*.*.*.*" + return IP_MASK_PLACEHOLDER } runCatching { InetAddress.getByName(normalized) }.getOrNull() } normalized.contains(':') -> runCatching { InetAddress.getByName(normalized) }.getOrNull() else -> null - } ?: return "*.*.*.*" + } ?: return IP_MASK_PLACEHOLDER return when (parsed) { is Inet4Address -> { if (parsed.isSiteLocalAddress || parsed.isLoopbackAddress || parsed.isLinkLocalAddress) { @@ -41,7 +43,7 @@ fun maskIp(ip: String): String { } } } - else -> "*.*.*.*" + else -> IP_MASK_PLACEHOLDER } } diff --git a/app/src/main/java/com/notcvnt/rknhardering/vpn/VpnAppClassifier.kt b/app/src/main/java/com/notcvnt/rknhardering/vpn/VpnAppClassifier.kt index 2c7c312..5f315ab 100644 --- a/app/src/main/java/com/notcvnt/rknhardering/vpn/VpnAppClassifier.kt +++ b/app/src/main/java/com/notcvnt/rknhardering/vpn/VpnAppClassifier.kt @@ -46,7 +46,7 @@ object VpnAppClassifier { ) private val coreTypeMarkers = listOf( - CORE_TYPE_SING_BOX to listOf("sing-box", "singbox"), + CORE_TYPE_SING_BOX to listOf(CORE_TYPE_SING_BOX, "singbox"), CORE_TYPE_XRAY_V2RAY to listOf("xray", "v2ray", "v2fly"), CORE_TYPE_CLASH_MIHOMO to listOf("mihomo", "clash"), CORE_TYPE_WIREGUARD to listOf("wireguard"),