Skip to content

Commit

Permalink
Add subresource integrity support to import maps
Browse files Browse the repository at this point in the history
  • Loading branch information
yoavweiss committed May 31, 2024
1 parent 2312c6b commit b2fdca1
Showing 1 changed file with 184 additions and 27 deletions.
211 changes: 184 additions & 27 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -2705,6 +2705,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute

<ul class="brief">
<li><dfn data-x-href="https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata">parse integrity metadata</dfn></li>
<li><dfn data-x-href="https://w3c.github.io/webappsec-subresource-integrity/#the-integrity-attribute">the requirements of the integrity attribute</dfn></li>
<li><dfn data-x-href="https://w3c.github.io/webappsec-subresource-integrity/#get-the-strongest-metadata">get the strongest metadata from set</dfn></li>
</ul>
</dd>
Expand Down Expand Up @@ -26985,6 +26986,10 @@ document.body.appendChild(wbr);</code></pre>
data-x="attr-link-integrity">integrity</code> attribute, if it is specified, or the empty string
otherwise.</p></li>

<li><p>If <var>el</var> does not have an <code data-x="attr-link-integrity">integrity</code>
attribute, then set <var>integrity metadata</var> to the result of <span>resolving a module
integrity metadata</span> with <var>url</var> and <var>settings object</var>.</p></li>

<li><p>Let <var>referrer policy</var> be the current state of <var>el</var>'s <code
data-x="attr-link-referrerpolicy">referrerpolicy</code> attribute.</p></li>

Expand Down Expand Up @@ -62618,6 +62623,12 @@ o............A....e

<dt>"<code data-x="">module</code>"</dt>
<dd>
<p>If <var>el</var> does not have an <code data-x="attr-script-integrity">integrity</code>
attribute, then set <var>options</var>'s <span
data-x="concept-script-fetch-options-integrity">integrity metadata</span> to the result of
<span>resolving a module integrity metadata</span> with <var>url</var> and
<var>settings object</var>.</p>

<p><span>Fetch an external module script graph</span> given <var>url</var>, <var>settings
object</var>, <var>options</var>, and <var>onComplete</var>.</p>
</dd>
Expand Down Expand Up @@ -106255,12 +106266,51 @@ document.querySelector("button").addEventListener("click", bound);
<span data-x="concept-script-fetch-options-fetch-priority">fetch priority</span>.</p></dd>
</dl>

<p>For any given <span>script fetch options</span> <var>options</var>, the <dfn>descendant script
fetch options</dfn> are a new <span>script fetch options</span> whose <span data-x="struct
item">items</span> all have the same values, except for the <span
data-x="concept-script-fetch-options-integrity">integrity metadata</span>, which is instead the
empty string, and the <span data-x="concept-script-fetch-options-fetch-priority">fetch
priority</span>, which is instead "<code data-x="">auto</code>".</p>
<p>To <dfn>get the descendant script fetch options</dfn> given a <span>script fetch options</span>
<var>originalOptions</var>, a <span>URL</span> <var>url</var>, and an <span>environment settings
object</span> <var>settingsObject</var>:</p>

<ol>
<li><p>Let <var>newOptions</var> be a copy of <var>originalOptions</var>.</p></li>

<li><p>Let <var>integrity</var> be the empty string.</p></li>

<li><p>If <var>settingsObject</var>'s <span
data-x="concept-settings-object-global">global object</span> is a <code>Window</code> object,
then set <var>integrity</var> to the result of <span>resolving a module integrity metadata</span>
with <var>url</var> and <var>settingsObject</var>.</p></li>

<li><p>Set <var>newOptions</var>'s <span
data-x="concept-script-fetch-options-integrity">integrity metadata</span> to
<var>integrity</var>.</p></li>

<li><p>Set <var>newOptions</var>'s <span
data-x="concept-script-fetch-options-fetch-priority">fetch priority</span> to "<code
data-x="">auto</code>".</p></li>

<li><p>Return <var>newOptions</var>.</p></li>
</ol>

