From 7000b05bf5ca48bba5a443703284fd7418a45707 Mon Sep 17 00:00:00 2001 From: Yoav Weiss Date: Thu, 23 Jan 2020 12:01:53 +0100 Subject: [PATCH 1/4] Half way through turning into a set --- README.md | 39 +++++++++++++++++++++++---------------- index.bs | 1 + 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index c06cc0b..58b0233 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ challenges in mind with the proposal that follows. ## A Proposal -By default, servers should receive only the user agent's brand and major version number. Servers +By default, servers should receive only the user agent's brand and significant version number. Servers can opt-into receiving information about minor versions, the underlying operating system's major version, and details about the underlying architecture and device. The user agent can make reasonable decisions about how to respond to sites' requests for more granular data. We might @@ -95,10 +95,10 @@ accomplish this as follows: 3. Browsers should introduce several new Client Hint header fields: - 1. The `Sec-UA` header field represents the user agent's brand and major version. For example: + 1. The `Sec-UA` header field represents the user agent's brand and version. For example: ```http - Sec-CH-UA: "Chrome 73" + Sec-CH-UA: "Chrome; 73" ``` Note: See the GREASE-like discussion below for how we could anticipate the inevitable lies @@ -107,7 +107,7 @@ accomplish this as follows: 2. The `Sec-CH-UA-Platform` header field represents the platform's brand and major version. For example: ```http - Sec-CH-UA-Platform: "Win 10" + Sec-CH-UA-Platform: "Win; 10" ``` 3. The `Sec-CH-UA-Arch` header field represents the underlying architecture's instruction set and @@ -136,17 +136,22 @@ accomplish this as follows: ```idl interface mixin NavigatorUA { - [SecureContext] Promise getUserAgent(); + [SecureContext] NavigatorUAData getUserAgent(); }; + Navigator includes NavigatorUA; + dictionary NavigatorUABrandVersionDict { + DOMString brand; // "Chrome" + DOMString version; // "69" + }; + interface NavigatorUAData { - readonly attribute DOMString brand; // "Chrome" - readonly attribute DOMString version; // "69" - readonly attribute DOMString platform; // "Win 10" - readonly attribute DOMString architecture; // "ARM64" - readonly attribute DOMString model; // "" - readonly attribute bool mobile; // false + readonly attribute FrozenArray brand; // [ { brand: "Chrome", version: "69" } ] + readonly attribute bool mobile; // false + readonly attribute Promise platform; // { brand: "Win", version: "10" } + readonly attribute Promise architecture; // "ARM64" + readonly attribute Promise model; // "" }; ``` @@ -157,7 +162,7 @@ accomplish this as follows: approach. User agents will attach the `Sec-CH-UA` header to every secure outgoing request by default, with a value -that includes only the major version (e.g. "`Chrome 69`"). Servers can opt-into receiving more +that includes only the significant version (e.g. "`Chrome; 69`"). Servers can opt-into receiving more detailed version information in the `Sec-CH-UA` header, along with the other available Client Hints, by delivering an `Accept-CH` header header in the usual way. @@ -172,7 +177,7 @@ A user agent's initial request to `https://example.com` will include the followi ```http User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.1.2222.33 Safari/537.36 -Sec-CH-UA: "Chrome 74" +Sec-CH-UA: "Chrome; 74" ``` If a server delivers the following response header: @@ -186,8 +191,8 @@ Then subsequent requests to `https://example.com` will include the following req ```http User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.1.2222.33 Safari/537.36 -Sec-CH-UA: "Chrome 74.0.3424.124" -Sec-CH-UA-Platform: "macOS 12" +Sec-CH-UA: "Chrome; 74.0.3424.124" +Sec-CH-UA-Platform: "macOS; 12" Sec-CH-UA-Arch: "ARM64" ``` @@ -216,7 +221,9 @@ Locking the User-Agent string will lock in the existing behavior of UA-sniffing may break things in the future if we diverge significantly from today's behavior in some interesting way, but that doesn't seem like a risk unique to this proposal. -## Should the UA string be a set? +## Should the UA string really be a set? + +Yes! History has shown us that there are real incentives for user agents to lie about their branding in order to thread the needle of sites' sniffing scripts. While I'm optimistic that we can reset diff --git a/index.bs b/index.bs index 005bf50..7ea85fb 100644 --- a/index.bs +++ b/index.bs @@ -7,6 +7,7 @@ Repository: wicg/ua-client-hints Shortname: ua-client-hints Level: None Editor: Mike West 56384, Google Inc., mkwst@google.com +Editor: Yoav Weiss 58673, Google Inc., yoav@yoav.ws Abstract: This document defines a set of Client Hints that aim to provide developers with the ability to perform agent-based content negotiation when necessary, while avoiding the historical baggage and From 7e9b747da97cac2c35aca2f38f5384cc9d627dff Mon Sep 17 00:00:00 2001 From: Yoav Weiss Date: Thu, 23 Jan 2020 14:04:10 +0100 Subject: [PATCH 2/4] Align examples, IDL and ABNF to the fact that UA is a set. Also change promises to be on the attributes --- README.md | 56 ++++++++++++++++++++++++++++--------------------------- index.bs | 52 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 60 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 58b0233..f650fd9 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ accomplish this as follows: 1. The `Sec-UA` header field represents the user agent's brand and version. For example: ```http - Sec-CH-UA: "Chrome; 73" + Sec-CH-UA: "Chrome; v=73" ``` Note: See the GREASE-like discussion below for how we could anticipate the inevitable lies @@ -107,7 +107,7 @@ accomplish this as follows: 2. The `Sec-CH-UA-Platform` header field represents the platform's brand and major version. For example: ```http - Sec-CH-UA-Platform: "Win; 10" + Sec-CH-UA-Platform: "Win; v=10" ``` 3. The `Sec-CH-UA-Arch` header field represents the underlying architecture's instruction set and @@ -142,14 +142,14 @@ accomplish this as follows: Navigator includes NavigatorUA; dictionary NavigatorUABrandVersionDict { - DOMString brand; // "Chrome" + required DOMString brand; // "Chrome" DOMString version; // "69" }; interface NavigatorUAData { - readonly attribute FrozenArray brand; // [ { brand: "Chrome", version: "69" } ] - readonly attribute bool mobile; // false - readonly attribute Promise platform; // { brand: "Win", version: "10" } + readonly attribute FrozenArray brand; // [ { brand: "Chrome", version: "69" } ] + readonly attribute boolean mobile; // false + readonly attribute Promise platform; // { brand: "Win", version: "10" } readonly attribute Promise architecture; // "ARM64" readonly attribute Promise model; // "" }; @@ -177,7 +177,7 @@ A user agent's initial request to `https://example.com` will include the followi ```http User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.1.2222.33 Safari/537.36 -Sec-CH-UA: "Chrome; 74" +Sec-CH-UA: "Chrome; v=74" ``` If a server delivers the following response header: @@ -191,8 +191,8 @@ Then subsequent requests to `https://example.com` will include the following req ```http User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.1.2222.33 Safari/537.36 -Sec-CH-UA: "Chrome; 74.0.3424.124" -Sec-CH-UA-Platform: "macOS; 12" +Sec-CH-UA: "Chrome; v=74.0.3424.124" +Sec-CH-UA-Platform: "macOS; v=12" Sec-CH-UA-Arch: "ARM64" ``` @@ -225,18 +225,23 @@ interesting way, but that doesn't seem like a risk unique to this proposal. Yes! -History has shown us that there are real incentives for user agents to lie about their branding -in order to thread the needle of sites' sniffing scripts. While I'm optimistic that we can reset -expectations around sniffing by freezing the thing that's sniffed-upon today, and creating a sane -set of options for developers, it's likely that this is hopelessly naive. It's reasonable to -ponder what we should do to encourage sniffing in the right way, if we believe it's going to -happen one way or another. +History has shown us that there are real incentives for user agents to lie +about their branding in order to thread the needle of sites' sniffing scripts, +and prevent their users from being blocked by UA-based allow/block lists. -One suggestion is to model `UA` as a set, rather than a single entry. For example, user agents -might encourage standardized processing of the `UA` string by randomly including additional, -intentionally incorrect, comma-separated entries with arbitrary ordering (similar conceptually -to TLS's [GREASE][4]). Chrome 73's `UA` value might then be `"Chrome 73", "NotBrowser 12"`, or -`"BrowsingIsFun Version 12b", "Chrome 73"`. +Reseting expectations may help to prevent abuse of the UA string's brand in the +short term, but probably won't help in the long run. + +Having `UA` be a set enables browsers to express their brand, as well as their +different equivalence sets. We hope that this enabled expressiveness will +enable sites to differentially serve based on capabilities while minimizing +wrongfully categorizing browsers in the wrong buckets. + +Complementary to that, user agents might encourage standardized processing of +the `UA` string by randomly including additional, intentionally incorrect, +comma-separated entries with arbitrary ordering (similar conceptually to TLS's +[GREASE][4]). Chrome 73's `UA` value might then be `Chrome; 73`, `NotBrowser; +12`, or `BrowsingIsFun; 12b, Chrome; 73`. We'd reflect this value in the `NavigatorUAData.brand` attribute. @@ -251,14 +256,11 @@ cases? There are a few options for the string: -1. `"Chrome 73"`, which has the least entropy, but also sets poor expectations. -2. `"CriOS 73"` (or `"Chrome on iOS 73"`, or similar) which is basically what's sent today, and categorizes the browser as distinct. -3. `"CriOS 73", "Safari 12"`, which is interesting. -4. `"Chrome 73", "Safari 12"`, which is more interesting. - +1. `Chrome; 73`, which has the least entropy, but also sets poor expectations. +2. `CriOS; 73` (or `"Chrome on iOS 73"`, or similar) which is basically what's sent today, and categorizes the browser as distinct. +3. `CriOS; 73, Safari; 12`, which is interesting. +4. `Chrome; 73, Safari; 12`, which is more interesting. -(A more verbose alternative could add a `Sec-CH-UA-Engine` header, containing values like `Blink`, -`EdgeHTML`, `Gecko`, or `WebKit`.) ## Wait a minute, where is the Client Hints infrastructure specified? diff --git a/index.bs b/index.bs index 7ea85fb..06b2eea 100644 --- a/index.bs +++ b/index.bs @@ -143,7 +143,7 @@ A user navigates to `https://example.com/` for the first time. Their user agent header along with the HTTP request: ``` http - Sec-CH-UA: "Examplary Browser 73" + Sec-CH-UA: "Examplary Browser; v=73" ``` The server is interested in rendering content consistent with the user's underlying platform, and @@ -158,8 +158,8 @@ In response, the user agent includes more detailed version information, as well the underlying platform in the next request: ``` http - Sec-CH-UA: "Examplary Browser 73.3R8.2H.1" - Sec-CH-UA-Platform: "Windows 10" + Sec-CH-UA: "Examplary Browser; v=73.3R8.2H.1" + Sec-CH-UA-Platform: "Windows; v=10" ``` @@ -172,7 +172,7 @@ in [[I-D.ietf-httpbis-client-hints]]. The definitions below assume that each use a number of properties for itself: * brand (for example: "cURL", "Edge", "The World's Best Web Browser") -* major version (for example: "72", "3", or "28") +* significant version (for example: "72", "3", or "28") * full version (for example: "72.0.3245.12", "3.14159", or "297.70E04154A") * platform brand and version (for example: "Windows NT 6.0", "iOS 15", or "AmazingOS 17G") * platform architecture (for example: "ARM64", or "ia32") @@ -227,6 +227,10 @@ The header's ABNF is: Sec-CH-UA-Platform = sh-string ``` +Note: The `sh-string` above can include a `v` parameter, indicating the platform's version. + +ISSUE: Add processing model here that creates the header using SH serialization. + The 'Sec-CH-UA' Header Field {#sec-ch-ua} ---------------------------- @@ -240,9 +244,11 @@ The header's ABNF is: Sec-CH-UA = sh-list ``` +Note: The `sh-list` above is a list of `sh-string`, each MAY include a `v` parameter, indicating the user agent version. + Unlike most Client Hints, the `Sec-CH-UA` header will be sent with all requests, whether or not the server opted-into receiving the header via an `Accept-CH` header. Prior to an opt-in, however, it -will include only the user agent's branding information, and the major version number (both of which +will include only the user agent's branding information, and the significant version number (both of which are fairly clearly sniffable by "examining the structure of other headers and by testing for the availability and semantics of the features introduced or modified between releases of a particular browser" [[Janc2014]]). @@ -254,13 +260,10 @@ user agents MUST: 1. Let |value| be a [=Structured Header=] object whose value is a [=structured header/list=]. 2. Let |version| be the user agent's [=user agent/full version=] if |set| - [=list/contains=] `UA`, and the user agent's [=user agent/major version=] otherwise. + [=list/contains=] `UA`, and the user agent's [=user agent/significant version=] otherwise. 3. Let |ua| be a string whose value is the [=string/concatenation=] of the user agent's - [=user agent/brand=], a U+0020 SPACE character, and |version|. - - ISSUE(wicg/ua-client-hints#7): Should we split the version out into a separate - `Sec-CH-UA-Version` header? Or keep it here? + [=user agent/brand=], the string ";v=", and |version|. 4. [=list/Append=] |ua| to |value|. @@ -275,6 +278,10 @@ user agents MUST: 6. Return |value|. +Issue: Fix the processing model here so that it will use Structured Headers +serialization, and make sure that the value set for headers if the same as the +one returned from the JS API. + The 'Sec-CH-UA-Mobile' Header Field {#sec-ch-mobile} -------------------------------- @@ -298,17 +305,22 @@ Interface {#interface} =================
+dictionary NavigatorUABrandVersionDict {
+  required DOMString brand;
+  DOMString version;
+};
+
 [Exposed=Window]
 interface NavigatorUAData {
-  readonly attribute DOMString brand;
-  readonly attribute DOMString version;
-  readonly attribute DOMString platform;
-  readonly attribute DOMString architecture;
-  readonly attribute DOMString model;
+  readonly attribute FrozenArray<NavigatorUABrandVersionDict> brand;
   readonly attribute boolean mobile;
+  readonly attribute Promise<NavigatorUABrandVersionDict> platform;
+  readonly attribute Promise<DOMString> architecture;
+  readonly attribute Promise<DOMString> model;
 };
