diff --git a/ios/chrome/browser/safe_browsing/model/password_protection_egtest.mm b/ios/chrome/browser/safe_browsing/model/password_protection_egtest.mm index 70a58a96b7c41..e0712200a8dee 100644 --- a/ios/chrome/browser/safe_browsing/model/password_protection_egtest.mm +++ b/ios/chrome/browser/safe_browsing/model/password_protection_egtest.mm @@ -61,7 +61,9 @@ std::unique_ptr HandleRequest( std::string("--mark_as_allowlisted_for_phish_guard=") + _allowlistedURL.spec()); - if ([self isRunningTest:@selector(testPasswordReuseDetectionWarning)]) { + if ([self isRunningTest:@selector(testPasswordReuseDetectionWarning)] || + [self isRunningTest:@selector + (testPasswordReuseDetectionWarningNonAsciiPassword)]) { // Use commandline args to save a fake phishing cached verdict. config.additional_args.push_back( std::string("--mark_as_phish_guard_phishing=") + _phishingURL.spec()); @@ -116,6 +118,45 @@ std::unique_ptr HandleRequest( kWaitForUIElementTimeout]; } +// Regression test: PhishGuard must fire for passwords containing non-ASCII +// characters. PasswordProtectionJavaScriptFeature::ScriptMessageReceived checks +// `(*text).size() > 1` on a UTF-8 std::string, which rejects every code point +// >= U+0080 (2-4 UTF-8 bytes) even though the JS-side filter +// `event.key.length === 1` correctly forwarded a single keystroke. This test +// FAILS while the bug is present (the warning never appears) and passes once +// the byte-length check is fixed to count code points instead. +- (void)testPasswordReuseDetectionWarningNonAsciiPassword { + // Replace the saved credential with one containing U+00DF 'ß'. + GREYAssertTrue([PasswordManagerAppInterface clearCredentials], + @"Clearing credentials wasn't done."); + NSURL* URL = [NSURL URLWithString:@"http://www.example.com"]; + [PasswordManagerAppInterface storeCredentialWithUsername:@"Username" + password:@"Paßword" + URL:URL]; + GREYAssertEqual(1, [PasswordManagerAppInterface storedCredentialsCount], + @"There should be one credential."); + + [ChromeEarlGrey loadURL:_phishingURL]; + [ChromeEarlGrey waitForWebStateContainingText:kInputPage]; + + [[EarlGrey selectElementWithMatcher:chrome_test_util::WebViewMatcher()] + performAction:chrome_test_util::TapWebElementWithId(kInputElement)]; + [ChromeEarlGrey simulatePhysicalKeyboardEvent:@"P" flags:UIKeyModifierShift]; + // 'ß' is a single keystroke on a German hardware layout; emitted as one + // keypress with event.key == "ß" (UTF-16 length 1, UTF-8 length 2). + for (NSString* character in @[ @"a", @"ß", @"w", @"o", @"r", @"d" ]) { + [ChromeEarlGrey simulatePhysicalKeyboardEvent:character flags:0]; + } + + // EXPECTED once fixed: warning appears. + // ACTUAL with bug: times out — 'ß' was dropped at the size()>1 check, so + // PasswordReuseDetectionManager only saw "Paword" and never matched. + [ChromeEarlGrey + waitForUIElementToAppearWithMatcher:PasswordProtectionMatcher() + timeout:base::test::ios:: + kWaitForUIElementTimeout]; +} + // Tests that password protection UI is not shown when saved password is reused // on safe site. - (void)testPasswordProtectionNotShownForAllowListedURL {