<p>To <dfn data-x="resolving a module integrity metadata">resolve a module
integrity metadata</dfn>, given a <span>URL</span> <var>url</var> and an <span>environment
settings object</span> <var>settingsObject</var>:</p>

<ol>
<li><p><span>Assert</span>: <var>settingsObject</var>'s <span
data-x="concept-settings-object-global">global object</span> is a <code>Window</code>
object.</p></li>

<li><p>Let <var>map</var> be <var>settingsObject</var>'s <span
data-x="concept-settings-object-global">global object</span>'s <span
data-x="concept-window-import-map">import map</span>.</p></li>

<li><p>If <var>map</var>'s <span
data-x="concept-import-map-integrity">integrity</span>[<var>url</var>] does not <span
data-x="map exists">exist</span>, then return the empty string.</p></li>

<li><p>Return <var>map</var>'s <span
data-x="concept-import-map-integrity">integrity</span>[<var>url</var>].</p></li>
</ol>

<hr>

Expand Down Expand Up @@ -108344,6 +108394,29 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
</table>
</div>

<div class="example" id="example-import-map-integrity">
<p>Import maps can also be used to provide modules with integrity metadata to be used in
<cite>Subresource Integrity</cite> checks. <ref>SRI</ref>
</p>

<p>The following import map illustrates this:</p>

<pre><code class="json" data-x="">{
"imports": {
"a": "/a-1.mjs",
"b": "/b-1.mjs",
"c": "/c-1.mjs"
},
"integrity": {
"/a-1.mjs": "sha384-Li9vy3DqF8tnTXuiaAJuML3ky+er10rcgNR/VqsVpcw+ThHmYcwiB1pbOxEbzJr7",
"/d-1.mjs": "sha384-MBO5IDfYaE6c6Aao94oZrIOiC6CGiSN2n4QUbHNPhzk5Xhm0djZLQqTpL0HzTUxk"
}
}</code></pre>
<p>The above example provides integrity metadata to be enforced on the modules <code
data-x="">/a-1.mjs</code> and <code data-x="">/d-1.mjs</code>, even if the latter is not defined
as an import in the map.</p>
</div>

<hr>

<p>The <span>child text content</span> of a <code>script</code> element representing an
Expand All @@ -108353,11 +108426,13 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
<ul>
<li><p>It must be valid JSON. <ref>JSON</ref></p></li>

<li><p>The JSON must represent a JSON object, with at most the two keys "<code
data-x="">imports</code>" and "<code data-x="">scopes</code>".</p></li>
<li><p>The JSON must represent a JSON object, with at most the three keys "<code
data-x="">imports</code>", "<code data-x="">scopes</code>", and "<code
data-x="">integrity</code>".</p></li>

<li><p>The values corresponding to the "<code data-x="">imports</code>" and "<code
data-x="">scopes</code>" keys, if present, must themselves be JSON objects.</p></li>
<li><p>The values corresponding to the "<code data-x="">imports</code>", "<code
data-x="">scopes</code>", and "<code data-x="">integrity</code>" keys, if present,
must themselves be JSON objects.</p></li>

<li><p>The value corresponding to the "<code data-x="">imports</code>" key, if present, must be
a <span>valid module specifier map</span>.</p></li>
Expand All @@ -108366,6 +108441,10 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
JSON object, whose keys are <span data-x="valid URL string">valid URL strings</span> and whose
values are <span data-x="valid module specifier map">valid module specifier
maps</span>.</p></li>

<li><p>The value corresponding to the "<code data-x="">integrity</code>" key, if present, must
be a JSON object, whose keys are <span data-x="valid URL string">valid URL strings</span> and
whose values fit <span>the requirements of the integrity attribute</span>.</p></li>
</ul>

<p>A <dfn>valid module specifier map</dfn> is a JSON object that meets the following
Expand All @@ -108389,22 +108468,30 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp

<h5>Import map processing model</h5>

<p>Formally, an <dfn>import map</dfn> is a <span>struct</span> with two <span data-x="struct
<p>Formally, an <dfn>import map</dfn> is a <span>struct</span> with three <span data-x="struct
item">items</span>:</p>

