Commit a6c977b1 authored by Xinghui Lu's avatar Xinghui Lu Committed by Chromium LUCI CQ

Add Referring app information to LoginReputationClientRequest

Proposal: go/phishguard-referring-app-android.

Add SafeBrowsingReferringAppBridge to obtain referring app information
from java side. This class is not added under safe_browsing/ module,
because it depends on IntentHandler, which is not modularized yet.

Bug: 1154860
Change-Id: I75307e232a63d4f09016db7bb712186f266896d0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2568643
Commit-Queue: Xinghui Lu <xinghuilu@chromium.org>
Reviewed-by: default avatarVarun Khaneja <vakh@chromium.org>
Reviewed-by: default avatarMichael Thiessen <mthiesse@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835825}
parent 4c3b260e
......@@ -3400,6 +3400,7 @@ generate_jni("chrome_jni_headers") {
"java/src/org/chromium/chrome/browser/rlz/RevenueStats.java",
"java/src/org/chromium/chrome/browser/rlz/RlzPingHandler.java",
"java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingPasswordReuseDialogBridge.java",
"java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingReferringAppBridge.java",
"java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingSettingsLauncher.java",
"java/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceFactory.java",
"java/src/org/chromium/chrome/browser/sharing/SharingJNIBridge.java",
......
......@@ -1206,6 +1206,7 @@ chrome_java_sources = [
"java/src/org/chromium/chrome/browser/rlz/RevenueStats.java",
"java/src/org/chromium/chrome/browser/rlz/RlzPingHandler.java",
"java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingPasswordReuseDialogBridge.java",
"java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingReferringAppBridge.java",
"java/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingSettingsLauncher.java",
"java/src/org/chromium/chrome/browser/safety_check/SafetyCheckUpdatesDelegateImpl.java",
"java/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceMetrics.java",
......
......@@ -197,6 +197,7 @@ chrome_junit_test_java_sources = [
"junit/src/org/chromium/chrome/browser/policy/EnterpriseInfoTest.java",
"junit/src/org/chromium/chrome/browser/privacy/settings/PrivacyPreferencesManagerImplTest.java",
"junit/src/org/chromium/chrome/browser/reengagement/ReengagementNotificationControllerTest.java",
"junit/src/org/chromium/chrome/browser/safe_browsing/SafeBrowsingReferringAppBridgeTest.java",
"junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceMetricsTest.java",
"junit/src/org/chromium/chrome/browser/search_engines/SearchEngineChoiceNotificationTest.java",
"junit/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineAdapterTest.java",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.safe_browsing;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.provider.Browser;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
import org.chromium.base.IntentUtils;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.IntentHandler.ExternalAppId;
import org.chromium.ui.base.WindowAndroid;
/**
* Bridge between Java and native SafeBrowsing code to get referring app information.
*/
public class SafeBrowsingReferringAppBridge {
private SafeBrowsingReferringAppBridge() {}
/**
* A helper class to store referring app information.
*/
static class ReferringAppInfo {
// The source of referring app name. These values must be aligned with the
// ReferringAppSource enum defined in csd.proto.
@IntDef({ReferringAppSource.REFERRING_APP_SOURCE_UNSPECIFIED,
ReferringAppSource.KNOWN_APP_ID, ReferringAppSource.UNKNOWN_APP_ID,
ReferringAppSource.ACTIVITY_REFERRER})
public @interface ReferringAppSource {
int REFERRING_APP_SOURCE_UNSPECIFIED = 0;
int KNOWN_APP_ID = 1;
int UNKNOWN_APP_ID = 2;
int ACTIVITY_REFERRER = 3;
}
private final @ReferringAppSource int mReferringAppSource;
private final String mReferringAppName;
public ReferringAppInfo(
@ReferringAppSource int referringAppSource, String referringAppName) {
mReferringAppSource = referringAppSource;
mReferringAppName = referringAppName;
}
@CalledByNative("ReferringAppInfo")
public @ReferringAppSource int getSource() {
return mReferringAppSource;
}
@CalledByNative("ReferringAppInfo")
public String getName() {
return mReferringAppName;
}
}
@CalledByNative
@VisibleForTesting
public static ReferringAppInfo getReferringAppInfo(WindowAndroid windowAndroid) {
Activity activity = windowAndroid.getActivity().get();
if (activity == null) {
return getEmptyReferringInfo();
}
Intent intent = activity.getIntent();
if (intent == null) {
return getEmptyReferringInfo();
}
@ExternalAppId
int externalId = IntentHandler.determineExternalIntentSource(intent);
if (externalId != ExternalAppId.OTHER) {
return new ReferringAppInfo(ReferringAppInfo.ReferringAppSource.KNOWN_APP_ID,
externalAppIdToString(externalId));
}
// If externalId is OTHER, fallback to EXTRA_APPLICATION_ID;
String appId = IntentUtils.safeGetStringExtra(intent, Browser.EXTRA_APPLICATION_ID);
if (appId != null) {
return new ReferringAppInfo(ReferringAppInfo.ReferringAppSource.UNKNOWN_APP_ID, appId);
}
// If appId is empty, fallback to EXTRA_REFERRER;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
Uri extraReferrer = activity.getReferrer();
if (extraReferrer != null) {
return new ReferringAppInfo(ReferringAppInfo.ReferringAppSource.ACTIVITY_REFERRER,
extraReferrer.toString());
}
}
return getEmptyReferringInfo();
}
private static String externalAppIdToString(@ExternalAppId int appId) {
switch (appId) {
case ExternalAppId.OTHER:
return "other";
case ExternalAppId.GMAIL:
return "gmail";
case ExternalAppId.FACEBOOK:
return "facebook";
case ExternalAppId.PLUS:
return "plus";
case ExternalAppId.TWITTER:
return "twitter";
case ExternalAppId.CHROME:
return "chrome";
case ExternalAppId.HANGOUTS:
return "google.hangouts";
case ExternalAppId.MESSENGER:
return "android.messages";
case ExternalAppId.NEWS:
return "google.news";
case ExternalAppId.LINE:
return "line";
case ExternalAppId.WHATSAPP:
return "whatsapp";
case ExternalAppId.GSA:
return "google.search.app";
case ExternalAppId.WEBAPK:
return "webapk";
case ExternalAppId.YAHOO_MAIL:
return "yahoo.mail";
case ExternalAppId.VIBER:
return "viber";
case ExternalAppId.YOUTUBE:
return "youtube";
default:
assert false : "not reached";
return "";
}
}
private static ReferringAppInfo getEmptyReferringInfo() {
return new ReferringAppInfo(
ReferringAppInfo.ReferringAppSource.REFERRING_APP_SOURCE_UNSPECIFIED, "");
}
}
file://chrome/browser/safe_browsing/OWNERS
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.safe_browsing;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Browser;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.app.ChromeActivity;
import org.chromium.chrome.browser.safe_browsing.SafeBrowsingReferringAppBridge.ReferringAppInfo;
import org.chromium.chrome.browser.safe_browsing.SafeBrowsingReferringAppBridge.ReferringAppInfo.ReferringAppSource;
import org.chromium.ui.base.WindowAndroid;
import java.lang.ref.WeakReference;
/**
* Unit tests for SafeBrowsingReferringAppBridge.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class SafeBrowsingReferringAppBridgeTest {
@Mock
private WindowAndroid mWindowAndroid;
@Mock
private ChromeActivity mActivity;
private WeakReference<Activity> mActivityRef;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mActivityRef = new WeakReference<>(mActivity);
when(mWindowAndroid.getActivity()).thenReturn(mActivityRef);
}
@Test
public void testFromKnownAppId() {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, IntentHandler.PACKAGE_GSA);
when(mActivity.getIntent()).thenReturn(intent);
ReferringAppInfo info = SafeBrowsingReferringAppBridge.getReferringAppInfo(mWindowAndroid);
Assert.assertEquals(ReferringAppSource.KNOWN_APP_ID, info.getSource());
Assert.assertEquals("google.search.app", info.getName());
}
@Test
public void testFromUnknownAppId() {
String packageName = "uncommon.app.name";
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Browser.EXTRA_APPLICATION_ID, packageName);
when(mActivity.getIntent()).thenReturn(intent);
ReferringAppInfo info = SafeBrowsingReferringAppBridge.getReferringAppInfo(mWindowAndroid);
Assert.assertEquals(ReferringAppSource.UNKNOWN_APP_ID, info.getSource());
Assert.assertEquals(packageName, info.getName());
}
@Test
@Config(sdk = Build.VERSION_CODES.N_MR1)
public void testFromActivityReferrerHighVersion() {
String appReferrer = "android-app://app.name/";
setAppReferrerIntent(appReferrer);
ReferringAppInfo info = SafeBrowsingReferringAppBridge.getReferringAppInfo(mWindowAndroid);
Assert.assertEquals(ReferringAppSource.ACTIVITY_REFERRER, info.getSource());
Assert.assertEquals(appReferrer, info.getName());
}
@Test
public void testFromActivityReferrerLowVersion() {
// @Config(..., sdk = Build.VERSION_CODES.LOLLIPOP) doesn't work. See crbug.com/944476.
ReflectionHelpers.setStaticField(
Build.VERSION.class, "SDK_INT", Build.VERSION_CODES.LOLLIPOP);
String appReferrer = "android-app://app.name/";
setAppReferrerIntent(appReferrer);
ReferringAppInfo info = SafeBrowsingReferringAppBridge.getReferringAppInfo(mWindowAndroid);
// The getReferrer API is not available if the version code is too low.
Assert.assertEquals(ReferringAppSource.REFERRING_APP_SOURCE_UNSPECIFIED, info.getSource());
Assert.assertEquals("", info.getName());
}
private void setAppReferrerIntent(String appReferrer) {
Uri appReferrerUri = Uri.parse(appReferrer);
Bundle extras = new Bundle();
extras.putParcelable(Intent.EXTRA_REFERRER, appReferrerUri);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtras(extras);
when(mActivity.getIntent()).thenReturn(intent);
when(mActivity.getReferrer()).thenReturn(appReferrerUri);
}
}
......@@ -3152,6 +3152,8 @@ static_library("browser") {
"reputation/safety_tip_infobar_delegate.h",
"safe_browsing/android/password_reuse_controller_android.cc",
"safe_browsing/android/password_reuse_controller_android.h",
"safe_browsing/android/safe_browsing_referring_app_bridge_android.cc",
"safe_browsing/android/safe_browsing_referring_app_bridge_android.h",
"safe_browsing/android/safe_browsing_settings_launcher_android.cc",
"safe_browsing/android/safe_browsing_settings_launcher_android.h",
"search/contextual_search_policy_handler_android.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/safe_browsing/android/safe_browsing_referring_app_bridge_android.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "chrome/android/chrome_jni_headers/SafeBrowsingReferringAppBridge_jni.h"
#include "content/public/browser/web_contents.h"
#include "ui/android/window_android.h"
using base::android::ConvertJavaStringToUTF8;
using base::android::ScopedJavaLocalRef;
using ReferringAppSource = safe_browsing::LoginReputationClientRequest::
ReferringAppInfo::ReferringAppSource;
namespace {
ReferringAppSource IntToReferringAppSource(int source) {
return static_cast<ReferringAppSource>(source);
}
} // namespace
namespace safe_browsing {
LoginReputationClientRequest::ReferringAppInfo GetReferringAppInfo(
content::WebContents* web_contents) {
ui::WindowAndroid* window_android = web_contents->GetTopLevelNativeWindow();
JNIEnv* env = base::android::AttachCurrentThread();
ScopedJavaLocalRef<jobject> j_info =
Java_SafeBrowsingReferringAppBridge_getReferringAppInfo(
env, window_android->GetJavaObject());
ReferringAppSource source =
IntToReferringAppSource(Java_ReferringAppInfo_getSource(env, j_info));
std::string name =
ConvertJavaStringToUTF8(Java_ReferringAppInfo_getName(env, j_info));
LoginReputationClientRequest::ReferringAppInfo referring_app_info;
referring_app_info.set_referring_app_source(source);
referring_app_info.set_referring_app_name(name);
return referring_app_info;
}
} // namespace safe_browsing
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_SAFE_BROWSING_ANDROID_SAFE_BROWSING_REFERRING_APP_BRIDGE_ANDROID_H_
#define CHROME_BROWSER_SAFE_BROWSING_ANDROID_SAFE_BROWSING_REFERRING_APP_BRIDGE_ANDROID_H_
#include <string>
#include "components/safe_browsing/core/proto/csd.pb.h"
namespace content {
class WebContents;
}
namespace safe_browsing {
// Get referring app info from Java side.
LoginReputationClientRequest::ReferringAppInfo GetReferringAppInfo(
content::WebContents* web_contents);
} // namespace safe_browsing
#endif // CHROME_BROWSER_SAFE_BROWSING_ANDROID_SAFE_BROWSING_REFERRING_APP_BRIDGE_ANDROID_H_
......@@ -90,6 +90,7 @@
#if defined(OS_ANDROID)
#include "chrome/browser/safe_browsing/android/password_reuse_controller_android.h"
#include "chrome/browser/safe_browsing/android/safe_browsing_referring_app_bridge_android.h"
#else
#include "chrome/browser/ui/browser_list.h"
#endif
......@@ -1748,6 +1749,14 @@ void ChromePasswordProtectionService::RemovePhishedSavedPasswordCredential(
}
}
#if defined(OS_ANDROID)
LoginReputationClientRequest::ReferringAppInfo
ChromePasswordProtectionService::GetReferringAppInfo(
content::WebContents* web_contents) {
return safe_browsing::GetReferringAppInfo(web_contents);
}
#endif
password_manager::PasswordStore*
ChromePasswordProtectionService::GetProfilePasswordStore() const {
// Always use EXPLICIT_ACCESS as the password manager checks IsIncognito
......
......@@ -239,6 +239,11 @@ class ChromePasswordProtectionService : public PasswordProtectionService {
const std::vector<password_manager::MatchingReusedCredential>&
matching_reused_credentials) override;
#if defined(OS_ANDROID)
LoginReputationClientRequest::ReferringAppInfo GetReferringAppInfo(
content::WebContents* web_contents) override;
#endif
// Returns the profile PasswordStore associated with this instance.
password_manager::PasswordStore* GetProfilePasswordStore() const;
......
......@@ -64,6 +64,9 @@ class MockPasswordProtectionService : public PasswordProtectionService {
MOCK_METHOD1(
RemovePhishedSavedPasswordCredential,
void(const std::vector<password_manager::MatchingReusedCredential>&));
MOCK_METHOD1(
GetReferringAppInfo,
LoginReputationClientRequest::ReferringAppInfo(content::WebContents*));
MOCK_METHOD2(IsPingingEnabled,
bool(LoginReputationClientRequest::TriggerType,
ReusedPasswordAccountType));
......
......@@ -277,6 +277,12 @@ void PasswordProtectionRequest::FillRequestProto(bool is_sampled_ping) {
}
#endif // BUILDFLAG(FULL_SAFE_BROWSING)
#if defined(OS_ANDROID)
LoginReputationClientRequest::ReferringAppInfo referring_app_info =
password_protection_service_->GetReferringAppInfo(web_contents_);
*request_proto_->mutable_referring_app_info() = std::move(referring_app_info);
#endif // defined(OS_ANDROID)
switch (trigger_type_) {
case LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE: {
LoginReputationClientRequest::Frame::Form* password_form;
......
......@@ -218,6 +218,12 @@ class PasswordProtectionService : public history::HistoryServiceObserver {
const std::vector<password_manager::MatchingReusedCredential>&
matching_reused_credentials) = 0;
#if defined(OS_ANDROID)
// Returns the referring app info that starts the activity.
virtual LoginReputationClientRequest::ReferringAppInfo GetReferringAppInfo(
content::WebContents* web_contents) = 0;
#endif
// Converts from password::metrics_util::PasswordType to
// LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType.
static ReusedPasswordType GetPasswordProtectionReusedPasswordType(
......
......@@ -1152,6 +1152,32 @@ base::Value SerializeUrlDisplayExperiment(
return std::move(d);
}
base::Value SerializeReferringAppInfo(
const LoginReputationClientRequest::ReferringAppInfo& info) {
base::DictionaryValue dict;
std::string source;
switch (info.referring_app_source()) {
case LoginReputationClientRequest::ReferringAppInfo::
REFERRING_APP_SOURCE_UNSPECIFIED:
source = "REFERRING_APP_SOURCE_UNSPECIFIED";
break;
case LoginReputationClientRequest::ReferringAppInfo::KNOWN_APP_ID:
source = "KNOWN_APP_ID";
break;
case LoginReputationClientRequest::ReferringAppInfo::UNKNOWN_APP_ID:
source = "UNKNOWN_APP_ID";
break;
case LoginReputationClientRequest::ReferringAppInfo::ACTIVITY_REFERRER:
source = "ACTIVITY_REFERRER";
break;
}
dict.SetString("referring_app_source", source);
dict.SetString("referring_app_info", info.referring_app_name());
return std::move(dict);
}
std::string SerializePGPing(const LoginReputationClientRequest& request) {
base::DictionaryValue request_dict;
......@@ -1208,6 +1234,12 @@ std::string SerializePGPing(const LoginReputationClientRequest& request) {
SerializeUrlDisplayExperiment(request.url_display_experiment()));
}
if (request.has_referring_app_info()) {
request_dict.SetKey(
"referring_app_info",
SerializeReferringAppInfo(request.referring_app_info()));
}
std::string request_serialized;
JSONStringValueSerializer serializer(&request_serialized);
serializer.set_pretty_print(true);
......
......@@ -370,6 +370,38 @@ message LoginReputationClientRequest {
optional bool simplified_url_display_enabled = 6;
};
optional UrlDisplayExperiment url_display_experiment = 14;
// The referring app info that launches Chrome. Only used on Android.
// This information may be spoofed so do not rely solely on its correctness
// for anti-abuse purposes. For more details, see:
// go/phishguard-referring-app-android.
message ReferringAppInfo {
// The source of the referring_app_name.
enum ReferringAppSource {
REFERRING_APP_SOURCE_UNSPECIFIED = 0;
// The referring app is known to Chrome. If the referring app info is from
// this source, the referring_app_name is a hardcoded string that is
// predefined in Chrome.
KNOWN_APP_ID = 1;
// The referring app is unknown to Chrome. The referring app name is
// obtained from
// https://developer.android.com/reference/android/provider/Browser?hl=en#EXTRA_APPLICATION_ID.
// If the referring app info is from this source, the referring_app_name
// is volatile and can be changed by the referring app.
UNKNOWN_APP_ID = 2;
// The referring app info is obtained from
// https://developer.android.com/reference/android/app/Activity#getReferrer().
// If the referring app info is from this source, the referring_app_name
// is volatile and can be changed by the referring app.
ACTIVITY_REFERRER = 3;
}
optional ReferringAppSource referring_app_source = 1;
optional string referring_app_name = 2;
}
optional ReferringAppInfo referring_app_info = 15;
}
// The message is used for client response for login reputation requests.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment