diff --git a/chrome/credential_provider/gaiacp/gaia_credential_base.cc b/chrome/credential_provider/gaiacp/gaia_credential_base.cc index 531e1029136f2..289ba892cc2e1 100644 --- a/chrome/credential_provider/gaiacp/gaia_credential_base.cc +++ b/chrome/credential_provider/gaiacp/gaia_credential_base.cc @@ -2499,7 +2499,22 @@ HRESULT CGaiaCredentialBase::OnUserAuthenticated(BSTR authentication_info, } const std::wstring email = GetDictString(*properties, kKeyEmail); - const std::wstring email_domain = email.substr(email.find(L"@") + 1); + // SECURITY: |email| comes from the GLS process via stdout pipe and is + // attacker-controlled if that process is compromised. If it contains no + // '@', find() returns npos; npos+1 wraps to 0 and substr(0) would return + // the entire string, letting an email of "" bypass the + // allowlist below. Reject malformed emails before extracting the domain. + const std::wstring::size_type at_pos = email.find(L'@'); + if (at_pos == std::wstring::npos || at_pos == 0 || + at_pos + 1 == email.length()) { + LOGFN(ERROR) << "Email '" << email << "' is malformed (missing '@' or " + << "empty local-part/domain)."; + *status_text = + CGaiaCredentialBase::AllocErrorString(IDS_INVALID_EMAIL_DOMAIN_BASE); + SecurelyClearDictionaryValue(properties); + return E_FAIL; + } + const std::wstring email_domain = email.substr(at_pos + 1); const std::vector allowed_domains = GetEmailDomainsList(); if (!std::ranges::contains(allowed_domains, email_domain)) {