# uaFullVersion vs fullVersionList, and a bare 153

> Our engine reported uaFullVersion as "153". Real Chrome always sends four numbers. Sending the full build moved FingerprintJS's confidence to stock's 0.99.

Source: https://mosaicbrowser.com/blog/full-version-without-build-number

## A full Chrome version with no build number is one no Chrome shipped

Our engine reported uaFullVersion as "153". Real Chrome always sends four numbers. Sending the full build moved FingerprintJS's confidence to stock's 0.99.

![153 versus 153.0.8010.37, each name in its own tile: A version no Chrome has ever reported](https://mosaicbrowser.com/covers/full-version-without-build-number.webp)

`uaFullVersion` is one string: the full version of the browser's main brand. `fullVersionList` is the same information for every brand the browser lists, `Google Chrome`, `Chromium` and the GREASE entry, each with its own full version. The low-entropy `brands` list carries only majors. In a real Chrome all of them agree, and every full version has four numbers. Ours did not: for a while our engine reported `uaFullVersion` as `"153"`, a version no Chrome has ever shipped, and fixing it moved FingerprintJS's confidence from `1` to stock's exact `0.99`.

## uaFullVersion vs fullVersionList, and the third reader

A page can learn Chrome's version from three places, and they are deliberately different in how much they say.

```
navigator.userAgent                      Chrome/153.0.0.0
navigator.userAgentData.brands           Chromium 153, Google Chrome 153, GREASE
getHighEntropyValues([...])              uaFullVersion, fullVersionList

```

The User-Agent string was reduced some years ago so it carries only the major, padded with zeros. `brands` is sent to every site and also stops at the major. The full build number lives in the high-entropy hints, which a page has to request, by script through `getHighEntropyValues()` or by asking the server for the `Sec-CH-UA-Full-Version-List` header. `uaFullVersion` is the older, single-string form and is deprecated in favour of the list, but it still answers, and detection scripts still read it because a mismatch between the two is cheap to spot.

A real Chrome version is `major.minor.build.patch`, such as `152.0.7977.83`. Three things follow from that, and each is something a script can check in a line: every full version has four parts, the major in the full version equals the major in `brands` and the User-Agent, and the `Chrome` and `Chromium` entries carry the same build.

Mosaic is a desktop app

Email yourself the link and open it on your computer.

[How it works: Machines that exist](https://mosaicbrowser.com/features/real-fingerprints)

## How ours came out as "153"

Our engine, Tesseract Chrome, takes the brand version from one switch, `--fingerprint-brand-version`, and that one switch feeds all three readers. Inside the engine, `GetChromiumVersion()` returns the switch value verbatim, and `GetMajorVersion()` cuts the major off it.

We were sending the major alone. So `brands`, which wants the major, was right, and both of the other readers repeated the bare number:

```
stock   uaFullVersion "152.0.7977.83"   fullVersionList 152.0.7977.83
ours    uaFullVersion "153"             fullVersionList 153

```

The code that builds the switch, `mosaicArgs()`, already took the engine's real version as a parameter. It threw it away with `void engineVersion`, which was the one thing the parameter was there for. It sends the full version now, and `uaFullVersion` reads `153.0.8010.37`.

The effect was measured on the FingerprintJS demo: its confidence moved from `1` to `0.99`, which is the figure stock Chrome scored on the same machine. A different problem on that demo, its failure to name our browser at all, turned out to be about Google's branding rather than the version; that investigation is in [when the FingerprintJS demo can't name your browser](https://mosaicbrowser.com/blog/fingerprintjs-cannot-name-browser).

## When a profile claims an older major

Every profile's claimed major follows the engine's real one, so the running binary's own version is normally what goes out. When a profile does claim another major, which the profile audit already flags, the full version comes from that major's real build number in our version table, not from an invented `.0.0.0`. A zeroed build is a tell of its own.

That table had drifted. It topped out at 149 while the engine was on 153, so it was refreshed from Google's own release data rather than typed in by hand.

## Two things that looked wrong and were not

The GREASE brand reads `Not_A Brand` with version `8` at major 153, and we checked whether that was correct before touching it. It is. Chrome builds the GREASE name and version from the major itself: two characters picked from a fixed set of eleven by `seed % 11` and `(seed + 1) % 11`, placed around `A`, and a version picked from three by `seed % 3`. At 153 that gives an underscore, a space and `8`. At 152 the same algorithm gives stock's `Not?A_Brand` and `24`. Changing it to look more random would have made it wrong.

The second is why we refuse to claim Edge. The same brand-version switch feeds the client-hint lists, which need the full build, and the User-Agent suffix, which needs `major.0.0.0`. For Chrome those two shapes come from one version. For Edge's brand they would not, so a profile could have a correct User-Agent or a correct set of client hints and never both. Profiles claim Chrome.

## How to check the version your browser reports

Open DevTools on any page and run:

```
navigator.userAgentData
  .getHighEntropyValues(['uaFullVersion', 'fullVersionList'])
  .then(console.log)

```

Then read the answer against the rules above. Every version should have four parts. The `Google Chrome` and `Chromium` entries should carry the same full version, and its major should match `brands` and the `Chrome/` token in `navigator.userAgent`. Compare with a branded Google Chrome of the same major rather than with another Chromium build, for the reasons in [the control that settles "is it our patches?"](https://mosaicbrowser.com/blog/the-control-is-googles-build).

None of this is a fingerprint in the sense of something unique to you. It is a consistency check, and the browser either passes it or contradicts itself. How we keep the rest of a profile's values agreeing with each other is on [real fingerprints](https://mosaicbrowser.com/features/real-fingerprints), and what a page can read in general is in the [browser fingerprint glossary entry](https://mosaicbrowser.com/glossary/browser-fingerprint).

## Questions

### What is the difference between uaFullVersion and fullVersionList?

uaFullVersion is one string, the full version of the browser's main brand. fullVersionList is an array with the full version of every brand in the brand list, the GREASE brand included. Both are high-entropy client hints; uaFullVersion is the older one and is deprecated in favour of the list.

### What does a real Chrome full version look like?

Four numbers separated by dots: major, minor, build and patch, such as 152.0.7977.83. The minor is 0 in current releases. A bare major such as 153 is not a version any Chrome release has reported.

### Why does the User-Agent show 153.0.0.0 when the client hints show more?

Chrome reduced the User-Agent string so it carries only the major, padded with zeros. The real build number moved to the high-entropy client hints, which a page has to ask for.

### How do I read my own browser's full version list?

In the DevTools console run navigator.userAgentData.getHighEntropyValues(['uaFullVersion', 'fullVersionList']) and read the result. Every version should have four parts and the Chrome and Chromium entries should agree.

## Related

[When the FingerprintJS demo can't name your browser](https://mosaicbrowser.com/blog/fingerprintjs-cannot-name-browser): FingerprintJS read 'Chromium-Based Browser Not Available' for our engine. We blamed the version; the real variable was Google's branding.

[The control that settles "is it our patches?"](https://mosaicbrowser.com/blog/the-control-is-googles-build): Unpatched Chrome for Testing got the same IPHey verdict as our engine, so our patches were not the cause. Its voice list still differs from Chrome's.

[Machines that exist: fingerprints drawn from real hardware](https://mosaicbrowser.com/features/real-fingerprints): Mosaic draws each profile's GPU, screen, CPU, memory and fonts from one real device, so the parts of a fingerprint agree with each other and with the host.

[Browser fingerprint: what a site can read about your machine](https://mosaicbrowser.com/glossary/browser-fingerprint): A browser fingerprint is the set of values a site reads about your browser and computer, which together recognize you without cookies. What goes into one.









## Mosaic, on your own computer

The browser this blog is about: its own Chromium, fingerprints from real machines and a proxy exit per profile. Free holds 3 profiles. Pro is $49 a month for any number.

Mosaic is a desktop app

Email yourself the link and open it on your computer.
