-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
UA full version info is too tightly bound to primary brand instead of engine #196
Comments
Thanks for the issue, @erik-anderson!
I think this is an interesting suggestion, and has the side-benefit of reducing default entropy. But this proposal is also suggesting we change what I'm not sure how I feel about Also, can you provide some theoretical sample code to help me understand how
Can you clarify? I'm having a hard time understanding what this means. Thanks. |
After thinking about it more, I'm also not sure if placeholder values in
Yes, developers would need to call it a second time. Alternatively, we could consider updating
If we chose to put placeholder '0' values for version components, then you can imagine sites would need to add special code to handle that. Here's some sample script that shows a few patterns: const uaData = navigator.userAgentData;
const brands = uaData.brands; // [ {brand: "Google Chrome", version: "84"}, {brand: "Chromium", version: "84"} ]
for (brandVersionPair in navigator.userAgentData.brands) {
if (brandVersionPair.brand == "Chromium") {
const versionParts = brandVersionPair.version.split('.');
// Chromium 84 versions older than 84.0.124.0 is broken; require a higher version.
if (versionParts[0] === 84 && versionParts[1] === 0 && versionParts[2] <= 123) {
// This is broken if the third part is 0 because full UA info isn't available.
}
// Fix the previous check to handle not having full version info.
let haveFullVersionInfo = false;
if (versionParts[1] === 0 && versionParts[2] === 0 && versionParts[3] === 0) {
haveFullVersionInfo = true;
}
if (haveFullVersionInfo && versionParts[0] === 84 && versionParts[1] === 0 && versionParts[2] <= 123) {
// This check doesn't accidentally assume it's too old of a version if full version info isn't available.
}
// A potential simplification:
if (uaData.fullVersionAvailable && versionParts[0] === 84 && versionParts[1] === 0 && versionParts[2] <= 123) {
// This check also works.
}
}
} That said, this is still pretty messy. Keeping it separated in the const uaData = navigator.userAgentData;
const brands = uaData.brands; // [ {brand: "Google Chrome", version: "84"}, {brand: "Chromium", version: "84"} ]
(async ()=>{
const highEntropyValues = await uaData.getHighEntropyValues(
["platform", "uaFullVersion"]);
const platform = highEntropyValues.platform; // "Mac OS X"
const uaFullVersions = highEntropyValues.uaFullVersion; // a FrozenArray<NavigatorUABrandVersion>
for (brandVersionPair in uaFullVersions) {
if (brandVersionPair.brand == "Chromium") {
const versionParts = brandVersionPair.version.split('.');
if (versionParts[0] === 84 && versionParts[1] === 0 && versionParts[2] <= 123) {
// Do something special.
}
}
}
})();
}
Sorry! I agree this was hard to parse. What I was trying to reiterate that the |
Would a structured list of keys and guidance on values help? I've contemplated this in issue 200. If there is only one value per key and the browser vendor (as identified by the user) and layout engine (embedded in multiple browsers) are two separate keys isn't the problem raised here largely addressed? |
(just pointing out to myself that my comment doesn't make sense, thanks everyone for being kind and ignoring it)
The first suggestion is something to consider - maybe as I think the issue of how to make this API useful for embedders is an important one. cc @yoavweiss in case he has some insights. |
(Wrote this before seeing Mike's comment) I personally don't like the idea of changing the format of the One mildly interesting fingerprinting question: are there times when the full version of a derivative browser (I don't know the proper term here) isn't incremented when a new version of the base browser is up-streamed/rebased/whatever? Put another way, are there situations were a the full brand-list of full versions isn't a one-to-one relationship with the full version of the derived browser? Not a blocker or anything, but my have interesting entropy implications to consider. |
I don't currently anticipate that. If the browser is picking up new platform changes, as part of releasing that update it would also presumably bump its own version to a higher number. The other way around isn't the case, though-- if that browser is bumping its own version due to changes it made while not updating what it's taken from the common engine, that version may not get bumped (e.g. Edge's would increase, but the Chromium version wouldn't). |
I feel slightly concerned about encouraging the assumption that |
The current state seems to discourage NewBrowser x.x.x from reporting its version at all, otherwise it risks a BaseBrowserEngine z.z.z version check failing because x.x.x doesn't match the z.z.z scheme. As a result, it would more likely choose to report the BaseBrowserEngine y.y.y version to maximize the odds of passing the site check. Can you clarify your concern? Are you saying you share the concern about the current API encouraging brittle checks tied to the dominant browser tied to each engine? Or that exposing the full version info about all of the brands would introduce other undesirable side effects? |
I believe what @rowan-m is trying to say is that listing both NewBrowser and BaseBrowser's full version implys a level of compatibility that's not necessarily a safe assumption, even in terms of things like bug fixes |
emerges from a pile of email So, I'm still thinking about this problem and had a thought:
@erik-anderson do you think this problem is exacerbated by the fact that Edge tracks the major number of Chrome? I can totally see developers seeing the same Major version, and same Engine, and making assumptions that the Build will be a signal they can fork behavior on. And if they did, build Here's a terrible, non-API suggested fix: change the Build to always be higher than Chrome's (say, by 100 or whatever), or even more terrible, add some arbitrary number to the major version. 🙈 Also, have y'all seen any real-world compat bugs from this UA sniffing scenario (or, a non-UA-CH version) yet? |
I think it's actually made less impactful because we track the same major version number. For at least broader version-based checks we'll squeak through. My general concern is that we went to all of this trouble to GREASE the UA brands/major version list and then we simultaneously expose a version number that is very much specific to just one of those brands. I'm not actually sure how a site is actually supposed to know which brand the full version info is supposed to be associated with.
I have not yet seen breakage here. However, what triggered me to think through this more and file this issue is because I was talking to site owners for Microsoft Edge-associated content to discuss with them what it would look like if they were to look at UA Client Hints first. One of their use cases was actually providing different content based on the full version number. If/when we end up seeing some sites and/or libraries code to very specific Chrome version boundaries in the future (no browser check surprises me at this point), we'd likely look at just exposing the Chromium version and simultaneously consider doing something undesirable like exposing a second, proprietary version header that reveals the true Edge version. |
🤔 I'll keep chewing on this. Ideally we can solve for this and not require other UAs to have to implement something else because the API in insufficient. |
Circling back to your original proposal, and re-wording it just to see if I understand:
Side note: not all browser engines follow Chromium's MAJOR.MINOR.BUILD.PATCH format. For example, Firefox will only report MAJOR.MINOR at first, and then MAJOR.MINOR.PATCH if a dot release is released (but, they only expose MAJOR.MINOR in the UA string: https://bugzilla.mozilla.org/show_bug.cgi?id=572659 -- I would assume they would be consistent were they to implement UA-Ch). Safari also currently exposes MAJOR.MINOR.PATCH in the UA string, as a potential UA-CH implementer. (GREASE could possibly help here and prevent sites from expecting a certain number of trailing 0s.) Some imaginary headers:
(The downside is now you have to do more parsing to get the major version, if that's all you wanted)
Question: could a site use requesting Would this issue be solved if we instead encouraged always displaying the full "engine" brand/equivalence class version (if a browser decides to include it)?: sec-ch-ua: "Microsoft Edge";v="89", "Chromium";v="89.0.4389.90", ";Not A Brand";v="99" But that also increases the default entropy of But, perhaps
Thoughts @erik-anderson? |
Presumably it wouldn't be any more info than could be observed with the presence or absence of the
Yes, this might be reasonable. It does increase default entropy, but given the rapid rollout schedule we see with Chrome and Edge releases, at least for those browsers it probably wouldn't be significant since we'd expect almost all users to be mapped to one or two values within a given major release.
Yes, this could be reasonable. If we went with this, I would strongly advocate for removing Sec-Ch-UA-Full-Version (or, depending on compat risk, replace the definition of it with the list form) to discourage sites from using a bad pattern. That said, if we did support both header variants, we would likely make Edge report the equivalent value to what Chrome does (which, in practice, is also the same value as the Chromium version). |
Yeah, having both co-exist in the medium- to long-term seems confusing and prone to mistakes. |
I'd throw my hat in for (The fact that it took a non-trivial amount of time to figure out how to word that sentence makes me worry about the added complexity to the spec as well :P) |
mozilla/standards-positions#552 also flags the problem of having a single entry in |
It should be more clear this is a type, and not a single value.
As we are going to deprecate the existing client hint `Sec-CH-UA-Full-Version`, using `Sec-CH-UA-Full-Version-List` instead. For details please check WICG/ua-client-hints#196
As we are going to deprecate the existing client hint `Sec-CH-UA-Full-Version`, using `Sec-CH-UA-Full-Version-List` instead. For details please check WICG/ua-client-hints#196
As we are going to deprecate the existing client hint `Sec-CH-UA-Full-Version`, using `Sec-CH-UA-Full-Version-List` instead. For details please check WICG/ua-client-hints#196
As we are going to deprecate the existing client hint Sec-CH-UA-Full-Version, using Sec-CH-UA-Full-Version-List instead. For details please check WICG/ua-client-hints#196. Update the documents to match the spec: https://wicg.github.io/ua-client-hints/#interface
As we are going to deprecate the existing client hint Sec-CH-UA-Full-Version, using Sec-CH-UA-Full-Version-List instead. For details please check WICG/ua-client-hints#196. Update the documents to match the spec: https://wicg.github.io/ua-client-hints/#interface
* update gethighentropyvalues client hints options As we are going to deprecate the existing client hint Sec-CH-UA-Full-Version, using Sec-CH-UA-Full-Version-List instead. For details please check WICG/ua-client-hints#196. Update the documents to match the spec: https://wicg.github.io/ua-client-hints/#interface
Imagine you're a browser embedding an engine that is more broadly used by a larger browser vendor.
In rare occasions, sites may need to work around a platform bug which is later addressed within a major version patch of the shared engine. In this scenario, let's assume it's not otherwise detectable/observable outside of version number.
The way a site would likely get the context for that with UA-Client-Hints is to request the full version via
Accept-CH: Sec-CH-UA-Full-Version
or callingnavigator.userAgentData.getHighEntropyValues(["uaFullVersion"])
, parsing it, and then scoping the workaround to builds without the bug fix.It's also likely the site will do that targeting solely on the version number of the largest browser shipping that engine. At this point, we've lost a lot of the utility of having a fuller set of brands available via the
sec-ch-ua
header and the navigator.userAgentData.brands list.To make this more concrete, here's a current example Microsoft Edge UA string:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 Edg/88.0.705.63
If Edge were to turn on UA Client Hints, Chrome would report
88.0.4324.150
for the UA full version and Edge would report88.0.705.63
.Let's say there's a an issue in Chromium versions <= to that version. Even if someone understands it's a Chromium issue affecting multiple browsers and gates their check on "brands contains Chromium", they would need to be aware of every browser's version that maps to that Chromium release. Instead, they might naively author a check like
if (majorVersion == 88 && minor == 0 && build <= 4324) { /* tell user to update their browser */ }
and break non-Chrome embedders that choose a different versioning scheme.A browser like Edge could choose to simply report the Chromium version number for the full UA version, but that then precludes a site matching on Edge version number (which would be unfortunate since Edge could have its own bugs that need such targeting).
I think an approach that looks something like this would be more likely to help sites author the correct checks:
The base brand version string should report a number with a version format that matches the full version, but zeroed out for the non-major version numbers. i.e. it might look like
sec-ch-ua: " Not A;Brand";v="99.0.0.0", "Chromium";v="88.0.0.0", "Microsoft Edge";v="88.0.0.0"
.If full UA version info is requested via
Accept-CH
, all of the other version parts would be filled in, e.g.sec-ch-ua: " Not A;Brand";v="99.1.300.grease", "Chromium";v="88.0.4324.150", "Microsoft Edge";v="88.0.705.63"
. Someone parsing the string would now be able to directly target a specific Chromium release across different browser brands.For JS callers, the
navigator.userAgentData.brands
' version value would work the same way-- placeholder version numbers for consistent parsing. Perhaps also expose a boolean property (navigator.uaData.fullVersionAvailable
?) that tells you if the version info is truncated so that version checks don't need to check if all of the non-major version components are zeroed out.getHighEntropyValues
for auaFullVersion
request might return a brands value with the full version info now filled out and future calls tonavigator.userAgentData.brands
would also show the full version info. I would prefer to not have a single string passed provided as currently defined due to the aforementioned potential "brand-versus-platform version" confusion it introduces for site owners.The text was updated successfully, but these errors were encountered: