Commit fe66aae8 authored by Friedrich Horschig's avatar Friedrich Horschig Committed by Commit Bot

[PwdCheckAndroid] Display reason for double-compromised credentials

This CL ensures that credentials who are compromised in two ways display
both reasons in the list.

Translation screenshot for
https://storage.cloud.google.com/chromium-translation-screenshots/cf6c7eaf298250e9e14f7c244dacb7e11f07dd47

Bug: 1117489, 1092444
Change-Id: I214e5e2211d1732cb5fa745a582cc64a37890293
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2362616
Commit-Queue: Friedrich [CET] <fhorschig@chromium.org>
Reviewed-by: default avatarIoana Pandele <ioanap@chromium.org>
Cr-Commit-Position: refs/heads/master@{#799676}
parent bfc76b3e
...@@ -62,7 +62,7 @@ class PasswordCheckBridge { ...@@ -62,7 +62,7 @@ class PasswordCheckBridge {
assert password != null; assert password != null;
mPasswordCheckObserver.onCompromisedCredentialFound(new CompromisedCredential(signonRealm, mPasswordCheckObserver.onCompromisedCredentialFound(new CompromisedCredential(signonRealm,
origin, username, displayOrigin, displayUsername, password, passwordChangeUrl, origin, username, displayOrigin, displayUsername, password, passwordChangeUrl,
associatedApp, false, hasScript)); associatedApp, true, false, hasScript));
} }
@CalledByNative @CalledByNative
...@@ -84,9 +84,10 @@ class PasswordCheckBridge { ...@@ -84,9 +84,10 @@ class PasswordCheckBridge {
private static void insertCredential(CompromisedCredential[] credentials, int index, private static void insertCredential(CompromisedCredential[] credentials, int index,
String signonRealm, GURL origin, String username, String displayOrigin, String signonRealm, GURL origin, String username, String displayOrigin,
String displayUsername, String password, String passwordChangeUrl, String associatedApp, String displayUsername, String password, String passwordChangeUrl, String associatedApp,
boolean phished, boolean hasScript) { boolean leaked, boolean phished, boolean hasScript) {
credentials[index] = new CompromisedCredential(signonRealm, origin, username, displayOrigin, credentials[index] = new CompromisedCredential(signonRealm, origin, username, displayOrigin,
displayUsername, password, passwordChangeUrl, associatedApp, phished, hasScript); displayUsername, password, passwordChangeUrl, associatedApp, leaked, phished,
hasScript);
} }
/** /**
......
...@@ -29,6 +29,7 @@ import android.widget.LinearLayout; ...@@ -29,6 +29,7 @@ import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.ColorRes; import androidx.annotation.ColorRes;
import androidx.annotation.StringRes;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.graphics.drawable.DrawableCompat; import androidx.core.graphics.drawable.DrawableCompat;
...@@ -134,9 +135,7 @@ class PasswordCheckViewBinder { ...@@ -134,9 +135,7 @@ class PasswordCheckViewBinder {
username.setText(credential.getDisplayUsername()); username.setText(credential.getDisplayUsername());
TextView reason = view.findViewById(R.id.compromised_reason); TextView reason = view.findViewById(R.id.compromised_reason);
reason.setText(credential.isPhished() reason.setText(getReasonForCredential(credential));
? R.string.password_check_credential_row_reason_phished
: R.string.password_check_credential_row_reason_leaked);
ListMenuButton more = view.findViewById(R.id.credential_menu_button); ListMenuButton more = view.findViewById(R.id.credential_menu_button);
more.setDelegate(() -> { more.setDelegate(() -> {
...@@ -172,6 +171,12 @@ class PasswordCheckViewBinder { ...@@ -172,6 +171,12 @@ class PasswordCheckViewBinder {
} }
} }
private static @StringRes int getReasonForCredential(CompromisedCredential credential) {
if (!credential.isPhished()) return R.string.password_check_credential_row_reason_leaked;
if (!credential.isLeaked()) return R.string.password_check_credential_row_reason_phished;
return R.string.password_check_credential_row_reason_leaked_and_phished;
}
/** /**
* Called whenever a property in the given model changes. It updates the given view * Called whenever a property in the given model changes. It updates the given view
* accordingly. * accordingly.
......
...@@ -187,6 +187,9 @@ ...@@ -187,6 +187,9 @@
<message name="IDS_PASSWORD_CHECK_CREDENTIAL_ROW_REASON_PHISHED" desc="Small description explaining that a credential is compromised because it was entered on a deceptive site."> <message name="IDS_PASSWORD_CHECK_CREDENTIAL_ROW_REASON_PHISHED" desc="Small description explaining that a credential is compromised because it was entered on a deceptive site.">
Entered on a deceptive site Entered on a deceptive site
</message> </message>
<message name="IDS_PASSWORD_CHECK_CREDENTIAL_ROW_REASON_LEAKED_AND_PHISHED" desc="Small description explaining that a credential is compromised because it was entered on a deceptive site and in addition, was part of a data breach.">
Entered on a deceptive site and found in data breach
</message>
<message name="IDS_PASSWORD_CHECK_CREDENTIAL_ROW_SCRIPT_BUTTON_EXPLANATION" desc="Small description explaining that an automated password change can be done with a script."> <message name="IDS_PASSWORD_CHECK_CREDENTIAL_ROW_SCRIPT_BUTTON_EXPLANATION" desc="Small description explaining that an automated password change can be done with a script.">
Let Google Assistant help you change your password Let Google Assistant help you change your password
</message> </message>
......
...@@ -30,14 +30,15 @@ public class CompromisedCredential implements Parcelable { ...@@ -30,14 +30,15 @@ public class CompromisedCredential implements Parcelable {
final String password = in.readString(); final String password = in.readString();
final String passwordChangeUrl = in.readString(); final String passwordChangeUrl = in.readString();
final String associatedApp = in.readString(); final String associatedApp = in.readString();
boolean[] boolArguments = new boolean[2]; boolean[] boolArguments = new boolean[3];
in.readBooleanArray(boolArguments); in.readBooleanArray(boolArguments);
final boolean phished = boolArguments[0]; final boolean leaked = boolArguments[0];
final boolean hasScript = boolArguments[1]; final boolean phished = boolArguments[1];
final boolean hasScript = boolArguments[2];
return new CompromisedCredential(signonRealm, origin, username, displayOrigin, return new CompromisedCredential(signonRealm, origin, username, displayOrigin,
displayUsername, password, passwordChangeUrl, associatedApp, phished, displayUsername, password, passwordChangeUrl, associatedApp, leaked,
hasScript); phished, hasScript);
} }
@Override @Override
...@@ -54,6 +55,7 @@ public class CompromisedCredential implements Parcelable { ...@@ -54,6 +55,7 @@ public class CompromisedCredential implements Parcelable {
private final String mPassword; private final String mPassword;
private final String mPasswordChangeUrl; private final String mPasswordChangeUrl;
private final String mAssociatedApp; private final String mAssociatedApp;
private final boolean mLeaked;
private final boolean mPhished; private final boolean mPhished;
private final boolean mHasScript; private final boolean mHasScript;
...@@ -72,7 +74,7 @@ public class CompromisedCredential implements Parcelable { ...@@ -72,7 +74,7 @@ public class CompromisedCredential implements Parcelable {
*/ */
public CompromisedCredential(String signonRealm, GURL origin, String username, public CompromisedCredential(String signonRealm, GURL origin, String username,
String displayOrigin, String displayUsername, String password, String passwordChangeUrl, String displayOrigin, String displayUsername, String password, String passwordChangeUrl,
String associatedApp, boolean phished, boolean hasScript) { String associatedApp, boolean leaked, boolean phished, boolean hasScript) {
assert origin != null : "Credential origin is null! Pass an empty one instead."; assert origin != null : "Credential origin is null! Pass an empty one instead.";
assert signonRealm != null; assert signonRealm != null;
assert passwordChangeUrl != null : "Change URL may be empty but not null!"; assert passwordChangeUrl != null : "Change URL may be empty but not null!";
...@@ -80,6 +82,7 @@ public class CompromisedCredential implements Parcelable { ...@@ -80,6 +82,7 @@ public class CompromisedCredential implements Parcelable {
assert !passwordChangeUrl.isEmpty() assert !passwordChangeUrl.isEmpty()
|| !associatedApp.isEmpty() || !associatedApp.isEmpty()
: "Change URL and app name may not be empty at the same time!"; : "Change URL and app name may not be empty at the same time!";
assert leaked || phished : "A compromised credential must be leaked or phished!";
mSignonRealm = signonRealm; mSignonRealm = signonRealm;
mOrigin = origin; mOrigin = origin;
mUsername = username; mUsername = username;
...@@ -88,6 +91,7 @@ public class CompromisedCredential implements Parcelable { ...@@ -88,6 +91,7 @@ public class CompromisedCredential implements Parcelable {
mPassword = password; mPassword = password;
mPasswordChangeUrl = passwordChangeUrl; mPasswordChangeUrl = passwordChangeUrl;
mAssociatedApp = associatedApp; mAssociatedApp = associatedApp;
mLeaked = leaked;
mPhished = phished; mPhished = phished;
mHasScript = hasScript; mHasScript = hasScript;
} }
...@@ -114,16 +118,18 @@ public class CompromisedCredential implements Parcelable { ...@@ -114,16 +118,18 @@ public class CompromisedCredential implements Parcelable {
public String getDisplayOrigin() { public String getDisplayOrigin() {
return mDisplayOrigin; return mDisplayOrigin;
} }
public boolean isPhished() {
return mPhished;
}
public String getAssociatedApp() { public String getAssociatedApp() {
return mAssociatedApp; return mAssociatedApp;
} }
public String getPasswordChangeUrl() { public String getPasswordChangeUrl() {
return mPasswordChangeUrl; return mPasswordChangeUrl;
} }
public boolean isLeaked() {
return mLeaked;
}
public boolean isPhished() {
return mPhished;
}
public boolean hasScript() { public boolean hasScript() {
return mHasScript; return mHasScript;
} }
...@@ -138,8 +144,8 @@ public class CompromisedCredential implements Parcelable { ...@@ -138,8 +144,8 @@ public class CompromisedCredential implements Parcelable {
&& mDisplayUsername.equals(that.mDisplayUsername) && mDisplayUsername.equals(that.mDisplayUsername)
&& mPassword.equals(that.mPassword) && mPassword.equals(that.mPassword)
&& mPasswordChangeUrl.equals(that.mPasswordChangeUrl) && mPasswordChangeUrl.equals(that.mPasswordChangeUrl)
&& mAssociatedApp.equals(that.mAssociatedApp) && mPhished == that.mPhished && mAssociatedApp.equals(that.mAssociatedApp) && mLeaked == that.mLeaked
&& mHasScript == that.mHasScript; && mPhished == that.mPhished && mHasScript == that.mHasScript;
} }
@Override @Override
...@@ -149,15 +155,15 @@ public class CompromisedCredential implements Parcelable { ...@@ -149,15 +155,15 @@ public class CompromisedCredential implements Parcelable {
+ ", username='" + mUsername + '\'' + ", displayOrigin='" + mDisplayOrigin + '\'' + ", username='" + mUsername + '\'' + ", displayOrigin='" + mDisplayOrigin + '\''
+ ", displayUsername='" + mDisplayUsername + '\'' + ", password='" + mPassword + ", displayUsername='" + mDisplayUsername + '\'' + ", password='" + mPassword
+ '\'' + ", passwordChangeUrl='" + mPasswordChangeUrl + '\'' + ", associatedApp='" + '\'' + ", passwordChangeUrl='" + mPasswordChangeUrl + '\'' + ", associatedApp='"
+ mAssociatedApp + '\'' + ", phished=" + mPhished + ", hasScript=" + mHasScript + mAssociatedApp + '\'' + ", leaked=" + mLeaked + ", phished=" + mPhished
+ '}'; + ", hasScript=" + mHasScript + '}';
} }
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(mSignonRealm, mOrigin.getPossiblyInvalidSpec(), mUsername, return Objects.hash(mSignonRealm, mOrigin.getPossiblyInvalidSpec(), mUsername,
mDisplayOrigin, mDisplayUsername, mPassword, mPasswordChangeUrl, mAssociatedApp, mDisplayOrigin, mDisplayUsername, mPassword, mPasswordChangeUrl, mAssociatedApp,
mPhished, mHasScript); mLeaked, mPhished, mHasScript);
} }
@Override @Override
...@@ -170,7 +176,7 @@ public class CompromisedCredential implements Parcelable { ...@@ -170,7 +176,7 @@ public class CompromisedCredential implements Parcelable {
parcel.writeString(mPassword); parcel.writeString(mPassword);
parcel.writeString(mPasswordChangeUrl); parcel.writeString(mPasswordChangeUrl);
parcel.writeString(mAssociatedApp); parcel.writeString(mAssociatedApp);
parcel.writeBooleanArray(new boolean[] {mPhished, mHasScript}); parcel.writeBooleanArray(new boolean[] {mLeaked, mPhished, mHasScript});
} }
@Override @Override
......
...@@ -68,7 +68,7 @@ public class PasswordCheckEditViewTest { ...@@ -68,7 +68,7 @@ public class PasswordCheckEditViewTest {
private static final CompromisedCredential ANA = private static final CompromisedCredential ANA =
new CompromisedCredential("https://some-url.com/signin", new CompromisedCredential("https://some-url.com/signin",
new GURL("https://some-url.com/"), "Ana", "some-url.com", "Ana", "password", new GURL("https://some-url.com/"), "Ana", "some-url.com", "Ana", "password",
"https://some-url.com/.well-known/change-password", "", false, false); "https://some-url.com/.well-known/change-password", "", true, false, false);
private PasswordCheckEditFragmentView mPasswordCheckEditView; private PasswordCheckEditFragmentView mPasswordCheckEditView;
......
...@@ -95,18 +95,23 @@ public class PasswordCheckViewTest { ...@@ -95,18 +95,23 @@ public class PasswordCheckViewTest {
private static final CompromisedCredential ANA = private static final CompromisedCredential ANA =
new CompromisedCredential("https://some-url.com/signin", new CompromisedCredential("https://some-url.com/signin",
new GURL("https://some-url.com/"), "Ana", "some-url.com", "Ana", "password", new GURL("https://some-url.com/"), "Ana", "some-url.com", "Ana", "password",
"https://some-url.com/.well-known/change-password", "", false, false); "https://some-url.com/.well-known/change-password", "", true, false, false);
private static final CompromisedCredential PHISHED = private static final CompromisedCredential PHISHED =
new CompromisedCredential("http://example.com/signin", new GURL("http://example.com/"), new CompromisedCredential("http://example.com/signin", new GURL("http://example.com/"),
"", "http://example.com", "(No username)", "DoSomething", "", "http://example.com", "(No username)", "DoSomething",
"http://example.com/.well-known/change-password", "", true, false); "http://example.com/.well-known/change-password", "", false, true, false);
private static final CompromisedCredential LEAKED = private static final CompromisedCredential LEAKED =
new CompromisedCredential("https://some-other-url.com/signin", new CompromisedCredential("https://some-other-url.com/signin",
new GURL("https://some-other-url.com/"), "AZiegler", "some-other-url.com", new GURL("https://some-other-url.com/"), "AZiegler", "some-other-url.com",
"AZiegler", "N0M3rcy", "", "com.other.package", false, false); "AZiegler", "N0M3rcy", "", "com.other.package", true, false, false);
private static final CompromisedCredential SCRIPTED = new CompromisedCredential( private static final CompromisedCredential LEAKED_AND_PHISHED =
"https://script.com/signin", new GURL("https://script.com/"), "Charlie", "script.com", new CompromisedCredential("https://super-important.com/signin",
"Charlie", "secret", "https://script.com/.well-known/change-password", "", false, true); new GURL("https://super-important.com/"), "HSong", "super-important.com",
"HSong", "N3rfTh1s", "", "com.important.super", true, true, false);
private static final CompromisedCredential SCRIPTED =
new CompromisedCredential("https://script.com/signin", new GURL("https://script.com/"),
"Charlie", "script.com", "Charlie", "secret",
"https://script.com/.well-known/change-password", "", true, false, true);
private static final int LEAKS_COUNT = 2; private static final int LEAKS_COUNT = 2;
...@@ -398,8 +403,9 @@ public class PasswordCheckViewTest { ...@@ -398,8 +403,9 @@ public class PasswordCheckViewTest {
runOnUiThreadBlocking(() -> { runOnUiThreadBlocking(() -> {
mModel.get(ITEMS).add(buildCredentialItem(PHISHED)); mModel.get(ITEMS).add(buildCredentialItem(PHISHED));
mModel.get(ITEMS).add(buildCredentialItem(LEAKED)); mModel.get(ITEMS).add(buildCredentialItem(LEAKED));
mModel.get(ITEMS).add(buildCredentialItem(LEAKED_AND_PHISHED));
}); });
waitForListViewToHaveLength(2); waitForListViewToHaveLength(3);
// The phished credential is rendered first: // The phished credential is rendered first:
assertThat(getCredentialOriginAt(0).getText(), is(PHISHED.getDisplayOrigin())); assertThat(getCredentialOriginAt(0).getText(), is(PHISHED.getDisplayOrigin()));
...@@ -416,6 +422,14 @@ public class PasswordCheckViewTest { ...@@ -416,6 +422,14 @@ public class PasswordCheckViewTest {
is(getString(R.string.password_check_credential_row_reason_leaked))); is(getString(R.string.password_check_credential_row_reason_leaked)));
assertThat(getCredentialChangeButtonAt(1).getVisibility(), is(View.VISIBLE)); assertThat(getCredentialChangeButtonAt(1).getVisibility(), is(View.VISIBLE));
assertThat(getCredentialChangeHintAt(1).getVisibility(), is(View.GONE)); assertThat(getCredentialChangeHintAt(1).getVisibility(), is(View.GONE));
// The leaked and phished credential is rendered third:
assertThat(getCredentialOriginAt(2).getText(), is(LEAKED_AND_PHISHED.getDisplayOrigin()));
assertThat(getCredentialUserAt(2).getText(), is(LEAKED_AND_PHISHED.getDisplayUsername()));
assertThat(getCredentialReasonAt(2).getText(),
is(getString(R.string.password_check_credential_row_reason_leaked_and_phished)));
assertThat(getCredentialChangeButtonAt(2).getVisibility(), is(View.VISIBLE));
assertThat(getCredentialChangeHintAt(2).getVisibility(), is(View.GONE));
} }
@Test @Test
......
...@@ -71,10 +71,10 @@ import org.chromium.url.GURL; ...@@ -71,10 +71,10 @@ import org.chromium.url.GURL;
public class PasswordCheckControllerTest { public class PasswordCheckControllerTest {
private static final CompromisedCredential ANA = private static final CompromisedCredential ANA =
new CompromisedCredential("https://m.a.xyz/signin", mock(GURL.class), "Ana", "m.a.xyz", new CompromisedCredential("https://m.a.xyz/signin", mock(GURL.class), "Ana", "m.a.xyz",
"Ana", "password", "", "xyz.a.some.package", false, false); "Ana", "password", "", "xyz.a.some.package", true, false, false);
private static final CompromisedCredential BOB = new CompromisedCredential( private static final CompromisedCredential BOB = new CompromisedCredential(
"http://www.b.ch/signin", mock(GURL.class), "", "http://www.b.ch", "(No username)", "http://www.b.ch/signin", mock(GURL.class), "", "http://www.b.ch", "(No username)",
"DoneSth", "http://www.b.ch/.well-known/change-password", "", false, true); "DoneSth", "http://www.b.ch/.well-known/change-password", "", true, false, true);
@Rule @Rule
public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor(); public TestRule mFeaturesProcessorRule = new Features.JUnitProcessor();
......
...@@ -83,6 +83,8 @@ void PasswordCheckBridge::GetCompromisedCredentials( ...@@ -83,6 +83,8 @@ void PasswordCheckBridge::GetCompromisedCredentials(
base::android::ConvertUTF8ToJavaString(env, base::android::ConvertUTF8ToJavaString(env,
credential.change_password_url), credential.change_password_url),
base::android::ConvertUTF8ToJavaString(env, credential.package_name), base::android::ConvertUTF8ToJavaString(env, credential.package_name),
(credential.compromise_type ==
password_manager::CompromiseTypeFlags::kCredentialLeaked),
(credential.compromise_type == (credential.compromise_type ==
password_manager::CompromiseTypeFlags::kCredentialPhished), password_manager::CompromiseTypeFlags::kCredentialPhished),
credential.has_script); credential.has_script);
......
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