A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="en">
  4. <!-- Has to be within the first 1024 bytes, hence before the `title` element
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Unsigned Commits (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f7f7f7">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
  26. <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
  27. <!-- Is that even respected? Retrospectively? What a shAItshow…
  28. https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
  29. <meta name="robots" content="noai, noimageai">
  30. <!-- Documented, feel free to shoot an email. -->
  31. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  32. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  35. <link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
  36. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  37. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  38. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  39. <script>
  40. function toggleTheme(themeName) {
  41. document.documentElement.classList.toggle(
  42. 'forced-dark',
  43. themeName === 'dark'
  44. )
  45. document.documentElement.classList.toggle(
  46. 'forced-light',
  47. themeName === 'light'
  48. )
  49. }
  50. const selectedTheme = localStorage.getItem('theme')
  51. if (selectedTheme !== 'undefined') {
  52. toggleTheme(selectedTheme)
  53. }
  54. </script>
  55. <meta name="robots" content="noindex, nofollow">
  56. <meta content="origin-when-cross-origin" name="referrer">
  57. <!-- Canonical URL for SEO purposes -->
  58. <link rel="canonical" href="https://blog.glyph.im/2024/01/unsigned-commits.html">
  59. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  60. <article>
  61. <header>
  62. <h1>Unsigned Commits</h1>
  63. </header>
  64. <nav>
  65. <p class="center">
  66. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  67. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  68. </svg> Accueil</a> •
  69. <a href="https://blog.glyph.im/2024/01/unsigned-commits.html" title="Lien vers le contenu original">Source originale</a>
  70. <br>
  71. Mis en cache le 2024-01-25
  72. </p>
  73. </nav>
  74. <hr>
  75. <p>I am going to tell you why I don’t think you should sign your Git commits, even
  76. though doing so with SSH keys is now easier than ever. But first, to
  77. contextualize my objection, I have a brief hypothetical for you, and then a bit
  78. of history from the evolution of security on the web.</p>
  79. <hr>
  80. <p><img alt="paper reading “Sign Here:” with a pen poised over it" src="https://blog.glyph.im/images/just-sign-here.jpeg"></p>
  81. <hr>
  82. <p>It seems like these days, everybody’s signing all different kinds of papers.</p>
  83. <p>Bank forms, permission slips, power of attorney; it seems like if you want to
  84. securely validate a document, you’ve gotta sign it.</p>
  85. <p>So I have invented a machine that automatically signs every document on your
  86. desk, just in case it needs your signature. Signing is good for security, so
  87. you should probably get one, and turn it on, just in case something needs your
  88. signature on it.</p>
  89. <p>We also want to make sure that verifying your signature is easy, so we will
  90. have them all notarized and duplicates stored permanently and publicly for
  91. future reference.</p>
  92. <p>No? Not interested?</p>
  93. <hr>
  94. <p>Hopefully, that sounded like a silly idea to you.</p>
  95. <p>Most adults in modern civilization have learned that signing your name to a
  96. document has an <em>effect</em>. It is not merely decorative; the words in the
  97. document being signed have some specific meaning and can be enforced against
  98. you.</p>
  99. <p>In some ways the metaphor of “signing” in cryptography is bad. One does not
  100. “sign” things with “keys” in real life. But here, it is spot on: a
  101. cryptographic signature can have an effect.</p>
  102. <p>It should be an <em>input</em> to some software, one that is acted upon. Software
  103. does a thing differently depending on the presence or absence of a signature.
  104. If it doesn’t, the signature probably shouldn’t be there.</p>
  105. <hr>
  106. <p>Consider the most venerable example of encryption and signing that we all deal
  107. with every day: HTTPS. Many years ago, browsers would happily display
  108. unencrypted web pages. The browser would also encrypt the connection, if the
  109. server operator had paid for an expensive certificate and correctly configured
  110. their server. If that operator messed up the encryption, it would pop up a
  111. helpful dialog box that would tell the user “This website did something wrong
  112. that you cannot possibly understand. Would you like to ignore this and keep
  113. working?” with buttons that said “Yes” and “No”.</p>
  114. <p>Of course, these are not the precise words that were written. The words, as
  115. written, said things about “information you exchange” and “security
  116. certificate” and “certifying authorities” but “Yes” and “No” were the words
  117. that most users <em>read</em>. Predictably, most users just clicked “Yes”.</p>
  118. <p>In the usual case, where users ignored these warnings, it meant that no user
  119. ever got meaningful security from HTTPS. It was a component of the web stack
  120. that did nothing but funnel money into the pockets of certificate authorities
  121. and occasionally present annoying interruptions to users.</p>
  122. <p>In the case where the user carefully read and honored these warnings in the
  123. spirit they were intended, adding any sort of transport security to your
  124. website was a potential liability. If you got everything perfectly correct,
  125. nothing happened except the browser would display a picture of a <a href="https://www.wired.com/2016/11/googles-chrome-hackers-flip-webs-security-model/">small green
  126. purse</a>. If
  127. you made any small mistake, it would scare users off and thereby directly harm
  128. your business. You would only want to do it if you were doing something that
  129. put a big enough target on your site that you became unusually interesting to
  130. attackers, or were required to do so by some contractual obligation like credit
  131. card companies.</p>
  132. <p>Keep in mind that the second case here is the <em>best</em> case.</p>
  133. <p>In 2016, the browser makers noticed this problem and started taking some
  134. <a href="https://security.googleblog.com/2016/09/moving-towards-more-secure-web.html">pretty aggressive
  135. steps</a>
  136. towards actually enforcing the security that HTTPS was supposed to provide, by
  137. fixing the user interface to do the right thing. If your site didn’t have
  138. security, it would be shown as “Not Secure”, a subtle warning that would
  139. gradually escalate in intensity as time went on, correctly incentivizing site
  140. operators to adopt transport security certificates. On the user interface
  141. side, certificate errors would be significantly harder to disregard, making it
  142. so that users who didn’t understand what they were seeing would actually be
  143. stopped from doing the dangerous thing.</p>
  144. <p>Nothing fundamental<sup id="fnref:1:unsigned-commits-2024-1"></sup> changed about the technical aspects of the
  145. cryptographic primitives or constructions being used by HTTPS in this time
  146. period, but <em>socially</em>, the meaning of an HTTP server signing and encrypting
  147. its requests changed a lot.</p>
  148. <hr>
  149. <p>Now, let’s consider signing Git commits.</p>
  150. <p>You may have heard that in some abstract sense you “should” be signing your
  151. commits. GitHub puts a little green “verified” badge next to commits that are
  152. signed, which is neat, I guess. They provide “security”. 1Password provides a
  153. <a href="https://developer.1password.com/docs/ssh/git-commit-signing/">nice UI</a> for
  154. setting it up. If you’re not a 1Password user, GitHub itself recommends you
  155. <a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/telling-git-about-your-signing-key#telling-git-about-your-ssh-key">put in just a few lines of
  156. configuration</a>
  157. to do it with either a GPG, SSH, or even an S/MIME key.</p>
  158. <p>But while GitHub’s documentation quite lucidly tells you <em>how</em> to sign your
  159. commits, its explanation of
  160. <a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification">why</a>
  161. is somewhat less clear. Their purse is the word “Verified”; it’s still green.
  162. If you enable “<a href="https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits">vigilant
  163. mode</a>”,
  164. you can make the blank “no verification status” option say “Unverified”, but
  165. not much else changes.</p>
  166. <p>This is like the old-style HTTPS verification “Yes”/“No” dialog, except that
  167. there is not even an interruption to your workflow. They might put the
  168. “Unverified” status on there, but they’ve gone ahead and clicked “Yes” for you.</p>
  169. <p>It is tempting to think that the “HTTPS” metaphor will map neatly onto Git
  170. commit signatures. It was bad when the web wasn’t using HTTPS, and the next
  171. step in that process was for <a href="https://en.wikipedia.org/wiki/Let%27s_Encrypt">Let’s
  172. Encrypt</a> to come along and for
  173. the browsers to fix their implementations. Getting your certificates properly
  174. set up in the meanwhile and becoming familiar with the tools for properly doing
  175. HTTPS was unambiguously a <em>good</em> thing for an engineer to do. I did, and I’m
  176. quite glad I did so!</p>
  177. <p>However, there is a significant difference: signing and encrypting an HTTPS
  178. request is ephemeral; signing a Git commit is functionally permanent.</p>
  179. <p>This ephemeral nature meant that errors in the early HTTPS landscape were
  180. easily fixable. Earlier I mentioned that there was a time where you might
  181. <em>not</em> want to set up HTTPS on your production web servers, because any small
  182. screw-up would break your site and thereby your business. But if you were
  183. really skilled and you could see the future coming, you could set up
  184. monitoring, avoid these mistakes, and rapidly recover. These mistakes didn’t
  185. need to badly break your site.</p>
  186. <p>We <em>can</em> extend the analogy to HTTPS, but we have to take a detour into one of
  187. the more unpleasant mistakes in HTTPS’s history: <a href="https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning">HTTP Public Key
  188. Pinning</a>, or “HPKP”.
  189. The idea with HPKP was that you could publish a record in an HTTP header where
  190. your site commits<sup id="fnref:2:unsigned-commits-2024-1"></sup> to using certain certificate authorities for a period of
  191. time, where that period of time could be “forever”. Attackers gonna attack,
  192. and <a href="https://scotthelme.co.uk/using-security-features-to-do-bad-things/#usinghpkpforevil">attack they
  193. did</a>.
  194. Even without getting attacked, a site could easily commit “HPKP Suicide” where
  195. they would pin the wrong certificate authority with a long timeline, and their
  196. site was effectively gone for every browser that had ever seen those pins. As
  197. a result, after a few years, HPKP was <a href="https://scotthelme.co.uk/hpkp-is-no-more/">completely removed from all
  198. browsers</a>.</p>
  199. <p>Git commit signing is even worse. With HPKP, you could easily make terrible
  200. mistakes with permanent consequences even though you knew the <em>exact</em> meaning
  201. of the data you were putting into the system at the time you were doing it.
  202. With signed commits, you are saying something permanently, but you don’t really
  203. know <em>what</em> it is that you’re saying.</p>
  204. <hr>
  205. <p><em>Today</em>, what is the benefit of signing a Git commit? GitHub might present it
  206. as “Verified”. It’s worth noting that only <em>GitHub</em> will do this, since they
  207. are the root of trust for this signing scheme. So, by signing commits and
  208. registering your keys with GitHub, you are, at best, helping to lock in GitHub
  209. as a permanent piece of infrastructure that is even harder to dislodge because
  210. they are not only where your code is stored, but also the arbiters of whether
  211. or not it is trustworthy.</p>
  212. <p><em>In the future</em>, what is the possible security benefit? If we all collectively
  213. decide we want Git to be more secure, then we will need to meaningfully treat
  214. signed commits differently from unsigned ones.</p>
  215. <p>There’s a long tail of unsigned commits several billion entries long. And
  216. those are in the permanent record as much as the signed ones are, so future
  217. tooling will have to be able to deal with them. If, as stewards of Git, we
  218. wish to move towards a more secure Git, as the stewards of the web moved
  219. towards a more secure web, we do <em>not</em> have the option that the web did. In
  220. the browser, the meaning of a plain-text HTTP or incorrectly-signed HTTPS site
  221. changed, in order to encourage the site’s operator to <em>change</em> the site to be
  222. HTTPS.</p>
  223. <p>In contrast, the meaning of an unsigned commit cannot change, because there are
  224. zillions of unsigned commits lying around in critical infrastructure and we
  225. need them to remain there. Commits cannot meaningfully be changed to become
  226. signed retroactively. Unlike an online website, they are part of a historical
  227. record, not an operating program. So we cannot establish the difference in
  228. treatment by changing how unsigned commits are treated.</p>
  229. <p>That means that tooling maintainers will need to provide some difference in
  230. behavior that provides some incentive. With HTTPS where the binary choice was
  231. clear: don’t present sites with incorrect, potentially compromised
  232. configurations to users. The question was just <em>how</em> to achieve that. With
  233. Git commits, the difference in treatment of a “trusted” commit is far less
  234. clear.</p>
  235. <p>If you will forgive me a slight straw-man here, one possible naive
  236. interpretation is that a “trusted” signed commit is that it’s OK to run in CI.
  237. Conveniently, it’s not simply “trusted” in a general sense. If you signed it,
  238. it’s trusted to be <em>from you</em>, specifically. Surely it’s fine if we bill the
  239. CI costs for validating the PR that includes that signed commit to your GitHub
  240. account?</p>
  241. <p>Now, someone can piggy-back off a 1-line typo fix that you made on top of an
  242. unsigned commit to some large repo, making you implicitly responsible for
  243. transitively signing all unsigned parent commits, even though you haven’t
  244. looked at any of the code.</p>
  245. <p>Remember, also, that the only central authority that is practically trustable
  246. at this point is your GitHub account. That means that if you are using a
  247. third-party CI system, even if you’re using a third-party Git host, you can
  248. only run “trusted” code if GitHub is online and responding to requests for its
  249. “get me the trusted signing keys for this user” API. This also adds a lot of
  250. value to a GitHub credential breach, strongly motivating attackers to sneakily
  251. attach their own keys to your account so that their commits in unrelated repos
  252. can be “Verified” by you.</p>
  253. <p>Let’s review the pros and cons of turning on commit signing <em>now</em>, before you
  254. know what it is going to be used for:</p>
  255. <table>
  256. <thead>
  257. <tr>
  258. <th align="left">Pro</th>
  259. <th align="left">Con</th>
  260. </tr>
  261. </thead>
  262. <tbody>
  263. <tr>
  264. <td align="left">Green “Verified” badge</td>
  265. <td align="left"><strong>Unknown, possibly unlimited future liability</strong> for the consequences of running code in a commit you signed</td>
  266. </tr>
  267. <tr>
  268. <td align="left"><p></p></td>
  269. <td align="left">Further implicitly cementing GitHub as a centralized trust authority in the open source world</td>
  270. </tr>
  271. <tr>
  272. <td align="left"></td>
  273. <td align="left">Introducing unknown reliability problems into infrastructure that relies on commit signatures</td>
  274. </tr>
  275. <tr>
  276. <td align="left"></td>
  277. <td align="left">Temporary breach of your GitHub credentials now lead to potentially permanent consequences if someone can smuggle a new trusted key in there</td>
  278. </tr>
  279. <tr>
  280. <td align="left"></td>
  281. <td align="left">New kinds of ongoing process overhead as commit-signing keys become new permanent load-bearing infrastructure, like “what do I do with expired keys”, “how often should I rotate these”, and so on</td>
  282. </tr>
  283. </tbody>
  284. </table>
  285. <p>I feel like the “Con” column is coming out ahead.</p>
  286. <hr>
  287. <p>That probably seemed like increasingly unhinged hyperbole, and it was.</p>
  288. <p>In reality, the consequences are unlikely to be nearly so dramatic. The status
  289. quo has a very high amount of inertia, and probably the “Verified” badge will
  290. remain the only visible difference, except for a few repo-specific esoteric
  291. workflows, like pushing trust verification into offline or sandboxed build
  292. systems. I do still think that there is <em>some</em> potential for nefariousness
  293. around the “unknown and unlimited” dimension of any future plans that might
  294. rely on verifying signed commits, but any flaws are likely to be subtle attack
  295. chains and not anything flashy and obvious.</p>
  296. <p>But I think that one of the biggest problems in information security is a lack
  297. of <a href="https://owasp.org/www-community/Threat_Modeling">threat modeling</a>. We
  298. encrypt things, we sign things, we institute <a href="https://cryptosmith.com/password-sanity/exp-harmful/">rotation
  299. policies</a> and
  300. <a href="https://neal.fun/password-game/">elaborate</a> useless
  301. <a href="https://xkcd.com/936/">rules</a> for passwords, because we are looking for a
  302. “best practice” that is going to save us from having to think about what our
  303. actual security problems are.</p>
  304. <p>I think the actual harm of signing git commits is to perpetuate an engineering
  305. culture of unquestioningly cargo-culting sophisticated and complex tools like
  306. cryptographic signatures into new contexts where they have no use.</p>
  307. <p>Just from a baseline utilitarian philosophical perspective, for a given action
  308. A, all else being equal, it’s always better <em>not</em> to do A, because taking an
  309. action always has <em>some</em> non-zero opportunity cost even if it is just the time
  310. taken to do it. Epsilon cost and zero benefit is still a net harm. This is
  311. even more true in the context of a complex system. Any action taken in
  312. response to a rule in a system is going to interact with all the other rules in
  313. that system. You have to pay complexity-rent on every new rule. So an
  314. apparently-useless embellishment like signing commits can have potentially
  315. far-reaching consequences in the future.</p>
  316. <p>Git commit signing itself is not particularly consequential. I have probably
  317. spent more time writing this blog post than the sum total of all the time
  318. wasted by all programmers configuring their git clients to add useless
  319. signatures; even the relatively modest readership of this blog will likely
  320. transfer more data reading this post than all those signatures will take to
  321. transmit to the various git clients that will read them. If I just convince
  322. you not to sign your commits, I don’t think I’m coming out ahead in the
  323. <a href="https://en.wikipedia.org/wiki/Felicific_calculus">felicific calculus</a> here.</p>
  324. <p>What I am actually trying to point out here is that it is useful to <em>carefully
  325. consider how to avoid adding junk complexity to your systems</em>. One area where
  326. junk tends to leak in to designs and to cultures particularly easily is in
  327. intimidating subjects like trust and safety, where it is easy to get anxious
  328. and convince ourselves that piling on more <em>stuff</em> is safer than leaving things
  329. simple.</p>
  330. <p>If I can help you avoid adding even a little bit of unnecessary complexity, I
  331. think it will have been well worth the cost of the writing, and the reading.</p>
  332. <h2 id="acknowledgments">Acknowledgments</h2>
  333. <p class="update-note">Thank you to <a href="https://www.patreon.com/creatorglyph">my patrons</a> who are
  334. supporting my writing on this blog. If you like what you’ve read here and
  335. you’d like to read more of it, or you’d like to support my <a href="https://github.com/glyph/">various open-source
  336. endeavors</a>, you can <a href="https://www.patreon.com/join/8655595">support me on Patreon as
  337. well</a>! I am also <a href="mailto:consulting@glyph.im">available for
  338. consulting work</a> if you think your organization
  339. could benefit from expertise on topics such as “What <em>else</em> should I <em>not</em> apply a cryptographic signature to?”.</p>
  340. </article>
  341. <hr>
  342. <footer>
  343. <p>
  344. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  345. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  346. </svg> Accueil</a> •
  347. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  348. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  349. </svg> Suivre</a> •
  350. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  351. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  352. </svg> Pro</a> •
  353. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  354. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  355. </svg> Email</a> •
  356. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  357. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  358. </svg> Légal</abbr>
  359. </p>
  360. <template id="theme-selector">
  361. <form>
  362. <fieldset>
  363. <legend><svg class="icon icon-brightness-contrast">
  364. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  365. </svg> Thème</legend>
  366. <label>
  367. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  368. </label>
  369. <label>
  370. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  371. </label>
  372. <label>
  373. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  374. </label>
  375. </fieldset>
  376. </form>
  377. </template>
  378. </footer>
  379. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  380. <script>
  381. function loadThemeForm(templateName) {
  382. const themeSelectorTemplate = document.querySelector(templateName)
  383. const form = themeSelectorTemplate.content.firstElementChild
  384. themeSelectorTemplate.replaceWith(form)
  385. form.addEventListener('change', (e) => {
  386. const chosenColorScheme = e.target.value
  387. localStorage.setItem('theme', chosenColorScheme)
  388. toggleTheme(chosenColorScheme)
  389. })
  390. const selectedTheme = localStorage.getItem('theme')
  391. if (selectedTheme && selectedTheme !== 'undefined') {
  392. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  393. }
  394. }
  395. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  396. window.addEventListener('load', () => {
  397. let hasDarkRules = false
  398. for (const styleSheet of Array.from(document.styleSheets)) {
  399. let mediaRules = []
  400. for (const cssRule of styleSheet.cssRules) {
  401. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  402. continue
  403. }
  404. // WARNING: Safari does not have/supports `conditionText`.
  405. if (cssRule.conditionText) {
  406. if (cssRule.conditionText !== prefersColorSchemeDark) {
  407. continue
  408. }
  409. } else {
  410. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  411. continue
  412. }
  413. }
  414. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  415. }
  416. // WARNING: do not try to insert a Rule to a styleSheet you are
  417. // currently iterating on, otherwise the browser will be stuck
  418. // in a infinite loop…
  419. for (const mediaRule of mediaRules) {
  420. styleSheet.insertRule(mediaRule.cssText)
  421. hasDarkRules = true
  422. }
  423. }
  424. if (hasDarkRules) {
  425. loadThemeForm('#theme-selector')
  426. }
  427. })
  428. </script>
  429. </body>
  430. </html>