<ul>
<li><p><dfn data-x="concept-import-map-imports">imports</dfn>, a <span>module specifier
map</span>; and</p></li>
map</span>;</p></li>

<li><p><dfn data-x="concept-import-map-scopes">scopes</dfn>, an <span>ordered map</span> of
<span data-x="URL">URLs</span> to <span data-x="module specifier map">module specifier
maps</span>.</p>
maps</span>; and</p></li>

<li><p><dfn data-x="concept-import-map-integrity">integrity</dfn>, a <span>module integrity
map</span>.</p></li>
</ul>

<p>A <dfn>module specifier map</dfn> is an <span>ordered map</span> whose <span data-x="map
key">keys</span> are <span data-x="string">strings</span> and whose <span data-x="map
value">values</span> are either <span data-x="URL">URLs</span> or nulls.</p>

<p>A <dfn>module integrity map</dfn> is an <span>ordered map</span> whose <span data-x="map
key">keys</span> are <span data-x="URL">URLs</span> and whose <span data-x="map
value">values</span> are <span data-x="string">strings</span> that will be used as <span
data-x="concept-request-integrity-metadata">integrity metadata</span>.</p>

<p>An <dfn>empty import map</dfn> is an <span>import map</span> with its <span
data-x="concept-import-map-imports">imports</span> and <span
data-x="concept-import-map-scopes">scopes</span> both being empty maps.</p>
Expand Down Expand Up @@ -108478,20 +108565,40 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
</ol>
</li>

<li><p>Let <var>normalizedIntegrity</var> be an empty <span>ordered map</span>.</p></li>

<li>
<p>If <var>parsed</var>["<code data-x="">integrity</code>"] <span data-x="map
exists">exists</span>, then:</p>

<ol>
<li><p>If <var>parsed</var>["<code data-x="">integrity</code>"] is not an <span>ordered
map</span>, then throw a <code>TypeError</code> indicating that the value for the "<code
data-x="">integrity</code>" top-level key needs to be a JSON object.</p></li>

<li><p>Set <var>normalizedIntegrity</var> to the result of <span>normalizing a module
integrity map</span> given <var>parsed</var>["<code data-x="">integrity</code>"] and
<var>baseURL</var>.</p></li>
</ol>
</li>

<li>
<p>If <var>parsed</var>'s <span data-x="map key">keys</span> <span data-x="list
contains">contains</span> any items besides "<code data-x="">imports</code>" or "<code
data-x="">scopes</code>", then the user agent should <span>report a warning to the
console</span> indicating that an invalid top-level key was present in the import map.</p>
contains">contains</span> any items besides "<code data-x="">imports</code>", "<code
data-x="">scopes</code>", or "<code data-x="">integrity</code>", then the user agent should
<span>report a warning to the console</span> indicating that an invalid top-level key was
present in the import map.</p>

<p class="note">This can help detect typos. It is not an error, because that would prevent any
future extensions from being added backward-compatibly.</p>
</li>

<li><p>Return an <span>import map</span> whose <span
data-x="concept-import-map-imports">imports</span> are <var>sortedAndNormalizedImports</var> and
data-x="concept-import-map-imports">imports</span> are <var>sortedAndNormalizedImports</var>,
whose <span data-x="concept-import-map-scopes">scopes</span> are
<var>sortedAndNormalizedScopes</var>.</p></li>
<var>sortedAndNormalizedScopes</var>, and whose <span
data-x="concept-import-map-integrity">integrity</span> are
<var>normalizedIntegrity</var>.</p></li>
</ol>

<div class="example" id="example-import-map-normalization">
Expand Down Expand Up @@ -108640,6 +108747,57 @@ dictionary <dfn dictionary>PromiseRejectionEventInit</dfn> : <span>EventInit</sp
turn gives "<code data-x="">foo/bar/</code>" a higher priority than "<code data-x="">foo/</code>"
during <span data-x="resolve a module specifier">module specifier resolution</span>.</p>

<p>To <dfn data-x="normalizing a module integrity map">normalize a module integrity map</dfn>,
given an <span>ordered map</span> <var>originalMap</var>:</p>