+
 interface mixin NavigatorUA {
-  [SecureContext] Promise<NavigatorUAData> getUserAgent();
+  [SecureContext] NavigatorUAData getUserAgent();
 };
 Navigator includes NavigatorUA;
 
@@ -334,14 +346,12 @@ Processing model {#processing}
         ::  The user agent's [=user agent/model=].
         :   {{NavigatorUAData/mobile}}
         ::  The user agent's [=user agent/mobileness=].
-        :   {{NavigatorUAData/version}}
-        ::  The user agent's [=user agent/full version=].
 
     2.  [=Resolve=] |p| with |UAData|.
 
 3.  Return |p|.
 
-ISSUE: Provide a method to only access the UA's major version.
+ISSUE: Provide a method to only access the UA's significant version.
 
 Security and Privacy Considerations {#security-privacy}
 ===================================
@@ -410,13 +420,13 @@ could encourage standardized processing of the `UA` string, by randomly
 including additional, intentionally incorrect, comma-separated entries with
 arbitrary ordering. That would reduce the chance that we ossify on a few
 required strings.  For example, Chrome 73's `Sec-CH-UA` header might be
-`"Chrome 73", "NotBrowser 12"`, `"BrowsingIsFun Version 12b", "Chrome 73"`, or
+`Chrome; 73, NotBrowser; 12`, `BrowsingIsFun; 12b, Chrome; 73`, or
 something completely different.
 
 The 'Sec-CH-' prefix {#sec-ch}
 --------------------
 
-Based on some discussion in https://github.com/w3ctag/design-reviews/issues/320, it seems
+Based on some [discussion with the TAG](https://github.com/w3ctag/design-reviews/issues/320), it seems
 reasonable to forbid access to these headers from JavaScript, and demarcate them as
 browser-controlled client hints so they can be documented and included in requests without
 triggering CORS preflights. A `Sec-CH-` prefix seems like a viable approach, but this bit might

From ee7c46d12cf72f5fa45d309b8053f24c5d20db86 Mon Sep 17 00:00:00 2001
From: Yoav Weiss 
Date: Thu, 23 Jan 2020 14:06:32 +0100
Subject: [PATCH 3/4] Revert promises for now

---
 README.md | 8 ++++----
 index.bs  | 8 ++++----
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/README.md b/README.md
index f650fd9..ae4bd2c 100644
--- a/README.md
+++ b/README.md
@@ -136,7 +136,7 @@ accomplish this as follows:
 
     ```idl
     interface mixin NavigatorUA {
-      [SecureContext] NavigatorUAData getUserAgent();
+      [SecureContext] Promise getUserAgent();
     };
 
     Navigator includes NavigatorUA;
@@ -149,9 +149,9 @@ accomplish this as follows:
     interface NavigatorUAData {
       readonly attribute FrozenArray brand;   // [ { brand: "Chrome", version: "69" } ]
       readonly attribute boolean mobile;                                   // false
-      readonly attribute Promise platform;    // { brand: "Win", version: "10" }
-      readonly attribute Promise architecture;                  // "ARM64"
-      readonly attribute Promise model;                         // ""
+      readonly attribute NavigatorUABrandVersionDict platform;    // { brand: "Win", version: "10" }
+      readonly attribute DOMString architecture;                  // "ARM64"
+      readonly attribute DOMString model;                         // ""
     };
     ```
 
diff --git a/index.bs b/index.bs
index 06b2eea..030f5cb 100644
--- a/index.bs
+++ b/index.bs
@@ -314,13 +314,13 @@ dictionary NavigatorUABrandVersionDict {
 interface NavigatorUAData {
   readonly attribute FrozenArray<NavigatorUABrandVersionDict> brand;
   readonly attribute boolean mobile;
-  readonly attribute Promise<NavigatorUABrandVersionDict> platform;
-  readonly attribute Promise<DOMString> architecture;
-  readonly attribute Promise<DOMString> model;
+  readonly attribute NavigatorUABrandVersionDict platform;
+  readonly attribute DOMString architecture;
+  readonly attribute DOMString model;
 };
 
 interface mixin NavigatorUA {
-  [SecureContext] NavigatorUAData getUserAgent();
+  [SecureContext] Promise<NavigatorUAData> getUserAgent();
 };
 Navigator includes NavigatorUA;
 

From 75932c3343a904d2dc13d3e87fe8ed4ecb2e75f8 Mon Sep 17 00:00:00 2001
From: Yoav Weiss 
Date: Thu, 23 Jan 2020 16:05:37 +0100
Subject: [PATCH 4/4] Comments from aarontag

---
 README.md | 34 +++++++++++++++++-----------------
 index.bs  | 10 +++++-----
 2 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/README.md b/README.md
index ae4bd2c..da6399c 100644
--- a/README.md
+++ b/README.md
@@ -95,10 +95,10 @@ accomplish this as follows:
 
 3.  Browsers should introduce several new Client Hint header fields:
 
-    1.  The `Sec-UA` header field represents the user agent's brand and  version. For example:
+    1.  The `Sec-UA` header field represents the user agent's brand and significant version. For example:
 
         ```http
-        Sec-CH-UA: "Chrome; v=73"
+        Sec-CH-UA: "Chrome"; v="73"
         ```
 
         Note: See the GREASE-like discussion below for how we could anticipate the inevitable lies
@@ -107,7 +107,7 @@ accomplish this as follows:
     2.  The `Sec-CH-UA-Platform` header field represents the platform's brand and major version. For example:
 
         ```http
-        Sec-CH-UA-Platform: "Win; v=10"
+        Sec-CH-UA-Platform: "Win"; v="10"
         ```
 
     3.  The `Sec-CH-UA-Arch` header field represents the underlying architecture's instruction set and
@@ -142,16 +142,16 @@ accomplish this as follows:
     Navigator includes NavigatorUA;
 
     dictionary NavigatorUABrandVersionDict {
-      required DOMString brand;          // "Chrome"
+      required DOMString brand; // "Chrome"
       DOMString version;        // "69"
     };
 
     interface NavigatorUAData {
       readonly attribute FrozenArray brand;   // [ { brand: "Chrome", version: "69" } ]
       readonly attribute boolean mobile;                                   // false
-      readonly attribute NavigatorUABrandVersionDict platform;    // { brand: "Win", version: "10" }
-      readonly attribute DOMString architecture;                  // "ARM64"
-      readonly attribute DOMString model;                         // ""
+      readonly attribute NavigatorUABrandVersionDict platform;             // { brand: "Win", version: "10" }
+      readonly attribute DOMString architecture;                           // "ARM64"
+      readonly attribute DOMString model;                                  // ""
     };
     ```
 
@@ -162,7 +162,7 @@ accomplish this as follows:
     approach.
 
 User agents will attach the `Sec-CH-UA` header to every secure outgoing request by default, with a value
-that includes only the significant version (e.g. "`Chrome; 69`"). Servers can opt-into receiving more
+that includes only the significant version (e.g. `"Chrome"; v="69"`). Servers can opt-into receiving more
 detailed version information in the `Sec-CH-UA` header, along with the other available Client Hints, by
 delivering an `Accept-CH` header header in the usual way.
 
@@ -177,7 +177,7 @@ A user agent's initial request to `https://example.com` will include the followi
 ```http
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
             Chrome/71.1.2222.33 Safari/537.36
-Sec-CH-UA: "Chrome; v=74"
+Sec-CH-UA: "Chrome"; v="74"
 ```
 
 If a server delivers the following response header:
@@ -191,8 +191,8 @@ Then subsequent requests to `https://example.com` will include the following req
 ```http
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
             Chrome/71.1.2222.33 Safari/537.36
-Sec-CH-UA: "Chrome; v=74.0.3424.124"
-Sec-CH-UA-Platform: "macOS; v=12"
+Sec-CH-UA: "Chrome"; v="74.0.3424.124"
+Sec-CH-UA-Platform: "macOS"; v="12"
 Sec-CH-UA-Arch: "ARM64"
 ```
 
@@ -240,8 +240,8 @@ wrongfully categorizing browsers in the wrong buckets.
 Complementary to that, user agents might encourage standardized processing of
 the `UA` string by randomly including additional, intentionally incorrect,
 comma-separated entries with arbitrary ordering (similar conceptually to TLS's
-[GREASE][4]). Chrome 73's `UA` value might then be `Chrome; 73`, `NotBrowser;
-12`, or `BrowsingIsFun; 12b, Chrome; 73`.
+[GREASE][4]). Chrome 73's `UA` value might then be `"Chrome"; v="73"`,
+`"NotBrowser"; v="12"`, or `"BrowsingIsFun"; v="12b", "Chrome"; v="73"`.
 
 We'd reflect this value in the `NavigatorUAData.brand` attribute.
 
@@ -256,10 +256,10 @@ cases?
 
 There are a few options for the string:
 
-1.  `Chrome; 73`, which has the least entropy, but also sets poor expectations.
-2.  `CriOS; 73` (or `"Chrome on iOS 73"`, or similar) which is basically what's sent today, and categorizes the browser as distinct.
-3.  `CriOS; 73, Safari; 12`, which is interesting.
-4.  `Chrome; 73, Safari; 12`, which is more interesting.
+1.  `"Chrome"; v="73"`, which has the least entropy, but also sets poor expectations.
+2.  `"CriOS"; v="73"` (or `"Chrome on iOS", v="73"`, or similar) which is basically what's sent today, and categorizes the browser as distinct.
+3.  `"CriOS"; v="73", "Safari"; v="12"`, which is interesting.
+4.  `"Chrome"; v="73", "Safari";v="12"`, which is more interesting.
 
 
 ## Wait a minute, where is the Client Hints infrastructure specified?
diff --git a/index.bs b/index.bs
index 030f5cb..29a61e6 100644
--- a/index.bs
+++ b/index.bs
@@ -143,7 +143,7 @@ A user navigates to `https://example.com/` for the first time. Their user agent
 header along with the HTTP request:
 
 ``` http
-  Sec-CH-UA: "Examplary Browser; v=73"
+  Sec-CH-UA: "Examplary Browser"; v="73"
 ```
 
 The server is interested in rendering content consistent with the user's underlying platform, and
@@ -158,8 +158,8 @@ In response, the user agent includes more detailed version information, as well
 the underlying platform in the next request:
 
 ``` http
-  Sec-CH-UA: "Examplary Browser; v=73.3R8.2H.1"
-  Sec-CH-UA-Platform: "Windows; v=10"
+  Sec-CH-UA: "Examplary Browser"; v="73.3R8.2H.1"
+  Sec-CH-UA-Platform: "Windows"; v="10"
 ```
 
 
@@ -420,8 +420,8 @@ could encourage standardized processing of the `UA` string, by randomly
 including additional, intentionally incorrect, comma-separated entries with
 arbitrary ordering. That would reduce the chance that we ossify on a few
 required strings.  For example, Chrome 73's `Sec-CH-UA` header might be
-`Chrome; 73, NotBrowser; 12`, `BrowsingIsFun; 12b, Chrome; 73`, or
-something completely different.
+`"Chrome"; v="73"`, `"NotBrowser"; v="12"`, `"BrowsingIsFun"; v="12b",
+"Chrome"; v="73"`, or something completely different.
 
 The 'Sec-CH-' prefix {#sec-ch}
 --------------------