Skip to content

feat(emulate): Add Chrome 150, 151, 152 - #107

Open
Soju06 wants to merge 2 commits into
0x676e67:mainfrom
Soju06:feat/chrome-150-151-152
Open

feat(emulate): Add Chrome 150, 151, 152#107
Soju06 wants to merge 2 commits into
0x676e67:mainfrom
Soju06:feat/chrome-150-151-152

Conversation

@Soju06

@Soju06 Soju06 commented Aug 19, 2026

Copy link
Copy Markdown

Adds Chrome 150, 151 and 152 to the emulation profiles and to the weighted
version selector (newest first), following the shape of #99.

sec-ch-ua brand strings

The GREASE token and the brand ordering are version-seeded, so they cannot be
extrapolated from 149. Each value was derived from Chromium's
GenerateBrandVersionList and then confirmed against a real binary:

version sec-ch-ua
149 "Google Chrome";v="149", "Chromium";v="149", "Not)A;Brand";v="24"
150 "Not;A=Brand";v="8", "Chromium";v="150", "Google Chrome";v="150"
151 "Not=A?Brand";v="99", "Google Chrome";v="151", "Chromium";v="151"
152 "Chromium";v="152", "Not?A_Brand";v="24", "Google Chrome";v="152"

Verification method: each browser was driven headless into a local HTTP/2
server and sec-ch-ua was read off the request. Captures came from
Chrome for Testing 150.0.7828.0, 151.0.7872.0 and 152.0.7923.0.

The same capture path reproduces the existing v149 string byte for byte
against installed Chrome 149.0.7827.155, which is what validates the method
rather than the derivation alone. Chrome for Testing is Chromium-branded, so
its output omits the Google Chrome entry while the GREASE and Chromium
entries match exactly.

HTTP/2

Unchanged across 149 to 152 (Akamai hash 52d84b11737d980aef856699f885ca86),
so the new profiles reuse v132::build_emulation like v148/v149.

TLS: known limitation, documented in the source

TLS does change across these versions and wreq 6.0's public options cannot
express the delta, so this PR does not claim TLS parity:

149  t13d1516h2_8daaf6152771_d8a2da3f94cd
150  t13d1517h2_8daaf6152771_cb7bf5808d99
151  t13d1518h2_8daaf6152771_4980c97edce0
152  same normalized JA4 as 151

Chrome 150 added the ML-DSA signature schemes 0x0904/0x0905/0x0906 and
the trust-anchor extension 0xca34
(BoringSSL,
trust anchors);
Chrome 151 added extension 0x12e0.

Header emulation is exact; the TLS delta is called out in a source comment so
consumers know which layer moved.

Tests

Adds hermetic header assertions for Chrome152 on Windows and macOS in
tests/client.rs, using the existing local-server helper.

  • cargo clippy --all-targets --all-features — clean
  • cargo build --all-features — clean
  • New Chrome152 tests pass

Note on the existing suite: cargo test --all-features shows 34 failures in
tests/emulate_chrome.rs. These are pre-existing on this branch's base —
verified by stashing the change and re-running, which produces the identical
34 — and appear to require outbound network access.

Not included

No changes to other browser families, no dependency or feature changes, and no
reformatting of untouched code.

Soju06 added 2 commits August 18, 2026 13:17
Added Chrome 150/151/152 to Emulation profiles and included them in the
weighted version selector, newest first.

The `sec-ch-ua` brand list is version-seeded, so the GREASE token and the
brand ordering differ per version and cannot be extrapolated from 149.
Each string was derived from Chromium's `GenerateBrandVersionList` and then
confirmed against real browser binaries over HTTP/2:

  150  "Not;A=Brand";v="8", "Chromium";v="150", "Google Chrome";v="150"
  151  "Not=A?Brand";v="99", "Google Chrome";v="151", "Chromium";v="151"
  152  "Chromium";v="152", "Not?A_Brand";v="24", "Google Chrome";v="152"

Captures came from Chrome for Testing 150.0.7828.0 / 151.0.7872.0 /
152.0.7923.0; the same capture path reproduces the existing v149 string
byte for byte against Chrome 149.0.7827.155, which validates the method.
Chrome for Testing is Chromium-branded, so its output omits the
`Google Chrome` entry while the GREASE and Chromium entries match exactly.

HTTP/2 is unchanged across these versions (Akamai hash
52d84b11737d980aef856699f885ca86), so the profiles reuse
`v132::build_emulation`.