<ol>
<li><p>Let <var>normalized</var> be an empty <span>ordered map</span>.</p></li>

<li>
<p><span data-x="list iterate">For each</span> <var>key</var> → <var>value</var> of
<var>originalMap</var>:</p>

<ol>
<li>
<p>Let <var>resolvedURL</var> be the result of <span>resolving a URL-like module
specifier</span> given <var>key</var> and <var>baseURL</var>.</p>

<p class="note">Unlike "<code data-x="">imports</code>", keys of the integrity map are treated
as URLs, not module specifiers. However, we use the <span data-x="resolving a URL-like module
specifier">resolve a URL-like module specifier</span> algorithm to prohibit "bare" relative
URLs like <code data-x="">foo</code>, which could be mistaken for module specifiers.</p>
</li>

<li>
<p>If <var>resolvedURL</var> is null, then:</p>

<ol>
<li><p>The user agent may <span>report a warning to the console</span> indicating that
the key failed to resolve.</p></li>

<li><p><span>Continue</span>.</p></li>
</ol>
</li>

<li>
<p>If <var>value</var> is not a <span>string</span>, then:</p>

<ol>
<li><p>The user agent may <span>report a warning to the console</span> indicating that
<span data-x="concept-request-integrity-metadata">integrity metadata</span> values need to
be <span data-x="string">strings</span>.</p></li>

<li><p><span>Continue</span>.</p></li>
</ol>
</li>

<li><p>Set <var>normalized</var>[<var>resolvedURL</var>] to <var>value</var>.</p></li>
</ol>
</li>

<li><p>Return <var>normalized</var>.</p></li>
</ol>

<p>To <dfn data-x="normalizing a specifier key">normalize a specifier key</dfn>, given a <span>string</span> <var>specifierKey</var> and a <span>URL</span> <var>baseURL</var>:</p>

<ol>
Expand Down Expand Up @@ -109271,7 +109429,7 @@ import "https://example.com/foo/../module2.mjs";</code></pre>

<li><p>Let <var>referencingScript</var> be null.</p></li>

<li><p>Let <var>fetchOptions</var> be the <span>default classic script fetch
<li><p>Let <var>originalFetchOptions</var> be the <span>default classic script fetch
options</span>.</p></li>

<li><p>Let <var>fetchReferrer</var> be "<code data-x="">client</code>".</p></li>
Expand All @@ -109286,16 +109444,11 @@ import "https://example.com/foo/../module2.mjs";</code></pre>
<li><p>Set <var>settingsObject</var> to <var>referencingScript</var>'s <span
data-x="concept-script-settings-object">settings object</span>.</p></li>

<li><p>Set <var>fetchOptions</var> to the new <span>descendant script fetch options</span> for
<var>referencingScript</var>'s <span data-x="concept-script-script-fetch-options">fetch
options</span>.</p></li>

<li><p><span>Assert</span>: <var>fetchOptions</var> is not null, as
<var>referencingScript</var> is a <span>classic script</span> or a <span>JavaScript module
script</span>.</p></li>

<li><p>Set <var>fetchReferrer</var> to <var>referencingScript</var>'s <span
data-x="concept-script-base-url">base URL</span>.</p></li>

<li><p>Set <var>originalFetchOptions</var> to <var>referencingScript</var>'s <span
data-x="concept-script-script-fetch-options">fetch options</span>.</p></li>
</ol>

<div class="example">
Expand Down Expand Up @@ -109334,6 +109487,10 @@ import "https://example.com/foo/../module2.mjs";</code></pre>
</ol>
</li>

<li><p>Let <var>fetchOptions</var> be the result of <span data-x="get the descendant script
fetch options">getting the descendant script fetch options</span> given
<var>originalFetchOptions</var>, <var>settingsObject</var>, and <var>url</var>.</p></li>

<li><p>Let <var>destination</var> be <code data-x="">"script"</code>.</p></li>

<li><p>Let <var>fetchClient</var> be <var>settingsObject</var>.</p></li>
Expand Down

0 comments on commit b2fdca1

Please sign in to comment.