https://www.michalspacek.com/exports/articlesMichal Špaček: All articles2023-11-22T17:28:28+01:00Michal Špačekhttps://www.michalspacek.com/origin-site-etld-etld-plus-one-public-suffix-psl-what-are-they2023-11-20T22:30:00+01:002023-11-22T17:28:28+01:00<p>We call it pages, domains, servers, websites, internets and we hope the other
party will understand. Maybe, maybe not, but that can always be cleared with the
additional “wait, a server, don't you mean a website?” You can't just ask
those questions when reading various specifications and technical documents, so
they try to call things by their correct names and in a consistent manner. And
they do it so well that terms like origin, site, same origin, same site, <abbr
title="effective top-level domain">eTLD</abbr> and public suffix are normally
not even translated to other languages, because then nobody would understand it.
And how does the attractiveness of subdomains relate to this?</p>Origin, site, <abbr title="effective top-level domain">eTLD</abbr>, eTLD+1,
public suffix, PSL. What are they?<h3>Updates</h3><ul><li><em><strong>22.11.</strong> Added a short info about cross-origin and cross-site requests</em></li></ul><p>I've realized I'm using some of these terms in almost every article or talk,
and rather than explain it every time (and each time simplified in a different
way), I've decided to make it into this article that I can link to. Almost all
of it could be expressed with the following picture:</p>
<div class="figure"><img
src="https://www.michalspacek.cz/i/images/blog/domain-etld-site-origin.png"
alt="https://www.cam.ac.uk URL with TLD, eTLD, eTLD+1, Site, Origin parts marked"
width="720" height="288">
<p>A picture is worth a thousand words, but this blog post is worth two
thousand two hundred and twelve</p>
</div>
<p>We won't go into the secrets of URL and all its parts, it would take us a
hundred years. We'll just explain what is in the title, because the browser sets
certain boundaries based on that.</p>
<p>Imagine a simple URL, for example:</p>
<pre>https://www.example.com/foo/bar</pre>
<h2 id="domain">Domain</h2>
<p>This may be more of a refresher, but it will come in handy later. The domain
in the URL above is <code>www.example.com</code> and has several levels:</p>
<ul>
<li>The top-level domain (<abbr title="top-level domain">TLD</abbr>) is
<code>com</code></li>
<li>The second-level domain (<abbr title="second-level domain">2LD</abbr>) is
<code>example.com</code></li>
<li>The third-level (<abbr title="third-level domain">3LD</abbr>) is
<code>www.example.com</code></li>
<li>And so on</li>
</ul>
<p>Instead of a domain, it can also be an IP address, but for simplicity we'll
stick with domains.</p>
<h2 id="registrable-domain">Registrable domain</h2>
<p>A registrable domain is that part of a domain that you can register or lease
either from a domain registrar or from another entity that offers e.g. blogs on
subdomains. If we ignore any further delegation, all subdomains under the
registrable, including it, belong to the same organization.</p>
<p>You could also say that a registrable domain is what you type in the
registration form. If you want to have a website at
<code>www.michalspacek.com</code>, you also register only
<code>michalspacek.com</code>. However, a registrable domain is not always the
same as a second-level domain.</p>
<p>This is the case, for example, in the United Kingdom, where universities and
other academic institutions can <a
href="https://www.jisc.ac.uk/forms/acuk-domain-request-form">register</a>
domains that identically end in <code>ac.uk</code> (<em>ac</em> as
<em>academia</em>) and differ only in the third-level domain. Examples include
the two <a
href="https://en.wikipedia.org/wiki/Oxford%E2%80%93Cambridge_rivalry">rival</a>
universities of <a href="https://www.ox.ac.uk/">Oxford</a>
(<code>ox.ac.uk</code>) and <a href="https://www.cam.ac.uk/">Cambridge</a>
(<code>cam.ac.uk</code>). Other common domains are <code>co.uk</code> (for
commercial entities) and <code>net.uk</code> (internet service providers), with
<a href="https://en.wikipedia.org/wiki/.uk#Active">almost 20</a> in total.</p>
<p>It's the same in many other countries, subdomains such as
<code>….gov.something</code> for various government organizations can often be
seen. Similarly for domains of various storages such as
<code>….s3.amazonaws.com</code> for Amazon S3 or domains such as
<code>….blogspot.com</code>, <code>….blogspot.no</code>,
<code>….blogspot.com.au</code> and tons more for Google's Blogger.</p>
<div>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Registrable domain</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>example.com</code></td>
<td><code>example.com</code></td>
</tr>
<tr>
<td><code>www.example.com</code></td>
<td><code>example.com</code></td>
</tr>
<tr>
<td><code>foo.bar.example.com</code></td>
<td><code>example.com</code></td>
</tr>
<tr>
<td><code>ox.ac.uk</code></td>
<td><code>ox.ac.uk</code></td>
</tr>
<tr>
<td><code>www.ox.ac.uk</code></td>
<td><code>ox.ac.uk</code></td>
</tr>
<tr>
<td><code>talks.ox.ac.uk</code></td>
<td><code>ox.ac.uk</code></td>
</tr>
<tr>
<td><code>blogs.law.ox.ac.uk</code></td>
<td><code>ox.ac.uk</code></td>
</tr>
<tr>
<td><code>my.foo.s3.amazonaws.com</code></td>
<td><code>foo.s3.amazonaws.com</code></td>
</tr>
<tr>
<td><code>foo.blogspot.com.au</code></td>
<td><code>foo.blogspot.com.au</code></td>
</tr>
<tr>
<td><code>www.foo.blogspot.com.au</code></td>
<td><code>foo.blogspot.com.au</code></td>
</tr>
</tbody>
</table>
</div>
<p>A registrable domain is sometimes colloquially called “naked domain”,
“base domain”, “root domain” or “apex domain”. These expressions are
probably never translated into other languages, because under some of these
expressions, many would certainly imagine something completely different.</p>
<p>Registrable domain is <a
href="https://url.spec.whatwg.org/#host-registrable-domain">defined in the URL
Standard</a>.</p>
<h2 id="effective-top-level-domain-etld-and-etld-1">Effective top-level domain
(<abbr title="effective top-level domain">eTLD</abbr>) and <abbr
title="effective top-level domain">eTLD</abbr>+1</h2>
<p>An effective top-level domain, abbreviated <abbr
title="effective top-level domain">eTLD</abbr>, is a domain one level above a
registrable domain. <abbr title="effective top-level domain">eTLD</abbr> is a
domain under which other domains are registered, usually by different owners.
Often the <abbr title="effective top-level domain">eTLD</abbr> is equal to the
<abbr title="top-level domain">TLD</abbr> itself, but in many cases it is not.
Looking at a domain unfortunately doesn't allow us to simply find its effective
top-level or registrable domain algorithmically or with a regular expression,
but there is a list of <abbr
title="effective top-level domain">eTLD</abbr>s that we (or a machine) can look
at, see below.</p>
<p>An effective top-level domain <em>plus one</em> or <em><strong><abbr
title="effective top-level domain">eTLD</abbr>+1</strong></em> is the same as a
registrable domain.</p>
<div>
<table>
<thead>
<tr>
<th>Domain</th>
<th><abbr title="effective top-level domain">eTLD</abbr></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>example.com</code></td>
<td><code>com</code></td>
</tr>
<tr>
<td><code>www.example.com</code></td>
<td><code>com</code></td>
</tr>
<tr>
<td><code>foo.bar.example.com</code></td>
<td><code>com</code></td>
</tr>
<tr>
<td><code>ox.ac.uk</code></td>
<td><code>ac.uk</code></td>
</tr>
<tr>
<td><code>www.ox.ac.uk</code></td>
<td><code>ac.uk</code></td>
</tr>
<tr>
<td><code>talks.ox.ac.uk</code></td>
<td><code>ac.uk</code></td>
</tr>
<tr>
<td><code>blogs.law.ox.ac.uk</code></td>
<td><code>ac.uk</code></td>
</tr>
<tr>
<td><code>foo.blogspot.com.au</code></td>
<td><code>blogspot.com.au</code></td>
</tr>
<tr>
<td><code>www.foo.blogspot.com.au</code></td>
<td><code>blogspot.com.au</code></td>
</tr>
</tbody>
</table>
</div>
<p><abbr title="effective top-level domain">eTLD</abbr> cannot be registrable,
in other words: a registrable domain part of <abbr
title="effective top-level domain">eTLD</abbr> is <em>null</em>.</p>
<h2 id="public-suffix-list-psl">Public suffix list (PSL)</h2>
<p><abbr title="public suffix list">PSL</abbr> is a list of effective top-level
domains, <abbr title="effective top-level domain">eTLD</abbr>s, but here they're
called “public suffixes”, because they are basically suffixes of publicly
registrable domains. The file is available from <a
href="https://publicsuffix.org/">publicsuffix.org</a> (<a
href="https://publicsuffix.org/list/public_suffix_list.dat">direct link</a>) and
is updated using pull requests <a href="https://github.com/publicsuffix/list">on
GitHub</a>.</p>
<p>The part of the code that tries to find the <abbr
title="effective top-level domain">eTLD</abbr> for e.g.
<code>www.foo.blogspot.com.au</code> has to look in that list and see if:</p>
<ul>
<li>Is <code>au</code> a public suffix? <a
href="https://github.com/publicsuffix/list/blob/9f004d9fc2f9e3fef3682c9245653fa669c7ed0a/public_suffix_list.dat#L222">It
is</a>, similar to other country code top-level domains (ccTLD).</li>
<li>Is <code>com.au</code> a public suffix? <a
href="https://github.com/publicsuffix/list/blob/9f004d9fc2f9e3fef3682c9245653fa669c7ed0a/public_suffix_list.dat#L224">Yes</a>.</li>
<li>Is <code>blogspot.com.au</code> a public suffix? Also <a
href="https://github.com/publicsuffix/list/blob/9f004d9fc2f9e3fef3682c9245653fa669c7ed0a/public_suffix_list.dat#L13489">yes</a>.</li>
<li>Is <code>foo.blogspot.com.au</code> a public suffix? No, it isn't.</li>
<li>Is <code>www.foo.blogspot.com.au</code> a public suffix? Also not.</li>
</ul>
<p>So the result is that the effective <abbr title="top-level domain">TLD</abbr>
(eTLD) is <code>blogspot.com.au</code>, the registrable domain and at the same
time the <abbr title="effective top-level domain">eTLD</abbr>+1 is
<code>foo.blogspot.com.au</code>.</p>
<p>It's actually a bit more complicated, because the list also includes entries
with <code>*</code> and <code>!</code>, like <a
href="https://github.com/publicsuffix/list/blob/9f004d9fc2f9e3fef3682c9245653fa669c7ed0a/public_suffix_list.dat#L752-L753">e.g.</a>
<code>*.ck</code> (<code>[anything].ck</code> is an <abbr
title="effective top-level domain">eTLD</abbr>) and <code>!www.ck</code> (…
except <code>www.ck</code>), but we'll leave that out for simplicity.</p>
<p>It is also necessary to go through the whole domain, it is not enough to stop
at the first “no”, because it could easily happen that the “public
suffix” is encountered somewhere later: the <abbr
title="effective top-level domain">eTLD</abbr> of the domain
<code>files.s3.amazonaws.com</code> is <code>s3.amazonaws.com</code>, because <a
href="https://github.com/publicsuffix/list/blob/9f004d9fc2f9e3fef3682c9245653fa669c7ed0a/public_suffix_list.dat#L837">like
<code>com</code></a>, <a
href="https://github.com/publicsuffix/list/blob/9f004d9fc2f9e3fef3682c9245653fa669c7ed0a/public_suffix_list.dat#L11678"><code>s3.amazonaws.com</code>
is also a public suffix</a>, but just <code>amazonaws.com</code> is not.</p>
<p>Browsers <a href="https://publicsuffix.org/learn/">use</a> public suffix list
mostly to restrict cookies from being set to apply to all domains with the
suffix <code>.co.uk</code> etc. Firefox also uses the list to highlight domains
in the address bar.</p>
<div class="figure"><img
src="https://www.michalspacek.cz/i/images/blog/firefox119-url-domain-highlight.png"
alt="URL bar with https://www.cambridgestudents.cam.ac.uk in Firefox 119, eTLD+1 highlighted"
width="496" height="60">
<p>Highlighting <abbr title="effective top-level domain">eTLD</abbr>+1 in
Firefox 119</p>
</div>
<div class="figure"><img
src="https://www.michalspacek.cz/i/images/blog/chrome119-url-domain-highlight.png"
alt="URL bar with https://www.cambridgestudents.cam.ac.uk in Chrome 119, no eTLD+1 highlighting"
width="468" height="69">
<p>The same domain loaded in Chrome 119</p>
</div>
<p>The public suffix list is also used by certification authorities when issuing
wildcard certificates for HTTPS, because they are not allowed to issue a
certificate for e.g. <code>*.co.uk</code>. And you can use the list too, for
example to warn your users that “you probably don't want to do this with this
domain”, but ideally for nothing else. <a
href="https://publicsuffix.org/learn/">Libraries</a> exist for many languages.
Personally I have experience with the <a
href="https://github.com/jeremykendall/php-domain-parser">PHP Domain Parser</a>,
which can also use a locally cached list.</p>
<h2 id="origin">Origin</h2>
<p>The term <em>origin</em> refers to the part of the URL that contains the
protocol (more precisely, the scheme), the domain and the port.</p>
<div>
<table>
<thead>
<tr>
<th>URL</th>
<th><em>Origin</em></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>https://www.example.com/foo</code></td>
<td><code>https://www.example.com</code></td>
</tr>
<tr>
<td><code>https://www.example.com:303/foo</code></td>
<td><code>https://www.example.com:303</code></td>
</tr>
</tbody>
</table>
</div>
<p>Note that <em>origin</em> does not end with a slash, which already belongs to
the <em>path</em> part <code>/foo</code>.</p>
<h2 id="same-origin">Same origin</h2>
<p>By the term <em>same-origin policy</em> we mean several different
restrictions that allow two things to somehow work together if and only if they
have a “same origin” relationship, in simple terms if they have the same
<em>origin</em>s.</p>
<p><em>Same-origin policy</em> is also used e.g. to restrict JavaScript running
in an <code>iframe</code> (typically as part of an advertisement) so that it
cannot steal, I mean read from the parent document using for example
<code>window.parent.document.getElementById('username')</code>, unless the
<code>iframe</code> and the parent document have the same <em>origin</em>. In
such case it's not even possible the other way around, the parent document
cannot access the <code>iframe</code> contents (with for example
<code>document.getElementById('iframe').contentWindow.document.getElementById('username')</code>).</p>
<p>✔️</p>
<ul>
<li><code>https://example.com/foo</code> &
<code>https://example.com/bar</code> are “same origin”</li>
<li><code>https://foo.bar.baz.test/foo</code> &
<code>https://foo.bar.baz.test/bar</code> are “same origin”, the domain
doesn't matter, it can be <code>.com</code> or <code>.anything</code>, it just
has to be the same in both cases</li>
</ul>
<p>❌</p>
<ul>
<li><code>https://example.com/foo</code> &
<code>https://www.example.com/bar</code> aren't “same origin”
(<code>example.com</code> and <code>www.example.com</code> are not the same
domain)</li>
<li><code>https://example.com/foo</code> &
<code>http://example.com/bar</code> aren't “same origin” (<code>https</code>
and <code>http</code> are not the same protocols)</li>
<li><code>https://example.com:4431/foo</code> &
<code>https://example.com:4432/bar</code> aren't “same origin” (the ports
are different)</li>
</ul>
<p>For more information about <em>origin</em> and <em>same origin</em> in
slightly more formal language, see the <a
href="https://html.spec.whatwg.org/multipage/browsers.html#origin">HTML
specification</a>.</p>
<h2 id="site">Site</h2>
<p>The term “site” is often used to refer to an entire website, server, or
something like that, but we're here to go deeper. <em>Site</em> means a subset
of a URL, which includes a protocol (schema) and a registrable domain.</p>
<div>
<table>
<thead>
<tr>
<th>URL</th>
<th><em>Site</em></th>
</tr>
</thead>
<tbody>
<tr>
<td><code>https://www.example.com/foo/bar</code></td>
<td><code>https</code> & <code>example.com</code></td>
</tr>
<tr>
<td><code>https://neco.nek.de.example.com/foo/bar</code></td>
<td><code>https</code> & <code>example.com</code></td>
</tr>
<tr>
<td><code>http://www.ox.ac.uk/</code></td>
<td><code>http</code> & <code>ox.ac.uk</code></td>
</tr>
<tr>
<td><code>https://staff.admin.ox.ac.uk/</code></td>
<td><code>https</code> & <code>ox.ac.uk</code></td>
</tr>
<tr>
<td><code>https://www.cam.ac.uk/</code></td>
<td><code>https</code> & <code>cam.ac.uk</code></td>
</tr>
<tr>
<td><code>https://my.foo.s3.amazonaws.com/</code></td>
<td><code>https</code> & <code>foo.s3.amazonaws.com</code></td>
</tr>
<tr>
<td><code>https://foo.blogspot.com.au/</code></td>
<td><code>https</code> & <code>foo.blogspot.com.au</code></td>
</tr>
<tr>
<td><code>https://www.foo.blogspot.com.au/</code></td>
<td><code>https</code> & <code>foo.blogspot.com.au</code></td>
</tr>
</tbody>
</table>
</div>
<h2 id="same-site">Same site</h2>
<p>Two URLs or two <em>origins</em> are “same site” if their
<em>site</em>s are equal, i.e. if they have the same protocols (schemes) and
registerable domains. Unlike <em>same origin</em>, neither the port nor the
entire domain matters in this case.</p>
<p>✔️</p>
<ul>
<li><code>https://example.com/foo</code> &
<code>https://example.com/bar</code> are “same site” (and “same
origin”), their <em>site</em> is <code>https</code>
and <code>example.com</code></li>
<li><code>https://example.com/foo</code> &
<code>https://foo.bar.example.com/bar</code> are “same site” (but not
“same origin”), <em>site</em> is identically <code>https</code>
and <code>example.com</code></li>
<li><code>https://www.example.com/foo</code> &
<code>https://foo.bar.example.com/bar</code> are “same site”, <em>site</em>
of both is <code>https</code> and <code>example.com</code></li>
<li><code>https://www.cam.ac.uk/</code> &
<code>https://www.cambridgestudents.cam.ac.uk/</code> are “same site”, their
<em>site</em> is <code>https</code> and <code>cam.ac.uk</code></li>
</ul>
<p>❌</p>
<ul>
<li><code>https://example.com/foo</code> &
<code>http://example.com/bar</code> have different protocols and they are not
“same site” even though their registrable domain is the same</li>
<li><code>https://www.ox.ac.uk/</code> & <code>https://www.cam.ac.uk/</code>
are not “same site”, they have different registrable domains
<code>ox.ac.uk</code> and <code>cam.ac.uk</code></li>
<li><code>https://example.com/</code> & <code>https://example.net/</code>
are not “same site”, they have different registrable domains
<code>example.com</code> and <code>example.net</code></li>
<li><code>https://example.com./</code> & <code>https://example.com/</code>
are not “same site”, they have different registrable domains
<code>example.com.</code> and <code>example.com</code>, that dot at the end is
significant</li>
</ul>
<p>“Same site” checks are used, for example, by the browser to restrict
cookies, which it either sends in the request or not, depending on whether you
clicked the link on the same <em>site</em> as the <em>site</em> of the target
address, according to the setting of the <code>SameSite</code> cookie
attribute.</p>
<p>The terms <em>site</em> and <em>same site</em> are described in more detail
in the <a href="https://html.spec.whatwg.org/multipage/browsers.html#sites">HTML
specification</a>. Also worth noting is that there is a <em>“same site”
comparison without a scheme</em> (<a
href="https://html.spec.whatwg.org/multipage/browsers.html#schemelessly-same-site">schemelessly
same site</a>), which compares only registrable domains. This was once used for
cookies, but then switched to “schemeful same site” matching, in which the
scheme also plays a role, which we now call simply “same site”.</p>
<h2 id="cross-origin-and-cross-site-requests">Cross-origin and cross-site
requests</h2>
<p>Requests that originate on one <em>origin</em> but load something from
another <em>origin</em> are called cross-origin requests, basically the opposite
of “same origin”.</p>
<p>Similarly, cross-site requests, the opposite of “same site”, were created
on one <em>site</em> but load something from another <em>site</em>. Just beware
that somewhere, e.g., in the names of attacks like Cross-Site Scripting (XSS) or
Cross-Site Request Forgery (CSRF), “site” is used in the general sense, not
in the “schema + registrable domain” sense explained above.</p>
<h2 id="prefer-same-origin">Prefer <em>same origin</em></h2>
<p>The concept of <em>site</em> and public suffixes and their list requires some
manual work to match the actual reality as much as possible, and it reminds me
of that <a href="https://xkcd.com/2347/">random person from Nebraska</a>. If you
were writing a specification, it would be more appropriate to use
<em>origin</em> and <em>same origin</em>, otherwise the definition of
<em>site</em> may differ from browser to browser, because each may use a
different version of <abbr title="public suffix list">PSL</abbr>.</p>
<p>The public suffix list is maintained by Mozilla, the maker of Firefox, so the
“big” browsers and the “smaller” ones built on top of them will probably
be fine. But it's still a good idea to consider whether you want to base the
security of your work on whether someone adds a line to a file and whether
someone else downloads that file in time. <a
href="https://github.com/sleevi/psl-problems#should-i-use-the-public-suffix-listetld1-for-">You
don't</a>.</p>
<p>Using “same site” also makes subdomains more attractive to attackers.
Subdomain takeover is a <a
href="https://developer.mozilla.org/en-US/docs/Web/Security/Subdomain_takeovers">real
attack</a> by which attackers can bypass various restrictions using “same
site” matching. For all this, they may even just need a forgotten subdomain
that is not used for anything.</p>
<p>Just prefer <em>same origin</em>.</p>https://www.michalspacek.com/overriding-http-response-content-in-chrome2023-10-05T00:00:00+02:002023-10-13T03:41:27+02:00<p>The Chrome browser (and others like Edge) allows you to override both HTTP
response headers and the response content. I've <a
href="https://www.michalspacek.com/overriding-http-response-headers-in-chrome">previously
written</a> about <a
href="https://www.michalspacek.com/overriding-http-response-headers-in-chrome">overriding
the headers</a> for testing purposes, let's see how you can override the body,
or the content itself, as well. Starting with Chrome 117 (released in September
2023) it's also greatly simplified.</p>Overriding HTTP response content in Chrome<h3>Updates</h3><ul><li><em><strong>13.10.</strong> Chrome 118 has added a <em>Has overrides</em> column and a
<code>has-overrides</code> filter</em></li></ul><p>Let's see the overrides in action on some example page, like
example.com:</p>
<ol>
<li>Open Developer Tools (with F12 or so)</li>
<li>Go to the <em>Network</em> tab</li>
<li>Load <a href="https://example.com">example.com</a></li>
<li>Choose a response you want to override</li>
<li>Right-click it and select <em>Override content</em></li>
</ol>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-content-overrides/override-content.png"
alt="Content overriding in DevTools in the Network tab" width="650"
height="767"></div>
<p>There are multiple content override entry points, for example here from the
<em>Response</em> tab:</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-content-overrides/override-content-response.png"
alt="Content overriding in DevTools in Response" width="600" height="360"></div>
<h2 id="allow-access">Allow access</h2>
<p>If you haven't set up any overrides yet, Chrome will first ask you to select
a directory to store them.</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-content-overrides/select-override-folder.png"
alt="The select directory window is displayed in/above DevTools" width="500"
height="130"></div>
<p>Choose an empty directory or create a new one and don't forget to allow
access to it. Both actions happen in different places, the latter one is below
the address bar, and it's pretty easy to miss, believe me.</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-content-overrides/devtools-requests-full-access.png"
alt="DevTools requests full access to the selected directory" width="630"
height="150"></div>
<h2 id="content-override">Content override</h2>
<p>You can make the modifications in Developer Tools in the <em>Sources</em> tab
in the <em>Overrides</em> sub-tab (click <code>»</code> if it is not visible),
don't forget to always save the modified file, with
<code>Ctrl</code>+<code>S</code> for example.</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-content-overrides/overriden-content-in-sources.png"
alt="Overriding content in Sources" width="650" height="270"></div>
<p>You can also use your favorite editor to directly edit the files stored in
the directory selected above, the changes will be reflected in Chrome.</p>
<p>Note that the overwritten content is marked with a blue dot on the file icon
in DevTools, and the ⚠️ icon in the <em>Network</em> tab will alert you that
overriding is enabled in general:</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-content-overrides/override-content-network.png"
alt="A blue dot on the file icon and a yellow triangle on the tab" width="360"
height="200"></div>
<p>Starting with Chrome 118, you can add a new <em>Has overrides</em> column to
your <em>Network</em> tab (right-click the column headers) and use a new
<code>has-overrides</code> filter:</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-content-overrides/has-overrides.png"
alt="has-overrides filter and Has overrides column" width="720"
height="290"></div>
<p>Try turning on the <code>index.html</code> content override on the
<code>example.com</code> domain, change the content of the <code>H1</code> tag
for example, save the file and reload the page. If you have Developer Tools
open, you should see your edit. For me it turned out like this, while this is
not really what the original page says 😁</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-content-overrides/override-content-example.png"
alt="example.com with an overridden HTML code" width="640" height="280"></div>
<p>With DevTools closed everything behaves normally, no overriding happens even
if you've configured it earlier.</p>
<h2 id="overriding-json">Overriding JSON</h2>
<p>It is possible to override not only HTML, but also JSON or images. For JSON
it is done the same way, in <em>Network</em> select the response you want to
edit, right-click, select <em>Override content</em> and edit the JSON as
you wish.</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-content-overrides/override-content-json.png"
alt="Editing a JSON override" width="670" height="270"></div>
<p>I tried it on a test application called <a
href="https://httpbin.org/">httpbin.org</a>, where I selected to test the HTTP
<code>POST</code> method for simplicity and then have overridden the contents of
the <code>https://httpbin.org/post</code> response. On the next AJAX request,
here triggered by a click on <em>Execute</em>, Chrome responded to itself with
the locally stored content, which resulted in a “can't parse JSON” message
because I had deliberately messed up the response contents.</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-content-overrides/overriden-content-json.png"
alt="Edited and overridden response JSON" width="600" height="430"></div>
<h2 id="overriding-images">Overriding images</h2>
<p>Overriding the content of images is essentially the same, the only difference
is that you can't edit the image in Chrome when you select <em>Override
content</em> on an image. Instead, you can replace the image file Chrome has
saved in the local override content directory in the location that matches the
URL path. The replaced file will be used the next time you load it with
DevTools open.</p>
<h2 id="disabling-overrides">Disabling overrides</h2>
<p>You can disable overrides with a checkbox in <em>Sources</em> >
<em>Overrides</em>, you can also right-click a particular override and select
<em>Delete</em>, or even wipe the whole configuration by clicking 🚫, which
essentially means <em>disallow access to the overrides directory</em>.</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-content-overrides/delete-override-content.png"
alt="Deleting overrides" width="440" height="530"></div>
<p>Sometimes some applications rely on getting <em>something</em> from the
server, and this rewriting can mess up that idea a bit. It can be useful for
testing, but also for shooting various videos when you want to show some
functionality that cannot be simply simulated with clicks.</p>https://www.michalspacek.com/validity-period-of-https-certificates-issued-from-a-user-added-ca-is-essentially-2-years2023-08-18T23:30:00+02:002023-08-18T23:30:00+02:00<p>Since 2020, maximum lifetime of HTTPS certificates is limited to 1 year,
exactly 398 days. I've <a
href="https://www.michalspacek.com/maximum-https-certificate-lifetime-to-be-1-year-soon">previously
written</a> about the history and the reasons behind the change. But the reduced
lifetime applies only to certificates issued from a public certification
authority (CA) added to the operating system's or the browser's trusted root
store by the vendor.</p>Validity period of HTTPS certificates issued from a user-added CA is
essentially 2 years<p>But the maximum lifetime of 1 year doesn't apply to certificates issued from
or by a private root certification authority added by the user or the
administrator of the computer either manually or with a tool like <a
href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/certutil"><code>certutil</code></a>
or <a href="https://github.com/FiloSottile/mkcert"><code>mkcert</code></a>.
That's also what Apple states on a page announcing the lifetime reduction <a
href="https://support.apple.com/en-us/HT211025">to 398 days</a>: <em>This
change will not affect certificates issued from user-added or
administrator-added Root CAs</em>.</p>
<div class="figure"><img
src="https://www.michalspacek.cz/i/images/blog/user-added-certification-authorities/macos-keychain-access-user-added-ca.png"
alt="" width="704" height="273">
<p>User-added certification authority in macOS Keychain Access</p>
</div>
<p>But if you open Safari and try to load a page using a certificate issued by
such private CA and valid for e.g. 5 years, the browser will refuse to load the
page. The only thing you'll see is the following error message:</p>
<blockquote>
<p><em>Safari cannot open the page because it could not establish a secure
connection to the server.</em></p>
</blockquote>
<p>That's it. There's no 🔓 <em>This Connection Is Not Private</em>, no
details, no “continue to the site anyway, I (don't) know what I'm doing”,
unlike in case of an expired or self-signed certificate.</p>
<h2 id="safari-rules">Safari rules</h2>
<p>This implies that Safari has some maximum lifetime rules or limits even for
certificates issued by private authorities. But my Google-fu has failed to
return anything useful so I had to do what was right: after finding a key for
my private CA (called “exploited CA”), getting <code>openssl</code>, a web
server, a Mac, and a calculator I've decided to empirically figure it out. I've
quickly realized that a certificate with a lifetime of 398 days worked as well
as a certificate valid for 800 days (2×398 + 4 days), but a certificate with
a lifetime of 1592 days (4×398) did not.</p>
<p>16 certificates later, using <a
href="https://en.wikipedia.org/wiki/Binary_search_algorithm">half-interval
search</a> to find the right number between 800 and 1592, I arrived at a
certificate with a validity of 825 days. That number seemed somewhat suspicious
to me, and maybe that's why it occurred to me at that exact moment that the
previous limit might still apply for private CAs. The previous max lifetime was,
guess… 825 days. Of course Apple mentions it in their <a
href="https://support.apple.com/en-us/HT210176">docs</a>:</p>
<blockquote>
<p><em>All TLS server certificates must comply with these new security
requirements in iOS 13 and macOS 10.15 […] TLS server certificates must have
a validity period of 825 days or fewer (as expressed in the NotBefore and
NotAfter fields of the certificate).</em></p>
</blockquote>
<p>That “all TLS certificates” is the important bit but of course
I couldn't find this page before. And even if I did, I probably wouldn't get
the connection anyway.</p>
<p>A certificate valid for 825 days worked of course, while a certificate
valid for 826 days did not.</p>
<div class="figure"><img
src="https://www.michalspacek.cz/i/images/blog/user-added-certification-authorities/safari-private-ca-cert-validity-2-years.png"
alt="" width="720" height="456">
<p>A <code>snafu.cz</code> certificate valid for 825 days, issued from my
user-added CA, as shown in Safari's certificate viewer</p>
</div>
<h2 id="no-limits-in-chrome-and-firefox">(No) limits in Chrome and Firefox</h2>
<p>Both Chrome and Firefox will load a page with a private CA-issued certificate
valid for 10 years, there seem to be no reduced lifetimes for user-added
certification authorities in these browsers.</p>
<div class="figure"><img
src="https://www.michalspacek.cz/i/images/blog/user-added-certification-authorities/chrome-cert-viewer-validity-10-years.png"
alt="" width="480" height="419">
<p>Chrome showing a certificate valid for 10 years (3650 days), don't mind the
<em>Common Name</em> field 😅</p>
</div>
<div class="figure"><img
src="https://www.michalspacek.cz/i/images/blog/user-added-certification-authorities/firefox-cert-viewer-validity-10-years.png"
alt="" width="360" height="630">
<p>A certificate valid for 10 years (3650 days) in Firefox
certificate viewer</p>
</div>
<p>But if you'd like your e.g. developer environment usable in Safari,
<strong>your internal certification authority should issue certificates valid
for 825 days max</strong>, that's 2 years, 3 months and a few days. And of
course, like with the latest lifetime reduction, it's Apple's
“fault” 😊</p>
<p>And this is also why I dare to say that the validity of certificates issued
from a user-added authorities is essentially limited to 2 years, even though
this is really the case in just one browser. With the current validity limit of
398 days for certificates from public CAs, it is recommended to issue
certificates with a maximum validity of 397 days, probably in case something
miscalculates the seconds during daylight-saving shifts or so. In this case I'd
also recommend setting a maximum of 824 days.</p>
<p>It's best to simplify or even automate it so that you can easily issue
certificates valid for a maximum of one year or even less.</p>https://www.michalspacek.com/overriding-http-response-headers-in-chrome2023-05-04T00:00:00+02:002023-10-05T19:02:46+02:00<p>Starting with Chrome 113 (and in other browsers like Edge), you can override
HTTP response headers, or add a new one. This is handy as you can override e.g.
some security headers for testing. The HTTP response header override will be
applied before things like <abbr title="Content Security Policy">CSP</abbr> are
processed so you can modify the Content Security Policy for the page for
example.</p>Overriding HTTP response headers in Chrome<h3>Updates</h3><ul><li><em><strong>5.10.</strong> There's now an easier way in Chrome 117 and newer</em></li></ul><p>There are many other tools which allow you to do the same or even more, like
for example <a href="https://www.telerik.com/fiddler">Fiddler</a>, <a
href="https://portswigger.net/burp/documentation/desktop/tools/proxy">Burp
Proxy</a> or the <a href="https://tamper.dev/">Tamper Dev</a> extension, but
it's always nice to have the ability directly in your browser, supported by the
vendor, because sometimes you don't want to or even can't download or install
anything else.</p>
<p>Let's test it out by modifying a Content Security Policy (CSP) header sent
by my <a href="https://canhas.report/"><abbr
title="Content Security Policy">CSP</abbr> demo app</a>:</p>
<ol>
<li>Load <a href="https://canhas.report/">canhas.report</a></li>
<li>Open DevTools (F12)</li>
<li>Select <em>Network</em> tab</li>
<li>Open the first demo “<a
href="https://canhas.report/csp-report-uri">Content Security Policy
<code>report-uri</code></a>”</li>
<li>In DevTools, select the document URL (displayed as
<code>csp-report-uri</code>)</li>
<li>Right-click and choose <em>Override headers</em></li>
<li>You can edit headers in the right panel</li>
</ol>
<div class="figure"><img
src="https://www.michalspacek.cz/i/images/blog/chrome-header-overrides/override-headers.png"
alt="The right-click menu and response headers in DevTools Network tab in Chrome 117 and newer"
width="720" height="700">
<p>The right-click menu with the <em>Override headers</em> item, in the same
menu you'll see <em>Override content</em> as well, see <a
href="https://www.michalspacek.com/overriding-http-response-content-in-chrome">my
other article</a></p>
</div>
<p>Another option, the only one before Chrome 117, is to hover over the
<code>Content-Security-Policy</code> response header, and once you see a pencil
icon, click it:</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-header-overrides/override-header.png"
alt="Response headers in DevTools Network tab" width="710" height="320"></div>
<h2 id="granting-permission">Granting permission</h2>
<p>Chrome asks for a folder to store the overrides in just once, unless you
remove the configuration.</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-header-overrides/select-folder.png"
alt="Select folder dialog in/above DevTools" width="500" height="86"></div>
<p>The header overrides are just JSON files named <code>.headers</code> stored
in a directory structure that mirrors the URL. You can manually edit the
override files, remove them or create them as you wish, and the changes will be
immediately reflected in Chrome. Here's an example
<code>.headers</code> file:</p>
<pre>[
{
"applyTo": "*",
"headers": [
{
"name": "header-name-1",
"value": "header value"
}
]
},
{
"applyTo": "csp-report-uri",
"headers": [
{
"name": "content-security-policy",
"value": "default-src 'none'; script-src 'nonce-7Q+TqJ9I61ubCQN+ZSLrQoif' 'self' 'report-sample'; style-src 'self'; report-uri https://degum.has.report/report"
}
]
}
]</pre>
<p>Granting the permission requires two steps: first you select the folder and
then you have to grant permission for DevTools to access the folder. Each of the
dialogs is placed elsewhere, the first one is at the top of the DevTools window,
the other one is at the top of the page.</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-header-overrides/devtools-requests-full-access.png"
alt="DevTools requests full access to the selected folder" width="720"
height="122"></div>
<p>If you're like me, you'll always miss the second one and then you'll be
wondering why you see “Unable to add filesystem: <permission denied>”
in your console.</p>
<h2 id="overriding-and-adding-headers">Overriding and adding headers</h2>
<p>Once the folder is selected and DevTools can access it, you can modify the
header. Let's say the original <abbr title="Content Security Policy">CSP</abbr>
header may look like this:</p>
<pre>Content-Security-Policy: default-src 'none'; img-src 'self' https://www.michalspacek.cz; script-src [...]</pre>
<p>Meaning the browser will download nothing by default
(<code>default-src</code>), images (<code>img-src</code>) only from
<code>https://canhas.report</code> (<code>'self'</code>) and
<code>https://www.michalspacek.cz</code> and so on. Now let's try this:</p>
<ol>
<li>Remove <code>img-src 'self' https://www.michalspacek.cz;</code> so the
browser will refuse to download any image (because
<code>default-src 'none'</code>)</li>
<li>Hit Enter and notice the overridden header is highlighted and the font has
also changed</li>
<li>Reload the page and suddenly there are many broken images on the page</li>
<li>DevTools console says “Refused to load the image ‘<URL>’ because
it violates the following Content Security Policy directive: "default-src
‘none’”. Note that ‘img-src’ was not explicitly set, so
‘default-src’ is used as a fallback."</li>
<li>…</li>
<li>You have now successfully overridden a response header 🎉</li>
</ol>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-header-overrides/overridden-response-header.png"
alt="Overridden HTTP response header, highlighted" width="700"
height="140"></div>
<p>The console will also warn you that the browser “Refused to execute inline
script because it violates the following Content Security Policy directive:
"script-src ‘nonce-[…]’” because now the random nonce in the HTML
<code><script></code> tags doesn't match the hardcoded one in the
overridden header but let's ignore that in this case.</p>
<p>The overrides work only when DevTools is open. To remove a particular
override, you can click the bin icon next to the overridden header and reload
the page:</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-header-overrides/remove-override.png"
alt="An icon to remove the override" width="500" height="97"></div>
<p>You can also add a completely new response header if you want:</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-header-overrides/add-header.png"
alt="Add a new header with this button" width="320" height="323"></div>
<p>When some overrides were used for the response, you'll see a dot in front of
the HTTP status code in the <em>Status</em> column. When overrides are enabled,
a “Header overrides” link is displayed on the right side in the <em>Response
Headers</em> area.</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-header-overrides/header-overrides.png"
alt="Header overrides link in Response Headers" width="550" height="140"></div>
<h2 id="all-overrides">All overrides</h2>
<p>Clicking it will open <em>Sources</em>, <em>Overrides</em> tab (might be
hidden behind <code>»</code>), another place to modify your overrides. The left
pane shows the folder structure and the <code>.headers</code> files, right-click
to delete them. You can disable overrides completely in the same area and you'll
notice the ⚠️ icon to disappear from the <em>Network</em> tab when you do
so. Click the 🚫 icon to clear the configuration which effectively means
<em>remove the permission to access the folder with overrides</em>.</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/chrome-header-overrides/sources-overrides.png"
alt="Sources, Overrides tab in DevTools" width="720" height="400"></div>
<p>Here you can also configure other local overrides, i.e. <em>instead of
loading the URL, respond with this local file</em>, not just headers. See my
newer article on <a
href="https://www.michalspacek.com/overriding-http-response-content-in-chrome">how
to enable content overrides</a>.</p>
<p>The current response, where you edit the headers, can't be overridden,
it's too late, but what you'll actually do by editing HTTP headers is that
you'll create an override for the next response from the same URL, even if it
feels like you're editing the current response.</p>
<p>My Chrome has just updated to 113 so yours should soon too, if not already.
Editing HTTP response headers is a cool feature, hope to see it in other
developer tools soon, too!</p>https://www.michalspacek.com/check-vulnerable-packages-with-composer-audit2023-01-25T18:00:00+01:002023-01-27T11:31:39+01:00<p>When a security vulnerability is discovered in one of the PHP libraries you
use, there are several options how you can learn about the bug before it's too
late. I've written about <em>PHP Security Advisories Database</em> in one of my
previous posts and how you can use it with <em>Roave Security Advisories</em>
and a few other ways. However all of them require an extra package or
a tool.</p>Check vulnerable packages with <code>composer audit</code><h3>Updates</h3><ul><li><em><strong>27.1.</strong> With <code>--locked</code>, the audit is based on <code>composer.lock</code>
instead of the installed packages</em></li></ul><p>The <a
href="https://github.com/Roave/SecurityAdvisories"><em>roave/security-advisories</em></a>
package has a long list of vulnerable libraries including versions, and trying
to install one of those will fail with a warning. Besides the package I also
use a GitHub Action called <a
href="https://github.com/marketplace/actions/the-php-security-checker">The PHP
Security Checker</a>, which in fact is the <a
href="https://symfony.com/doc/current/setup.html#checking-security-vulnerabilities">Symfony
CLI security check</a> packaged as an action. I've written about all these in my
<a
href="https://www.michalspacek.com/dont-let-security-bugs-catch-you-off-guard">previous
article</a>, including examples.</p>
<p>But if you don't want to use anything extra and would like to utilize
something you already use, then I have some good news for you. Starting with
version 2.4 (<a href="https://blog.packagist.com/composer-2-4/">released in
summer 2022</a>), Composer can query the database directly. It does that
automatically after every <code>composer require</code> and you can also check
installed packages manually with <code>composer audit</code>. Composer uses its
<a href="https://packagist.org/apidoc#list-security-advisories">own API</a>, see
for example a <a
href="https://packagist.org/api/security-advisories/?packages[]=guzzlehttp/guzzle">list
of vulnerable <em>guzzlehttp/guzzle</em> versions</a>.</p>
<p>Packages are also checked after running <code>composer update</code> (in both
cases the audit can be skipped with <code>--no-audit</code>) but not by default
after <code>composer install</code> – the reason may be that it's often done
automatically and there's no one to see the result anyway, or possibly the fact
that it's done so often, for example when running tests, that it may lead to
just too much API queries. But if you want, you can still run an audit after
installation is complete with <code>--audit</code>.</p>
<h2 id="after-composer-require">After <code>composer require</code></h2>
<p>Let's preview how it looks like. Let's say I'd like to install a package
version with a known vulnerability, like for example Guzzle 7.4.4 – Composer
will install it but it will complain and alert me, check the last
two lines:</p>
<pre>$ composer require guzzlehttp/guzzle:7.4.4
./composer.json has been created
Running composer update guzzlehttp/guzzle
Loading composer repositories with package information
Updating dependencies
Lock file operations: 8 installs, 0 updates, 0 removals
- Locking guzzlehttp/guzzle (7.4.4)
- Locking guzzlehttp/promises (1.5.2)
- Locking guzzlehttp/psr7 (2.4.3)
- Locking psr/http-client (1.0.1)
- Locking psr/http-factory (1.0.1)
- Locking psr/http-message (1.0.1)
- Locking ralouphie/getallheaders (3.0.3)
- Locking symfony/deprecation-contracts (v3.2.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 8 installs, 0 updates, 0 removals
- Installing symfony/deprecation-contracts (v3.2.0): Extracting archive
- Installing psr/http-message (1.0.1): Extracting archive
- Installing psr/http-client (1.0.1): Extracting archive
- Installing ralouphie/getallheaders (3.0.3): Extracting archive
- Installing psr/http-factory (1.0.1): Extracting archive
- Installing guzzlehttp/psr7 (2.4.3): Extracting archive
- Installing guzzlehttp/promises (1.5.2): Extracting archive
- Installing guzzlehttp/guzzle (7.4.4): Extracting archive
2 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
4 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
Found 2 security vulnerability advisories affecting 1 package.
Run composer audit for a full list of advisories.</pre>
<p>It's more prominent with colors in your terminal:</p>
<div><img
src="https://www.michalspacek.cz/i/images/blog/composer/require-vulnerable.png"
alt="Poslední 2 řádky z výstupu composer require" width="630"
height="49"></div>
<p>You can change the format, see below, but still, you could easily miss it.
Especially if you'd run <code>composer require</code> with <code>--quiet</code>
meaning “do not output any message”.</p>
<h2 id="composer-audit"><code>composer audit</code></h2>
<p>Maybe the very last line is more important then, it says to run
<code>composer audit</code>:</p>
<pre>$ composer audit
Found 2 security vulnerability advisories affecting 1 package:
+-------------------+----------------------------------------------------------------------------------+
| Package | guzzlehttp/guzzle |
| CVE | CVE-2022-31091 |
| Title | Change in port should be considered a change in origin |
| URL | https://github.com/guzzle/guzzle/security/advisories/GHSA-q559-8m2m-g699 |
| Affected versions | >=7,<7.4.5|>=4,<6.5.8 |
| Reported at | 2022-06-20T22:24:00+00:00 |
+-------------------+----------------------------------------------------------------------------------+
+-------------------+----------------------------------------------------------------------------------+
| Package | guzzlehttp/guzzle |
| CVE | CVE-2022-31090 |
| Title | CURLOPT_HTTPAUTH option not cleared on change of origin |
| URL | https://github.com/guzzle/guzzle/security/advisories/GHSA-25mq-v84q-4j7r |
| Affected versions | >=7,<7.4.5|>=4,<6.5.8 |
| Reported at | 2022-06-20T22:24:00+00:00 |
+-------------------+----------------------------------------------------------------------------------+</pre>
<p>That's much better. Here you see the vulnerable package name and even the
vulnerability. Even the return value is not zero which means something went
wrong, so this could be used in scripts, too:</p>
<pre>$ echo $?
1</pre>
<p>This is how it will look like when trying to install the newest version
without known security vulnerabilities:</p>
<pre>$ composer require guzzlehttp/guzzle
[...]
No security vulnerability advisories found
Using version ^7.5 for guzzlehttp/guzzle
[...]</pre>
<p>And this is the <code>composer audit</code> output then, including the
command's return code which is now zero, meaning everything's alright:</p>
<pre>$ composer audit
No security vulnerability advisories found
$ echo $?
0</pre>
<p>For <code>composer audit</code> to work properly the packages must be
installed by default. But if you use <code>--locked</code>
(<code>composer audit --locked</code>) then the audit is based just on the
<code>composer.lock</code> file and there's no need to install the packages
beforehand.</p>
<p>Use <code>--no-dev</code> if, for whatever reason, you'd like to disable
auditing packages listed in <code>require-dev</code>. I'd personally check
everything though.</p>
<h2 id="as-a-github-action">As a GitHub Action</h2>
<p>Thanks to the non-zero return value when a package with a known vulnerability
is installed, <code>composer audit</code> can easily be used in GitHub Actions
for example. And that's exactly <a
href="https://github.com/spaze/michalspacek.cz/blob/5ef1f83318b197ae8dcea832e0f3e5cdeb3d2b6d/.github/workflows/composer-vulns.yml#L23-L27">how
I'm using it</a> as well:</p>
<pre
class="yaml"><code>composer-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: composer audit</code></pre>
<p>I run this check every two hours so that I know about a possible problem
and the need to update in time. For a more important application than my
website, I'd be happy to run it more often, like every hour.</p>
<div class="figure"><img
src="https://www.michalspacek.cz/i/images/blog/github/actions-dependency-vulnz-checker.png"
alt="The result of running a package check every 2 hours on GitHub Actions"
width="365" height="350">
<p>Running a check on GitHub Actions</p>
</div>
<h2 id="output-format">Output format</h2>
<p>Composer can output the vulnerability information in several formats:</p>
<ul>
<li><code>table</code>: a table, see above, default for
<code>composer audit</code></li>
<li><code>plain</code>: a textual output, no table</li>
<li><code>json</code>: guess</li>
<li><code>summary</code>: just a short info whether something has been found,
default for <code>composer require</code>,
<code>composer update</code>, <code>composer install</code></li>
</ul>
<p>You can change the format with:</p>
<ul>
<li><code>--audit-format=FORMAT</code> for <code>composer require</code>,
<code>composer update</code>, <code>composer install</code></li>
<li><code>--format=FORMAT</code> for <code>composer audit</code></li>
</ul>
<p>One more thing: you can update Composer by running
<code>composer self-update</code>.</p>