diff --git a/fetch.bs b/fetch.bs index 4b1f0c2ca..fb383dc84 100644 --- a/fetch.bs +++ b/fetch.bs @@ -54,10 +54,28 @@ urlPrefix:https://tc39.es/ecma262/#;type:dfn;spec:ecma-262 url:realm;text:realm url:sec-list-and-record-specification-type;text:Record url:current-realm;text:current realm + +urlPrefix:https://www.ietf.org/archive/id/draft-annevk-johannhof-httpbis-cookies-00.html#;type:dfn;spec:cookies + url:name-cookie-store-and-limits;text:cookie store + url:name-parse-and-store-a-cookie;text:parse and store a cookie + url:name-parse-a-cookie;text:parse a cookie + url:name-store-a-cookie;text:store a cookie + url:name-retrieve-cookies;text:retrieve cookies + url:name-serialize-cookies;text:serialize cookies + +<!-- TODO: pending HTML changes- ancestor enum (https://github.com/whatwg/html/pull/10559), has storage access bit, initiator origin plumbing --> +urlPrefix:https://html.spec.whatwg.org#;type:dfn;spec:html + url:TODO;text:ancestry;for:environment + url:TODO;text:has storage access;for:environment </pre> <pre class=biblio> { + "COOKIES": { + "authors": ["Johann Hofmann", "Anne van Kesteren"], + "href": "https://www.ietf.org/archive/id/draft-annevk-johannhof-httpbis-cookies-00.html", + "title": "Cookies: HTTP State Management Mechanism" + }, "HTTP": { "aliasOf": "RFC9110" }, @@ -1938,6 +1956,10 @@ not always relevant and might require different behavior. "<code>client</code>" or an <a for=/>origin</a>. Unless stated otherwise it is "<code>client</code>". +<p>A <a for=/>request</a> has an associated +<dfn export for=request id=concept-request-navigation-initiator-origin>top-level navigation +initiator origin</dfn>, which is an <a for=/>origin</a> or null. Unless stated otherwise it is null. + <p class=note>"<code>client</code>" is changed to an <a for=/>origin</a> during <a lt=fetch for=/>fetching</a>. It provides a convenient way for standards to not have to set <a for=/>request</a>'s <a for=request>origin</a>. @@ -2226,9 +2248,9 @@ or "<code>object</code>". <hr> <div algorithm> -<p>A <a for=/>request</a> <var>request</var> has a -<dfn for=request id=concept-request-tainted-origin>redirect-tainted origin</dfn> if these steps -return true: +<p>A <a for=/>request</a> has a <dfn for=request id=concept-request-redirect-taint>redirect-taint</dfn>, +which is "<code>same-origin</code>", "<code>same-site</code>", or "<code>cross-site</code>". +<p>To get <a for=/>request</a> <var>request</var>'s <a>redirect-taint</a>: <ol> <li><p><a for=/>Assert</a>: <var>request</var>'s <a for=request>origin</a> is not @@ -2236,6 +2258,8 @@ return true: <li><p>Let <var>lastURL</var> be null. + <li><p>Let <var>computedTaint</var> be "<code>same-origin</code>". + <li> <p><a for=list>For each</a> <var>url</var> of <var>request</var>'s <a for=request>URL list</a>: @@ -2243,14 +2267,20 @@ return true: <li><p>If <var>lastURL</var> is null, then set <var>lastURL</var> to <var>url</var> and <a for=iteration>continue</a>. + <li><p>If <var>url</var>'s <a for=url>origin</a> is not <a for=/>same site</a> with + <var>lastURL</var>'s <a for=url>origin</a> and <var>request</var>'s <a for=request>origin</a> is + not <a for=/>same site</a> with <var>lastURL</var>'s <a for=url>origin</a>, then return + "<code>cross-site</code>". + <li><p>If <var>url</var>'s <a for=url>origin</a> is not <a>same origin</a> with <var>lastURL</var>'s <a for=url>origin</a> and <var>request</var>'s <a for=request>origin</a> is - not <a>same origin</a> with <var>lastURL</var>'s <a for=url>origin</a>, then return true. + not <a>same origin</a> with <var>lastURL</var>'s <a for=url>origin</a>, then set + <var>computedTaint</var> to "<code>same-site</code>". <li>Set <var>lastURL</var> to <var>url</var>. </ol> - <li>Return false. + <li>Return <var>computedTaint</var>. </ol> </div> @@ -2262,8 +2292,8 @@ run these steps: <li><p><a for=/>Assert</a>: <var>request</var>'s <a for=request>origin</a> is not "<code>client</code>". - <li><p>If <var>request</var> has a <a for=request>redirect-tainted origin</a>, then return - "<code>null</code>". + <li><p>If <var>request</var>'s <a for=request>redirect-taint</a> is not "<code>same-origin</code>", + then return "<code>null</code>". <li><p>Return <var>request</var>'s <a for=request>origin</a>, <a lt="ASCII serialization of an origin">serialized</a>. @@ -2372,8 +2402,8 @@ source of security bugs. Please seek security review for features that deal with "<a for="embedder policy value"><code>credentialless</code></a>", then return true.</p> <li><p>If <var>request</var>'s <a for=request>origin</a> is <a>same origin</a> with - <var>request</var>'s <a for=request>current URL</a>'s <a for=url>origin</a> and <var>request</var> - does not have a <a for=request>redirect-tainted origin</a>, then return true.</p> + <var>request</var>'s <a for=request>current URL</a>'s <a for=url>origin</a> and <var>request</var>'s + <a for=request>redirect-taint</a> is not "<code>same-origin</code>", then return true.</p> <li><p>Return false.</p> </ol> @@ -2486,8 +2516,9 @@ this is also tracked internally using the request's <a for=request>timing allow <dfn export for=response>service worker timing info</dfn> (null or a <a for=/>service worker timing info</a>), which is initially null. -<p>A <a for=/>response</a> has an associated <dfn for=response>has-cross-origin-redirects</dfn> -(a boolean), which is initially false. +<p>A <a for=/>response</a> has an associated <dfn for=response>redirect taint</dfn> +("<code>same-origin</code>", "<code>same-site</code>", or "<code>cross-site</code>", which is +initially "<code>same-origin</code>". <hr> @@ -4225,7 +4256,108 @@ indicates the request’s purpose is to fetch a resource that is anticipated to <p class=note>The server can use this to adjust the caching expiry for prefetches, to disallow the prefetch, or to treat it differently when counting page visits. +<h2 id=cookies>Cookies</h2> + +<h3 id=cookie-header>`<code>Cookie</code>` header</h3> + +<p>The `<code>Cookie</code>` header is largely defined in its own specification. [[COOKIES]]. +We define infrastructure to be able to use them conveniently here. + +<div algorithm> +<p>To <dfn id=append-a-request-cookie-header>append a request `<code>Cookie</code>` header</dfn>, +given a <a for=/>request</a> <var>request</var>, run these steps: + +<ol> + <li><p>If the user-agent is configured to disable cookies for <var>request</var>, it should + return. + + <li><p>Let |sameSite| be the result of [=determining the same-site mode=] for <var>request</var>. + + <li><p>Let |isSecure| be false. + + <li><p>If <var>request</var>'s <a for=request>client</a> is a <a>secure context</a>, then set + |isSecure| to true. + + <li><p>Let |httpOnlyAllowed| be true. + + <p class=note>Fetch implies that the request is http-only, as opposed to document.cookie + + <li><p>Let |cookies| be the result of running <a>retrieve cookies</a> given |isSecure|, + <var>request</var>'s <a for=request>current URL</a>'s <a for=url>host</a>, <var>request</var>'s + <a for=request>current URL</a>'s <a for=url>path</a>, |httpOnlyAllowed|, and |sameSite| + + <p class=note>It is expected that the cookie store returns an ordered list of cookies + + <li>If |cookies| <a for="list">is empty</a>, then return. + + <li>Let |value| be the result of running <a>serialize cookies</a> given |cookies|. + + <li><a for="header list">Append</a> (`<code>Cookie</code>`, <var>value</var>) to + <var>request</var>'s <a for=request>header list</a>. +</ol> +</div> + +<div algorithm> +<p>To <dfn id=parse-and-store-response-cookie-headers>parse and store response +`<code>Set-Cookie</code>` headers</dfn>, given a <a for=/>request</a> <var>request</var> and a <a +for=/>response</a> <var>response</var>, run these steps: + +<ol> + <li><p>If the user-agent is configured to disable cookies for <var>request</var>, it should + return. + <li><p>Let |allowNonHostOnlyCookieForPublicSuffix| be false. + + <li><p>Let |isSecure| be false. + + <li><p>If <var>request</var>'s <a for=request>client</a> is a <a>secure context</a>, set + |isSecure| to true. + + <li><p>Let |httpOnlyAllowed| be true. + + <p class=note>Fetch implies that the request is http-only, as opposed to document.cookie + + <li><p>Let |sameSiteStrictOrLaxAllowed| be true if the result of [=determine the same-site mode=] + for |request| is "<code>StrictOrLess</code>", and false otherwise. + + <li><p><a for=list>For each</a> <var>header</var> of <var>response</var>'s <a for=response>header + list</a>: + + <ol> + <li><p>If <var>header</var>'s <a for=header>name</a> is not a <a>byte-case-insensitive</a> match + for `<code>Set-Cookie</code>`, <a for=iteration>continue</a>. + + <li><p><a>Parse and store a cookie</a> given <var>header</var>'s <a for=header>value</a>, + |isSecure|, <var>request</var>'s <a for=request>current URL</a>'s <a for=url>host</a>, + <var>request</var>'s <a for=request>current URL</a>'s <a for=url>path</a>, |httpOnlyAllowed|, + |allowNonHostOnlyCookieForPublicSuffix|, and |sameSiteStrictOrLaxAllowed| + </ol> +</ol> +</div> + +<div algorithm> +<p>To <dfn>determine the same-site mode</dfn> for a given <a for=/>request</a> <var>request</var>, +run these steps: + +<ol> + <li><p><a for=/>Assert</a>: <var>request</var>'s <a for=request>method</a> is "GET" or "POST". + + <li><p>If <var>request</var>'s <a for=request>top-level navigation initiator origin</a> is not + null and is not <a for=/>same site</a> to <var>request</var>'s <a for=request>URL</a>'s + <a for=url>origin</a>, return "<code>UnsetOrLess</code>". + + <li><p>If <var>request</var>'s <a for=request>method</a> is "GET" and <var>request</var>'s <a + for=request>destination</a> is "document", return "<code>LaxOrLess</code>". + + <li><p>If <var>request</var>'s <a for=request>client</a>'s <a for=environment>ancestry</a> is + "<code>cross-site</code>", return "<code>UnsetOrLess</code>". + + <li><p>If <var>request</var>'s <a for=request>redirect-taint</a> is "<code>cross-site</code>", + return "<code>UnsetOrLess</code>". + + <li><p>Return "StrictOrLess". +</ol> +</div> <h2 id=fetching>Fetching</h2> @@ -4680,8 +4812,8 @@ steps: <!-- If you are ever tempted to move this around, carefully consider responses from about URLs, blob URLs, service workers, HTTP cache, HTTP network, etc. --> - <li><p>If <var>request</var> has a <a for=request>redirect-tainted origin</a>, then set - <var>internalResponse</var>'s <a for=response>has-cross-origin-redirects</a> to true. + <li><p>Set <var>internalResponse</var>'s <a for=response>redirect taint</a> to <var>request</var>'s + <a for=request>redirect-taint</a>. <li><p>If <var>request</var>'s <a for=request>timing allow failed flag</a> is unset, then set <var>internalResponse</var>'s <a for=response>timing allow passed flag</a>. @@ -4834,7 +4966,7 @@ steps: <li> <p>If <var>fetchParams</var>'s <a for="fetch params">request</a>'s <a for=request>mode</a> is not "<code>navigate</code>" or <var>response</var>'s - <a for=response>has-cross-origin-redirects</a> is false: + <a for=response>redirect taint</a> is "<code>same-origin</code>": <ol> <li><p>Set <var>responseStatus</var> to <var>response</var>'s <a for=response>status</a>. @@ -5710,21 +5842,7 @@ run these steps: <p>If <var>includeCredentials</var> is true, then: <ol> - <li> - <p>If the user agent is not configured to block cookies for <var>httpRequest</var> (see - <a href=https://httpwg.org/specs/rfc6265.html#privacy-considerations>section 7</a> of - [[!COOKIES]]), then: - - <ol> - <li><p>Let <var>cookies</var> be the result of running the "cookie-string" algorithm (see - <a href=https://httpwg.org/specs/rfc6265.html#cookie>section 5.4</a> of - [[!COOKIES]]) with the user agent's cookie store and <var>httpRequest</var>'s - <a for=request>current URL</a>. - - <li>If <var>cookies</var> is not the empty string, then <a for="header list">append</a> - (`<code>Cookie</code>`, <var>cookies</var>) to <var>httpRequest</var>'s - <a for=request>header list</a>. - </ol> + <li><p><a>Append a request `<code>Cookie</code>` header</a> for <var>httpRequest</var>. <li> <p>If <var>httpRequest</var>'s <a for=request>header list</a> @@ -6288,14 +6406,9 @@ optional boolean <var>forceNewConnection</var> (default false), run these steps: <li><p>Set <var>response</var>'s <a for=response>body</a> to a new <a for=/>body</a> whose <a for=body>stream</a> is <var>stream</var>. - <li><p tracking-vector>If <var>includeCredentials</var> is true and the user agent is not - configured to block cookies for <var>request</var> (see - <a href=https://httpwg.org/specs/rfc6265.html#privacy-considerations>section 7</a> of - [[!COOKIES]]), then run the "set-cookie-string" parsing algorithm (see - <a href=https://httpwg.org/specs/rfc6265.html#set-cookie>section 5.2</a> of [[!COOKIES]]) on the - <a for=header>value</a> of each <a for=/>header</a> whose <a for=header>name</a> is a - <a>byte-case-insensitive</a> match for `<code>Set-Cookie</code>` in <var>response</var>'s - <a for=response>header list</a>, if any, and <var>request</var>'s <a for=request>current URL</a>. + <li><p tracking-vector>If <var>includeCredentials</var> is true, the user agent should <a>parse and + store response `<code>Set-Cookie</code>` headers</a> given <var>request</var> and + <var>response</var>. <li> <p>Run these steps <a>in parallel</a>: