From 23b4371ea79fb6d66e53730052afbe430d5a79f9 Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Fri, 25 Aug 2023 00:09:33 +0200 Subject: [PATCH 1/7] fix(webconnectivity): handle android_dns_cache_no_data as dns anomaly This diff changes the handling of android_dns_cache_no_data to mark it and any other DNS inconsistenct as DNS anomaly. The previous implementation was not handling any DNS inconsistency as an anomaly, therefore I needed to adjust a bunch of summary_test.go tests. Part of https://github.com/ooni/probe/issues/2499 and https://github.com/ooni/probe/issues/2029. I bumped the version number because this is a significant change in the analysis algorithm implemented by the probe. --- internal/experiment/webconnectivity/summary.go | 13 +++++++++---- .../experiment/webconnectivity/summary_test.go | 18 ++++++------------ .../webconnectivity/webconnectivity.go | 2 +- .../webconnectivity/webconnectivity_test.go | 2 +- .../webconnectivityqa/dnsblocking.go | 10 +++++++--- .../experiment/webconnectivityqa/success.go | 12 ++++++++---- .../experiment/webconnectivityqa/testkeys.go | 3 +++ .../webconnectivityqa/tlsblocking.go | 6 ++++-- 8 files changed, 39 insertions(+), 27 deletions(-) diff --git a/internal/experiment/webconnectivity/summary.go b/internal/experiment/webconnectivity/summary.go index 4601a360eb..1e0eb2e1ef 100644 --- a/internal/experiment/webconnectivity/summary.go +++ b/internal/experiment/webconnectivity/summary.go @@ -100,6 +100,7 @@ func Summarize(tk *TestKeys) (out Summary) { defer func() { out.Blocking = DetermineBlocking(out) }() + var ( accessible = true inaccessible = false @@ -108,6 +109,7 @@ func Summarize(tk *TestKeys) (out Summary) { httpFailure = "http-failure" tcpIP = "tcp_ip" ) + // If the measurement was for an HTTPS website and the HTTP experiment // succeeded, then either there is a compromised CA in our pool (which is // certifi-go), or there is transparent proxying, or we are actually @@ -119,11 +121,13 @@ func Summarize(tk *TestKeys) (out Summary) { out.Status |= StatusSuccessSecure return } + // If we couldn't contact the control, we cannot do much more here. if tk.ControlFailure != nil { out.Status |= StatusAnomalyControlUnreachable return } + // If DNS failed with NXDOMAIN and the control DNS is consistent, then it // means this website does not exist anymore. We need to include the weird // cache failure on Android into this analysis because that failure means @@ -142,15 +146,16 @@ func Summarize(tk *TestKeys) (out Summary) { out.Status |= StatusSuccessNXDOMAIN | StatusExperimentDNS return } - // Otherwise, if DNS failed with NXDOMAIN, it's DNS based blocking. - // TODO(bassosimone): do we wanna include other errors here? Like timeout? - if tk.DNSExperimentFailure != nil && - *tk.DNSExperimentFailure == netxlite.FailureDNSNXDOMAINError { + + // Otherwise, if the DNS is inconsistent, flag a DNS failure. Note that this + // case also includes when the probe lookup fails and the TH one does not. + if tk.DNSConsistency != nil && *tk.DNSConsistency == DNSInconsistent { out.Accessible = &inaccessible out.BlockingReason = &dns out.Status |= StatusAnomalyDNS | StatusExperimentDNS return } + // If we tried to connect more than once and never succedeed and we were // able to measure DNS consistency, then we can conclude something. if tk.TCPConnectAttempts > 0 && tk.TCPConnectSuccesses <= 0 && tk.DNSConsistency != nil { diff --git a/internal/experiment/webconnectivity/summary_test.go b/internal/experiment/webconnectivity/summary_test.go index 832b61f5ef..85723a2122 100644 --- a/internal/experiment/webconnectivity/summary_test.go +++ b/internal/experiment/webconnectivity/summary_test.go @@ -152,9 +152,7 @@ func TestSummarize(t *testing.T) { BlockingReason: &dns, Blocking: &dns, Accessible: &falseValue, - Status: webconnectivity.StatusAnomalyConnect | - webconnectivity.StatusExperimentConnect | - webconnectivity.StatusAnomalyDNS, + Status: webconnectivity.StatusAnomalyDNS | webconnectivity.StatusExperimentDNS, }, }, { name: "with TCP total failure and unexpected DNS consistency", @@ -350,9 +348,7 @@ func TestSummarize(t *testing.T) { BlockingReason: &dns, Blocking: &dns, Accessible: &falseValue, - Status: webconnectivity.StatusExperimentHTTP | - webconnectivity.StatusAnomalyTLSHandshake | - webconnectivity.StatusAnomalyDNS, + Status: webconnectivity.StatusExperimentDNS | webconnectivity.StatusAnomalyDNS, }, }, { name: "with SSL unknown auth _and_ untrustworthy DNS _and_ a longer chain", @@ -367,11 +363,10 @@ func TestSummarize(t *testing.T) { }, }, wantOut: webconnectivity.Summary{ - BlockingReason: &httpFailure, - Blocking: &httpFailure, + BlockingReason: &dns, + Blocking: &dns, Accessible: &falseValue, - Status: webconnectivity.StatusExperimentHTTP | - webconnectivity.StatusAnomalyTLSHandshake, + Status: webconnectivity.StatusExperimentDNS | webconnectivity.StatusAnomalyDNS, }, }, { name: "with status code and body length matching", @@ -442,8 +437,7 @@ func TestSummarize(t *testing.T) { BlockingReason: &dns, Blocking: &dns, Accessible: &falseValue, - Status: webconnectivity.StatusAnomalyHTTPDiff | - webconnectivity.StatusAnomalyDNS, + Status: webconnectivity.StatusExperimentDNS | webconnectivity.StatusAnomalyDNS, }, }, { name: "with suspect http-diff and consistent DNS", diff --git a/internal/experiment/webconnectivity/webconnectivity.go b/internal/experiment/webconnectivity/webconnectivity.go index 40732abcb9..815b385209 100644 --- a/internal/experiment/webconnectivity/webconnectivity.go +++ b/internal/experiment/webconnectivity/webconnectivity.go @@ -15,7 +15,7 @@ import ( const ( testName = "web_connectivity" - testVersion = "0.4.2" + testVersion = "0.4.3" ) // Config contains the experiment config. diff --git a/internal/experiment/webconnectivity/webconnectivity_test.go b/internal/experiment/webconnectivity/webconnectivity_test.go index 6b655ba655..4bb720094d 100644 --- a/internal/experiment/webconnectivity/webconnectivity_test.go +++ b/internal/experiment/webconnectivity/webconnectivity_test.go @@ -21,7 +21,7 @@ func TestNewExperimentMeasurer(t *testing.T) { if measurer.ExperimentName() != "web_connectivity" { t.Fatal("unexpected name") } - if measurer.ExperimentVersion() != "0.4.2" { + if measurer.ExperimentVersion() != "0.4.3" { t.Fatal("unexpected version") } } diff --git a/internal/experiment/webconnectivityqa/dnsblocking.go b/internal/experiment/webconnectivityqa/dnsblocking.go index 47c236cdf5..2a8b5edd1f 100644 --- a/internal/experiment/webconnectivityqa/dnsblocking.go +++ b/internal/experiment/webconnectivityqa/dnsblocking.go @@ -9,7 +9,7 @@ import ( func dnsBlockingAndroidDNSCacheNoData() *TestCase { return &TestCase{ Name: "measuring https://www.example.com/ with getaddrinfo errors and android_dns_cache_no_data", - Flags: TestCaseFlagNoV04, + Flags: 0, Input: "https://www.example.com/", Configure: func(env *netemx.QAEnv) { // make sure the env knows we want to emulate our getaddrinfo wrapper behavior @@ -19,7 +19,11 @@ func dnsBlockingAndroidDNSCacheNoData() *TestCase { // converted into android_dns_cache_no_data by the emulation layer env.ISPResolverConfig().RemoveRecord("www.example.com") }, - ExpectErr: false, - ExpectTestKeys: &testKeys{Accessible: false, Blocking: "dns"}, + ExpectErr: false, + ExpectTestKeys: &testKeys{ + DNSConsistency: "inconsistent", + Accessible: false, + Blocking: "dns", + }, } } diff --git a/internal/experiment/webconnectivityqa/success.go b/internal/experiment/webconnectivityqa/success.go index 83d75affdc..477d53923f 100644 --- a/internal/experiment/webconnectivityqa/success.go +++ b/internal/experiment/webconnectivityqa/success.go @@ -4,12 +4,14 @@ package webconnectivityqa func sucessWithHTTP() *TestCase { return &TestCase{ Name: "measuring http://www.example.com/ without censorship", + Flags: 0, Input: "http://www.example.com/", Configure: nil, ExpectErr: false, ExpectTestKeys: &testKeys{ - Accessible: true, - Blocking: false, + DNSConsistency: "consistent", + Accessible: true, + Blocking: false, }, } } @@ -18,12 +20,14 @@ func sucessWithHTTP() *TestCase { func sucessWithHTTPS() *TestCase { return &TestCase{ Name: "measuring https://www.example.com/ without censorship", + Flags: 0, Input: "https://www.example.com/", Configure: nil, ExpectErr: false, ExpectTestKeys: &testKeys{ - Accessible: true, - Blocking: false, + DNSConsistency: "consistent", + Accessible: true, + Blocking: false, }, } } diff --git a/internal/experiment/webconnectivityqa/testkeys.go b/internal/experiment/webconnectivityqa/testkeys.go index bcb5145166..b37aa9719e 100644 --- a/internal/experiment/webconnectivityqa/testkeys.go +++ b/internal/experiment/webconnectivityqa/testkeys.go @@ -11,6 +11,9 @@ import ( // testKeys is the test keys structure returned by this package. type testKeys struct { + // DNSConsistency indicates whether the DNS is consistent with the TH. + DNSConsistency any `json:"dns_consistency"` + // Accessible indicates whether the URL was accessible. Accessible any `json:"accessible"` diff --git a/internal/experiment/webconnectivityqa/tlsblocking.go b/internal/experiment/webconnectivityqa/tlsblocking.go index c178867380..d400fba5b7 100644 --- a/internal/experiment/webconnectivityqa/tlsblocking.go +++ b/internal/experiment/webconnectivityqa/tlsblocking.go @@ -10,6 +10,7 @@ import ( func tlsBlockingConnectionReset() *TestCase { return &TestCase{ Name: "measuring https://www.example.com/ with TLS blocking of the SNI", + Flags: 0, Input: "https://www.example.com/", Configure: func(env *netemx.QAEnv) { env.DPIEngine().AddRule(&netem.DPIResetTrafficForTLSSNI{ @@ -19,8 +20,9 @@ func tlsBlockingConnectionReset() *TestCase { }, ExpectErr: false, ExpectTestKeys: &testKeys{ - Accessible: false, - Blocking: "http-failure", + DNSConsistency: "consistent", + Accessible: false, + Blocking: "http-failure", }, } } From ccbe862f83b57c7cd843c30715d0d3ce4ee1e19c Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Fri, 25 Aug 2023 00:14:09 +0200 Subject: [PATCH 2/7] x --- internal/experiment/webconnectivity/summary.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/experiment/webconnectivity/summary.go b/internal/experiment/webconnectivity/summary.go index 1e0eb2e1ef..811c3c4a53 100644 --- a/internal/experiment/webconnectivity/summary.go +++ b/internal/experiment/webconnectivity/summary.go @@ -148,7 +148,9 @@ func Summarize(tk *TestKeys) (out Summary) { } // Otherwise, if the DNS is inconsistent, flag a DNS failure. Note that this - // case also includes when the probe lookup fails and the TH one does not. + // case also includes when the probe lookup fails and the TH one does not because + // in such case all the addresses found by the TH do not belong to the probe, + // which failed and did not resolve any IP address. if tk.DNSConsistency != nil && *tk.DNSConsistency == DNSInconsistent { out.Accessible = &inaccessible out.BlockingReason = &dns From fdcf1dbaa0d8efc3e8af151b963348ec00dbb44c Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Tue, 10 Oct 2023 15:19:15 +0200 Subject: [PATCH 3/7] x --- .../experiment/webconnectivity/dnsanalysis.go | 5 ++++ .../experiment/webconnectivity/summary.go | 25 +++++++++++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/internal/experiment/webconnectivity/dnsanalysis.go b/internal/experiment/webconnectivity/dnsanalysis.go index dd8f655a35..b3f177498b 100644 --- a/internal/experiment/webconnectivity/dnsanalysis.go +++ b/internal/experiment/webconnectivity/dnsanalysis.go @@ -34,12 +34,14 @@ func DNSAnalysis(URL *url.URL, measurement DNSLookupResult, control ControlResponse) (out DNSAnalysisResult) { // 0. start assuming it's not consistent out.DNSConsistency = &DNSInconsistent + // 1. flip to consistent if we're targeting an IP address because the // control will actually return dns_name_error in this case. if net.ParseIP(URL.Hostname()) != nil { out.DNSConsistency = &DNSConsistent return } + // 2. flip to consistent if the failures are compatible if measurement.Failure != nil && control.DNS.Failure != nil { switch *control.DNS.Failure { @@ -57,6 +59,7 @@ func DNSAnalysis(URL *url.URL, measurement DNSLookupResult, } return } + // 3. flip to consistent if measurement and control returned IP addresses // that belong to the same Autonomous System(s). // @@ -85,6 +88,7 @@ func DNSAnalysis(URL *url.URL, measurement DNSLookupResult, return } } + // 4. when ASN lookup failed (unlikely), check whether // there is overlap in the returned IP addresses ipmap := make(map[string]int) @@ -101,6 +105,7 @@ func DNSAnalysis(URL *url.URL, measurement DNSLookupResult, return } } + // 5. conclude that measurement and control are inconsistent return } diff --git a/internal/experiment/webconnectivity/summary.go b/internal/experiment/webconnectivity/summary.go index 811c3c4a53..b087fea843 100644 --- a/internal/experiment/webconnectivity/summary.go +++ b/internal/experiment/webconnectivity/summary.go @@ -147,11 +147,26 @@ func Summarize(tk *TestKeys) (out Summary) { return } - // Otherwise, if the DNS is inconsistent, flag a DNS failure. Note that this - // case also includes when the probe lookup fails and the TH one does not because - // in such case all the addresses found by the TH do not belong to the probe, - // which failed and did not resolve any IP address. - if tk.DNSConsistency != nil && *tk.DNSConsistency == DNSInconsistent { + // Web Connectivity's analysis algorithm up until v0.4.2 gave priority to checking for http-diff + // over saying that there's "dns" blocking because the DNS is inconsistent. + // + // In v0.4.3, we want to address https://github.com/ooni/probe/issues/2499 while still + // trying to preserve the original spirit of the analysis algorithm. + // + // To this end, we _only_ flag failure if the following happens: + // + // 1. the DNS is inconsistent; and + // + // 2. the probe's DNS lookup has failed with dns_nxdomain_error or android_dns_cache_no_data. + // + // By using this algorithm, we narrow the scope and impact of this change but we are, at + // the same time, able to catch cases such as the one mentioned above. + // + // A more aggressive approach would flag as "dns" blocking any inconsistent result but + // that would depart quite a lot from the behavior of v0.4.2. + if tk.DNSConsistency != nil && *tk.DNSConsistency == DNSInconsistent && + tk.DNSExperimentFailure != nil && (*tk.DNSExperimentFailure == netxlite.FailureDNSNXDOMAINError || + *tk.DNSExperimentFailure == netxlite.FailureAndroidDNSCacheNoData) { out.Accessible = &inaccessible out.BlockingReason = &dns out.Status |= StatusAnomalyDNS | StatusExperimentDNS From 008d25142019b86e0f8edbdb0f04d0331e62e028 Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Tue, 10 Oct 2023 15:36:08 +0200 Subject: [PATCH 4/7] x --- internal/experiment/webconnectivityqa/dnsblocking.go | 7 ++++--- internal/experiment/webconnectivityqa/testkeys.go | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/experiment/webconnectivityqa/dnsblocking.go b/internal/experiment/webconnectivityqa/dnsblocking.go index b4066905fb..dc27caa9b6 100644 --- a/internal/experiment/webconnectivityqa/dnsblocking.go +++ b/internal/experiment/webconnectivityqa/dnsblocking.go @@ -9,7 +9,7 @@ import ( func dnsBlockingAndroidDNSCacheNoData() *TestCase { return &TestCase{ Name: "dnsBlockingAndroidDNSCacheNoData", - Flags: TestCaseFlagNoV04, // see https://github.com/ooni/probe-cli/pull/1211 + Flags: 0, Input: "https://www.example.com/", Configure: func(env *netemx.QAEnv) { // make sure the env knows we want to emulate our getaddrinfo wrapper behavior @@ -23,8 +23,9 @@ func dnsBlockingAndroidDNSCacheNoData() *TestCase { ExpectTestKeys: &testKeys{ DNSExperimentFailure: "android_dns_cache_no_data", DNSConsistency: "inconsistent", - XDNSFlags: 2, // AnalysisDNSUnexpectedFailure - XBlockingFlags: 33, // analysisFlagDNSBlocking | analysisFlagSuccess + XStatus: 2080, // StatusExperimentDNS | StatusAnomalyDNS + XDNSFlags: 2, // AnalysisDNSUnexpectedFailure + XBlockingFlags: 33, // analysisFlagDNSBlocking | analysisFlagSuccess Accessible: false, Blocking: "dns", }, diff --git a/internal/experiment/webconnectivityqa/testkeys.go b/internal/experiment/webconnectivityqa/testkeys.go index 91761a3183..f33e8c040c 100644 --- a/internal/experiment/webconnectivityqa/testkeys.go +++ b/internal/experiment/webconnectivityqa/testkeys.go @@ -69,7 +69,7 @@ func compareTestKeys(expected, got *testKeys) error { } switch got.XExperimentVersion { - case "0.4.2": + case "0.4.3": // ignore the fields that are specific to LTE options = append(options, cmpopts.IgnoreFields(testKeys{}, "XDNSFlags", "XBlockingFlags", "XNullNullFlags")) From 4709fc5c2bf11ddcc3c08966209656ea33e6f8cc Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Tue, 10 Oct 2023 15:55:06 +0200 Subject: [PATCH 5/7] x --- .../experiment/webconnectivity/summary_test.go | 18 ++++++++++++------ .../experiment/webconnectivityqa/run_test.go | 8 ++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/internal/experiment/webconnectivity/summary_test.go b/internal/experiment/webconnectivity/summary_test.go index 1432f366ae..de6f58794c 100644 --- a/internal/experiment/webconnectivity/summary_test.go +++ b/internal/experiment/webconnectivity/summary_test.go @@ -152,7 +152,9 @@ func TestSummarize(t *testing.T) { BlockingReason: &dns, Blocking: &dns, Accessible: &falseValue, - Status: webconnectivity.StatusAnomalyDNS | webconnectivity.StatusExperimentDNS, + Status: webconnectivity.StatusAnomalyConnect | + webconnectivity.StatusExperimentConnect | + webconnectivity.StatusAnomalyDNS, }, }, { name: "with TCP total failure and unexpected DNS consistency", @@ -348,7 +350,9 @@ func TestSummarize(t *testing.T) { BlockingReason: &dns, Blocking: &dns, Accessible: &falseValue, - Status: webconnectivity.StatusExperimentDNS | webconnectivity.StatusAnomalyDNS, + Status: webconnectivity.StatusExperimentHTTP | + webconnectivity.StatusAnomalyTLSHandshake | + webconnectivity.StatusAnomalyDNS, }, }, { name: "with SSL unknown auth _and_ untrustworthy DNS _and_ a longer chain", @@ -363,10 +367,11 @@ func TestSummarize(t *testing.T) { }, }, wantOut: webconnectivity.Summary{ - BlockingReason: &dns, - Blocking: &dns, + BlockingReason: &httpFailure, + Blocking: &httpFailure, Accessible: &falseValue, - Status: webconnectivity.StatusExperimentDNS | webconnectivity.StatusAnomalyDNS, + Status: webconnectivity.StatusExperimentHTTP | + webconnectivity.StatusAnomalyTLSHandshake, }, }, { name: "with status code and body length matching", @@ -437,7 +442,8 @@ func TestSummarize(t *testing.T) { BlockingReason: &dns, Blocking: &dns, Accessible: &falseValue, - Status: webconnectivity.StatusExperimentDNS | webconnectivity.StatusAnomalyDNS, + Status: webconnectivity.StatusAnomalyHTTPDiff | + webconnectivity.StatusAnomalyDNS, }, }, { name: "with suspect http-diff and consistent DNS", diff --git a/internal/experiment/webconnectivityqa/run_test.go b/internal/experiment/webconnectivityqa/run_test.go index 0615fe3ba0..d0ae7433cb 100644 --- a/internal/experiment/webconnectivityqa/run_test.go +++ b/internal/experiment/webconnectivityqa/run_test.go @@ -78,7 +78,7 @@ func TestRunTestCase(t *testing.T) { return "web_connectivity" }, MockExperimentVersion: func() string { - return "0.4.2" + return "0.4.3" }, MockRun: func(ctx context.Context, args *model.ExperimentArgs) error { args.Measurement.TestKeys = &testKeys{} @@ -107,7 +107,7 @@ func TestRunTestCase(t *testing.T) { return "web_connectivity" }, MockExperimentVersion: func() string { - return "0.4.2" + return "0.4.3" }, MockRun: func(ctx context.Context, args *model.ExperimentArgs) error { args.Measurement.TestKeys = &testKeys{ @@ -139,7 +139,7 @@ func TestRunTestCase(t *testing.T) { return "web_connectivity" }, MockExperimentVersion: func() string { - return "0.4.2" + return "0.4.3" }, MockRun: func(ctx context.Context, args *model.ExperimentArgs) error { args.Measurement.TestKeys = &testKeys{ @@ -173,7 +173,7 @@ func TestRunTestCase(t *testing.T) { return "web_connectivity" }, MockExperimentVersion: func() string { - return "0.4.2" + return "0.4.3" }, MockRun: func(ctx context.Context, args *model.ExperimentArgs) error { args.Measurement.TestKeys = &testKeys{ From 65ae5619a6d97c647df66eac0f59ae9779586e8b Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Tue, 10 Oct 2023 16:03:23 +0200 Subject: [PATCH 6/7] [ci skip] Apply suggestions from code review --- internal/experiment/webconnectivity/summary.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/experiment/webconnectivity/summary.go b/internal/experiment/webconnectivity/summary.go index b087fea843..bd97a6177c 100644 --- a/internal/experiment/webconnectivity/summary.go +++ b/internal/experiment/webconnectivity/summary.go @@ -148,10 +148,10 @@ func Summarize(tk *TestKeys) (out Summary) { } // Web Connectivity's analysis algorithm up until v0.4.2 gave priority to checking for http-diff - // over saying that there's "dns" blocking because the DNS is inconsistent. + // over saying that there's "dns" blocking when the DNS is inconsistent. // // In v0.4.3, we want to address https://github.com/ooni/probe/issues/2499 while still - // trying to preserve the original spirit of the analysis algorithm. + // trying to preserve the original spirit of the v0.4.2 analysis algorithm. // // To this end, we _only_ flag failure if the following happens: // @@ -160,7 +160,7 @@ func Summarize(tk *TestKeys) (out Summary) { // 2. the probe's DNS lookup has failed with dns_nxdomain_error or android_dns_cache_no_data. // // By using this algorithm, we narrow the scope and impact of this change but we are, at - // the same time, able to catch cases such as the one mentioned above. + // the same time, able to catch cases such as the one mentioned by the issue above. // // A more aggressive approach would flag as "dns" blocking any inconsistent result but // that would depart quite a lot from the behavior of v0.4.2. From 5a796497c816b3c4950683ff2599907a49f809d8 Mon Sep 17 00:00:00 2001 From: Simone Basso Date: Tue, 10 Oct 2023 16:03:57 +0200 Subject: [PATCH 7/7] [ci skip] Update internal/experiment/webconnectivity/summary.go --- internal/experiment/webconnectivity/summary.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/experiment/webconnectivity/summary.go b/internal/experiment/webconnectivity/summary.go index bd97a6177c..ef0174aedb 100644 --- a/internal/experiment/webconnectivity/summary.go +++ b/internal/experiment/webconnectivity/summary.go @@ -153,7 +153,7 @@ func Summarize(tk *TestKeys) (out Summary) { // In v0.4.3, we want to address https://github.com/ooni/probe/issues/2499 while still // trying to preserve the original spirit of the v0.4.2 analysis algorithm. // - // To this end, we _only_ flag failure if the following happens: + // To this end, we _only_ flag anomaly if the following happens: // // 1. the DNS is inconsistent; and //