app: don't report ICMPv6 silence that was never measured
The per-network attribution fix worked — the finding named rmnet_data1 instead of the IPv4-only wifi — and immediately exposed a worse problem underneath. Cellular's result was `ok: false` because binding a socket to it failed with EPERM, so no echo request was ever sent; the finding then reported "IPv6 is configured, but ICMPv6 gets no reply" about a network the app had never pinged. That is an assertion about the user's carrier with nothing behind it. `attempted` now travels beside `ok`, set only once sendto has returned, and the finding requires both. Failing to bind is a fact about this app's permissions on this device; it says nothing about the network, and the two must not share a boolean. Verified on hardware with a VPN active: every network fails to bind with EPERM, nothing is sent, and no ICMPv6 finding is emitted — where the previous build would have blamed the carrier. Recorded in build-status: Android blocks per-network binding entirely while a VPN holds the default route, so per-network measurement is unavailable to anyone with one connected. That needs a deliberate answer rather than a silently green run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7a5004f293
commit
d9ee8bc2ae
@@ -420,14 +420,24 @@ class RunViewModel(app: Application) : AndroidViewModel(app) {
|
||||
* that depended on the wording of a human-readable string would break silently the first time
|
||||
* that wording improved.
|
||||
*/
|
||||
private fun icmpResults(t: Test): Map<String, Boolean> {
|
||||
val out = HashMap<String, Boolean>()
|
||||
/** What one network's ICMP attempt did: whether it ran at all, and whether it was answered. */
|
||||
private data class IcmpOutcome(val attempted: Boolean, val ok: Boolean)
|
||||
|
||||
/**
|
||||
* Per-network ICMP outcomes, keyed by network id.
|
||||
*
|
||||
* Reads the structured evidence the probe records rather than its prose detail — a finding
|
||||
* that depended on the wording of a human-readable string would break silently the first time
|
||||
* that wording improved.
|
||||
*/
|
||||
private fun icmpResults(t: Test): Map<String, IcmpOutcome> {
|
||||
val out = HashMap<String, IcmpOutcome>()
|
||||
val ev = t.evidence ?: return out
|
||||
for ((_, v) in ev) {
|
||||
val o = v as? kotlinx.serialization.json.JsonObject ?: continue
|
||||
val ref = (o["network_ref"] as? kotlinx.serialization.json.JsonPrimitive)?.content ?: continue
|
||||
val ok = (o["ok"] as? kotlinx.serialization.json.JsonPrimitive)?.content == "true"
|
||||
out[ref] = ok
|
||||
fun flag(k: String) = (o[k] as? kotlinx.serialization.json.JsonPrimitive)?.content == "true"
|
||||
out[ref] = IcmpOutcome(attempted = flag("attempted"), ok = flag("ok"))
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -578,8 +588,11 @@ class RunViewModel(app: Application) : AndroidViewModel(app) {
|
||||
for (n in networks) {
|
||||
val provisioned = ipv6Provisioned(networks, n.id)
|
||||
if (provisioned) anyV6Network = true
|
||||
val ok = results[n.id] ?: continue
|
||||
if (!provisioned || ok) continue
|
||||
val r = results[n.id] ?: continue
|
||||
// Silence is only evidence if something was actually sent. A bind that failed
|
||||
// with EPERM says the app could not use the interface, which is a fact about
|
||||
// this app's permissions and says nothing whatsoever about the network.
|
||||
if (!provisioned || !r.attempted || r.ok) continue
|
||||
val where = n.iface?.takeIf { it.isNotBlank() } ?: "this network"
|
||||
out.add(
|
||||
Finding(
|
||||
|
||||
Reference in New Issue
Block a user