TLS is not fully reproducible with wreq 6.0's public options and this is
documented in a source comment rather than silently approximated:

  149  t13d1516h2_8daaf6152771_d8a2da3f94cd
  150  t13d1517h2_8daaf6152771_cb7bf5808d99
  151  t13d1518h2_8daaf6152771_4980c97edce0
  152  same normalized JA4 as 151

Chrome 150 added the ML-DSA signature schemes 0x0904/0x0905/0x0906 and the
trust-anchor extension 0xca34; Chrome 151 added extension 0x12e0. Header
emulation is exact; the TLS delta remains a known limitation.

Adds hermetic header tests for Chrome152 on Windows and macOS.
Chrome 150 moved `accept-language` from just after `accept-encoding` to
just after the `sec-ch-ua` block. Measured over HTTP/2 against real
binaries (Chrome 149.0.7827.155, Chrome for Testing 150.0.7828.0 /
151.0.7872.0 / 152.0.7923.0):

  149   sec-ch-ua … upgrade-insecure-requests … accept-encoding accept-language priority
  150+  sec-ch-ua … accept-language upgrade-insecure-requests … accept-encoding priority

The v150/v151/v152 blocks added in this branch reused
`header_initializer_with_zstd_priority`, which emits the 149 order, so
they were byte-wrong on a header the profiles exist to reproduce.

Adds `header_initializer_with_zstd_priority_v150` and a `zstd_only` arm
of `header_chrome_accept_encoding` (the existing arm bundles the language
header, which 150+ no longer emits there). Profiles up to 149 keep the
existing initializer and are untouched.

Verified on the wire through the napi binding, one hermetic HTTP/2 server
per profile: chrome_150/151/152 emit accept-language at 4/13 and
chrome_149/148/142 stay at 12/13, each matching its real binary exactly.
@Soju06

Soju06 commented Aug 19, 2026

Copy link
Copy Markdown
Author

Pushed a follow-up commit (f395d94) fixing a header-order bug in the profiles
this PR adds.

What was wrong

Chrome 150 moved accept-language from just after accept-encoding to just
after the sec-ch-ua block. The v150/v151/v152 blocks reused
header_initializer_with_zstd_priority, which emits the 149 order, so the new
profiles were byte-wrong on a header they exist to reproduce.

Measured over HTTP/2 against real binaries:

149   sec-ch-ua … upgrade-insecure-requests … accept-encoding accept-language priority
150+  sec-ch-ua … accept-language upgrade-insecure-requests … accept-encoding priority

Position 12/13 → 4/13. Locale-invariant: probing with no locale flags, with
--lang=en-US, and with --lang=ja-JP all put it at 4/13 on 150+.

The fix

Adds header_initializer_with_zstd_priority_v150 plus a zstd_only arm of
header_chrome_accept_encoding — the existing arm bundles the language header,
which 150+ no longer emits in that position.

Profiles up to 149 are untouched and keep the existing initializer. That
matters: the shared macro backs 60 Chrome profiles plus Opera, so changing it
in place would have made every pre-150 profile wrong in the opposite direction.

Verification

Built through the napi binding and captured on the wire, one hermetic HTTP/2
server per profile:

profile accept-language position vs real binary
chrome_152 4/13 matches
chrome_151 4/13 matches
chrome_150 4/13 matches
chrome_149 12/13 matches (no regression)
chrome_148 12/13 matches (no regression)
chrome_142 12/13 matches (no regression)

Full header sequence compared element-wise, not just the one position.

cargo clippy --all-targets --all-features clean; cargo test --test client
passes both cases including the new Chrome152 assertions.

lavkanos added a commit to lavkanos/wreq-util that referenced this pull request Sep 2, 2026
Builds on 0x676e67#107. The 150-152 profiles reused v132's transport, so their
ClientHello was a 149-era fingerprint (JA4 t13d1516h2) under a Chrome/152
UA; real Chrome 152 is t13d1517h2.

Adds a preset with the ML-DSA signature algorithms (0x0904/05/06) ahead of
the classical eight and the trust_anchors extension (0xca34). JA4 matches a
real Chrome 152 capture: t13d1517h2_8daaf6152771_cb7bf5808d99.

trust_anchors is sent empty by default. The emulation-chromium-pki feature
fills it with Chrome's anchor IDs from chromium-root-certs and adds
chromium_root_store() so the client verifies against Chrome's roots.

Requires wreq #1220 (TlsOptions::requested_trust_anchors).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant