Skip to content

Commit 7b1bd1c

Browse files
Daniel RuberyChromium LUCI CQ
authored andcommitted
Generate reports for Safe Browsing about external app redirects
This CL creates ClientSafeBrowsingReportRequests for external app redirects and sends them to Safe Browsing when appropriate. Bug: 372064001 Change-Id: Icfe0f595e2e3b699e2fd70e76314c5d403919ff6 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5914755 Reviewed-by: thefrog <[email protected]> Commit-Queue: Daniel Rubery <[email protected]> Cr-Commit-Position: refs/heads/main@{#1367810}
1 parent 3392308 commit 7b1bd1c

14 files changed

+463
-2
lines changed

chrome/browser/safe_browsing/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ static_library("safe_browsing") {
123123
"delayed_warning_navigation_throttle.h",
124124
"download_protection/download_protection_util.cc",
125125
"download_protection/download_protection_util.h",
126+
"external_app_redirect_checking.cc",
127+
"external_app_redirect_checking.h",
126128
"network_context_service.cc",
127129
"network_context_service.h",
128130
"network_context_service_factory.cc",

chrome/browser/safe_browsing/android/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ source_set("android") {
1717

1818
deps = [
1919
":jni_headers",
20+
"//chrome/browser:browser_process",
2021
"//chrome/browser/profiles",
2122
"//chrome/browser/safe_browsing:advanced_protection",
2223
"//components/password_manager/core/browser/leak_detection",
2324
"//components/password_manager/core/common",
2425
"//components/prefs",
26+
"//components/safe_browsing/content/browser:safe_browsing_service",
2527
"//components/safe_browsing/core/common:safe_browsing_prefs",
2628
"//components/safe_browsing/core/common/hashprefix_realtime:hash_realtime_utils",
2729
"//components/signin/public/identity_manager",

chrome/browser/safe_browsing/android/safe_browsing_bridge.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
#include "base/files/file_path.h"
77
// NOTE: This target is transitively depended on by //chrome/browser and thus
88
// can't depend on it.
9+
#include "chrome/browser/browser_process.h"
910
#include "chrome/browser/profiles/profile.h"
1011
#include "chrome/browser/safe_browsing/advanced_protection_status_manager.h"
1112
#include "chrome/browser/safe_browsing/advanced_protection_status_manager_factory.h"
1213
#include "chrome/browser/signin/identity_manager_factory.h" // nogncheck
1314
#include "components/password_manager/core/common/password_manager_pref_names.h"
1415
#include "components/prefs/pref_service.h"
16+
#include "components/safe_browsing/content/browser/safe_browsing_service_interface.h"
1517
#include "components/safe_browsing/content/common/file_type_policies.h"
1618
#include "components/safe_browsing/core/common/hashprefix_realtime/hash_realtime_utils.h"
1719
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
@@ -105,6 +107,10 @@ static void JNI_SafeBrowsingBridge_ReportIntent(
105107
JNIEnv* env,
106108
content::WebContents* web_contents,
107109
std::string& package_name,
108-
std::string& uri) {}
110+
std::string& uri) {
111+
reinterpret_cast<SafeBrowsingServiceInterface*>(
112+
g_browser_process->safe_browsing_service())
113+
->ReportExternalAppRedirect(web_contents, package_name, uri);
114+
}
109115

110116
} // namespace safe_browsing
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Copyright 2024 The Chromium Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "chrome/browser/safe_browsing/external_app_redirect_checking.h"
6+
7+
#include "base/json/values_util.h"
8+
#include "chrome/browser/browser_process.h"
9+
#include "chrome/browser/profiles/profile.h"
10+
#include "chrome/browser/safe_browsing/chrome_user_population_helper.h"
11+
#include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager_factory.h"
12+
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
13+
#include "components/prefs/pref_service.h"
14+
#include "components/safe_browsing/content/browser/safe_browsing_navigation_observer_manager.h"
15+
#include "components/safe_browsing/core/browser/db/database_manager.h"
16+
#include "components/safe_browsing/core/common/proto/csd.pb.h"
17+
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
18+
#include "content/public/browser/web_contents.h"
19+
#include "services/preferences/public/cpp/dictionary_value_update.h"
20+
#include "services/preferences/public/cpp/scoped_pref_update.h"
21+
22+
namespace safe_browsing {
23+
24+
namespace {
25+
26+
constexpr base::TimeDelta kRecentVisitThreshold = base::Days(30);
27+
28+
// Keys in base::Value::Dict cannot contain periods.
29+
std::string SanitizeAppName(std::string_view app_name) {
30+
std::string sanitized;
31+
sanitized.reserve(app_name.size());
32+
for (char c : app_name) {
33+
if (c != '.') {
34+
sanitized += c;
35+
} else {
36+
sanitized += '_';
37+
}
38+
}
39+
40+
return sanitized;
41+
}
42+
43+
bool HasRecentAppVisit(PrefService& prefs, std::string_view app_name) {
44+
const base::Value::Dict& timestamps =
45+
prefs.GetDict(prefs::kExternalAppRedirectTimestamps);
46+
std::optional<base::Time> app_timestamp =
47+
base::ValueToTime(timestamps.Find(SanitizeAppName(app_name)));
48+
if (!app_timestamp) {
49+
return false;
50+
}
51+
return base::Time::Now() - *app_timestamp <= kRecentVisitThreshold;
52+
}
53+
54+
void OnAllowlistCheckComplete(
55+
base::OnceCallback<void(bool)> callback,
56+
bool is_allowlisted,
57+
std::optional<SafeBrowsingDatabaseManager::
58+
HighConfidenceAllowlistCheckLoggingDetails>) {
59+
std::move(callback).Run(!is_allowlisted);
60+
}
61+
62+
} // namespace
63+
64+
void ShouldReportExternalAppRedirect(
65+
scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
66+
content::WebContents* web_contents,
67+
std::string_view app_name,
68+
base::OnceCallback<void(bool)> callback) {
69+
if (!base::FeatureList::IsEnabled(kExternalAppRedirectTelemetry)) {
70+
// Always run `callback` asynchronously, to make calling code simpler.
71+
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
72+
FROM_HERE, base::BindOnce(std::move(callback), false));
73+
return;
74+
}
75+
76+
Profile* profile =
77+
Profile::FromBrowserContext(web_contents->GetBrowserContext());
78+
if (profile->IsOffTheRecord()) {
79+
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
80+
FROM_HERE, base::BindOnce(std::move(callback), false));
81+
return;
82+
}
83+
84+
PrefService& prefs = *profile->GetPrefs();
85+
if (!IsEnhancedProtectionEnabled(prefs)) {
86+
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
87+
FROM_HERE, base::BindOnce(std::move(callback), false));
88+
return;
89+
}
90+
91+
if (app_name.empty() || HasRecentAppVisit(prefs, app_name)) {
92+
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
93+
FROM_HERE, base::BindOnce(std::move(callback), false));
94+
return;
95+
}
96+
97+
database_manager->CheckUrlForHighConfidenceAllowlist(
98+
web_contents->GetLastCommittedURL(),
99+
base::BindOnce(&OnAllowlistCheckComplete, std::move(callback)));
100+
}
101+
102+
void LogExternalAppRedirectTimestamp(PrefService& prefs,
103+
std::string_view app_name) {
104+
if (app_name.empty()) {
105+
return;
106+
}
107+
prefs::ScopedDictionaryPrefUpdate update(
108+
&prefs, prefs::kExternalAppRedirectTimestamps);
109+
update->Set(SanitizeAppName(app_name), base::TimeToValue(base::Time::Now()));
110+
}
111+
112+
void CleanupExternalAppRedirectTimestamps(PrefService& prefs) {
113+
std::vector<std::string> to_remove;
114+
for (const auto [app_name, timestamp_value] :
115+
prefs.GetDict(prefs::kExternalAppRedirectTimestamps)) {
116+
std::optional<base::Time> timestamp = base::ValueToTime(timestamp_value);
117+
if (!timestamp || base::Time::Now() - *timestamp >= kRecentVisitThreshold) {
118+
to_remove.push_back(app_name);
119+
}
120+
}
121+
122+
prefs::ScopedDictionaryPrefUpdate update(
123+
&prefs, prefs::kExternalAppRedirectTimestamps);
124+
for (const std::string& key : to_remove) {
125+
update->Remove(key);
126+
}
127+
}
128+
129+
std::unique_ptr<ClientSafeBrowsingReportRequest> MakeExternalAppRedirectReport(
130+
content::WebContents* web_contents,
131+
std::string_view uri) {
132+
constexpr int kReferrerChainUserGestureLimit = 2;
133+
134+
if (!web_contents) {
135+
return nullptr;
136+
}
137+
138+
auto report = std::make_unique<ClientSafeBrowsingReportRequest>();
139+
140+
report->set_type(ClientSafeBrowsingReportRequest::EXTERNAL_APP_REDIRECT);
141+
report->set_url(std::string(uri));
142+
report->set_page_url(web_contents->GetLastCommittedURL().spec());
143+
*report->mutable_population() = GetUserPopulationForProfile(
144+
Profile::FromBrowserContext(web_contents->GetBrowserContext()));
145+
auto* navigation_observer_manager =
146+
SafeBrowsingNavigationObserverManagerFactory::GetForBrowserContext(
147+
web_contents->GetBrowserContext());
148+
149+
navigation_observer_manager->IdentifyReferrerChainByRenderFrameHost(
150+
web_contents->GetPrimaryMainFrame(), kReferrerChainUserGestureLimit,
151+
report->mutable_referrer_chain());
152+
return report;
153+
}
154+
155+
} // namespace safe_browsing
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright 2024 The Chromium Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef CHROME_BROWSER_SAFE_BROWSING_EXTERNAL_APP_REDIRECT_CHECKING_H_
6+
#define CHROME_BROWSER_SAFE_BROWSING_EXTERNAL_APP_REDIRECT_CHECKING_H_
7+
8+
#include <string>
9+
10+
#include "base/functional/bind.h"
11+
12+
namespace content {
13+
class WebContents;
14+
}
15+
16+
class PrefService;
17+
18+
namespace safe_browsing {
19+
20+
class ClientSafeBrowsingReportRequest;
21+
class SafeBrowsingDatabaseManager;
22+
23+
// Asynchronously check whether we should report to Safe Browsing a redirect by
24+
// `web_contents` to `app_name`. Returns the result asynchronously through
25+
// `callback`.
26+
void ShouldReportExternalAppRedirect(
27+
scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
28+
content::WebContents* web_contents,
29+
std::string_view app_name,
30+
base::OnceCallback<void(bool)> callback);
31+
32+
// Log the current time as the most recent usage of a redirect to the given app.
33+
void LogExternalAppRedirectTimestamp(PrefService& prefs,
34+
std::string_view app_name);
35+
36+
// Clean up expired redirect timestamps.
37+
void CleanupExternalAppRedirectTimestamps(PrefService& prefs);
38+
39+
// Create the report that will be sent to Safe Browsing, if appropriate.
40+
std::unique_ptr<ClientSafeBrowsingReportRequest> MakeExternalAppRedirectReport(
41+
content::WebContents* web_contents,
42+
std::string_view uri);
43+
44+
} // namespace safe_browsing
45+
46+
#endif // CHROME_BROWSER_SAFE_BROWSING_EXTERNAL_APP_REDIRECT_CHECKING_H_

0 commit comments

Comments
 (0)