// Standalone demonstration of the core arithmetic bug at
// chrome/credential_provider/gaiacp/gaia_credential_base.cc:2502
//
//   const std::wstring email_domain = email.substr(email.find(L"@") + 1);
//
// When email contains no '@', wstring::find returns npos (== SIZE_MAX).
// SIZE_MAX + 1 wraps to 0 via unsigned arithmetic. substr(0) returns the
// entire input string. If the attacker sets email = "<allowed-domain>"
// (no '@'), email_domain == "<allowed-domain>" and the allowlist check
// at :2505 passes.
//
// This file has zero Chromium dependencies and compiles on any platform:
//   clang++ -std=c++20 -fsanitize=undefined,integer npos_wrap_demo.cc -o demo
//   ./demo
//
// Note: -fsanitize=integer DOES flag the unsigned wrap (it reports
// well-defined-but-suspicious unsigned overflow). Chromium's GCPW build
// does not ship with the integer sanitizer, so this is silent in
// production. -fsanitize=undefined alone does NOT flag it.

#include <algorithm>
#include <cstdio>
#include <string>
#include <vector>

// std::ranges::contains is C++23. Chromium uses it via libc++; for
// portability we provide a tiny mirror with identical semantics.
namespace poc_ranges {
template <class R, class T>
bool contains(const R& r, const T& v) {
  return std::find(r.begin(), r.end(), v) != r.end();
}
}  // namespace poc_ranges

// Mirror of the production code path, std::wstring identical to source.
static std::wstring extract_domain_buggy(const std::wstring& email) {
  // EXACT copy of gaia_credential_base.cc:2502
  return email.substr(email.find(L"@") + 1);
}

static std::wstring extract_domain_fixed(const std::wstring& email) {
  std::wstring::size_type at = email.find(L'@');
  if (at == std::wstring::npos) {
    return L"";  // no domain
  }
  return email.substr(at + 1);
}

int main() {
  // Enterprise admin configured: domains_allowed_to_login = "contoso.com"
  std::vector<std::wstring> allowed_domains = {L"contoso.com"};

  // Attacker (compromised GLS process) writes email = "contoso.com"
  // to the stdout pipe -- no '@' character.
  std::wstring attacker_email = L"contoso.com";

  // ---- Reproduce the wrap ----
  std::wstring::size_type pos = attacker_email.find(L"@");
  printf("attacker_email          = \"%ls\"\n", attacker_email.c_str());
  printf("email.find(L\"@\")        = %zu  (npos = %zu)\n",
         pos, std::wstring::npos);
  printf("email.find(L\"@\") + 1    = %zu  <-- unsigned wrap to 0\n",
         pos + 1);

  std::wstring email_domain = extract_domain_buggy(attacker_email);
  printf("email.substr(...)       = \"%ls\"\n", email_domain.c_str());

  // Mirror of gaia_credential_base.cc:2505
  bool buggy_pass = poc_ranges::contains(allowed_domains, email_domain);
  printf("\n[BUGGY ] allowlist check: %s\n",
         buggy_pass ? "PASS  (attacker bypasses domain restriction)"
                    : "BLOCK");

  // ---- Sanity: what the fixed version does ----
  std::wstring fixed_domain = extract_domain_fixed(attacker_email);
  bool fixed_pass = poc_ranges::contains(allowed_domains, fixed_domain);
  printf("[FIXED ] allowlist check: %s  (domain = \"%ls\")\n",
         fixed_pass ? "PASS" : "BLOCK", fixed_domain.c_str());

  // ---- Sanity: a normal email still works in buggy code ----
  std::wstring normal = L"alice@contoso.com";
  std::wstring normal_domain = extract_domain_buggy(normal);
  printf("\n[normal] \"%ls\" -> domain \"%ls\" (sanity OK)\n",
         normal.c_str(), normal_domain.c_str());

  if (buggy_pass && !fixed_pass) {
    printf("\n==> BYPASS CONFIRMED: npos+1 wrap lets @-less email "
           "through allowlist.\n");
    return 0;
  }
  printf("\n==> unexpected result\n");
  return 1;
}
