From 515d5e5229553fbf99ec510a72b9fb307af19607 Mon Sep 17 00:00:00 2001 From: anarsec <anarsec@riseup.net> Date: Mon, 26 Jun 2023 22:22:44 +0000 Subject: [PATCH] csp, remove js, various updates --- content/posts/_index.md | 2 +- content/posts/e2ee/index.md | 51 ++-- content/posts/qubes/index.md | 6 +- content/posts/tails-best/index.md | 2 +- themes/DeepThought/static/js/site.js | 330 ------------------------- themes/DeepThought/templates/base.html | 1 + 6 files changed, 20 insertions(+), 372 deletions(-) delete mode 100644 themes/DeepThought/static/js/site.js diff --git a/content/posts/_index.md b/content/posts/_index.md index 08072a8..1068a5c 100644 --- a/content/posts/_index.md +++ b/content/posts/_index.md @@ -2,5 +2,5 @@ title = "Guides" sort_by = "date" paginate_by = 10 -description = "All guides are maintained - see the changelog." +description = 'All guides are maintained.' +++ diff --git a/content/posts/e2ee/index.md b/content/posts/e2ee/index.md index 468e67e..03a36de 100644 --- a/content/posts/e2ee/index.md +++ b/content/posts/e2ee/index.md @@ -41,7 +41,7 @@ Like all peer-to-peer communication, Cwtch requires *synchronous* communication, Any Cwtch user can turn the app on their phone or computer into an untrusted server to host a group chat, though this is best for temporary needs like an event or short-term coordination, because the device needs to stay powered on for it to work. Medium-term untrusted servers can be set up on a spare Android device that can stay on, and longer-term servers can be self-hosted on a VPS if you know Linux system administration. Once the server exists, contacts can be invited to use it. You can create a group chat with only two people, which enables asynchronous direct messages. ->**Note**: [**Briar**](https://briarproject.org) is another application which works in a similar way (with peer-to-peer and Tor), and uses the [Bramble Transport Protocol](https://code.briarproject.org/briar/briar/-/wikis/A-Quick-Overview-of-the-Protocol-Stack) (BTP). The main distinguishing feature of Briar is that it continues to function [even when underlying network infrastructure is down](https://briarproject.org/how-it-works/). It was [audited in 2017](https://code.briarproject.org/briar/briar/-/wikis/FAQ#has-briar-been-independently-audited). Unfortunately, Briar Desktop does not yet work with Tails or Qubes-Whonix, because it cannot [use the system Tor](https://code.briarproject.org/briar/briar/-/issues/2095). Unlike Cwtch, to add a contact on Briar, you must both add each other first. You can either exchange `briar://` links or scan a contact’s QR code if they are nearby. +>**Note**: [**Briar**](https://briarproject.org) is another application which works in a similar way (with peer-to-peer and Tor), and uses the [Bramble Transport Protocol](https://code.briarproject.org/briar/briar/-/wikis/A-Quick-Overview-of-the-Protocol-Stack) (BTP). The main distinguishing feature of Briar is that it continues to function [even when underlying network infrastructure is down](https://briarproject.org/how-it-works/). It was [audited in 2017](https://code.briarproject.org/briar/briar/-/wikis/FAQ#has-briar-been-independently-audited). Unfortunately, Briar Desktop does not yet work with Tails or Qubes-Whonix, because it cannot [use the system Tor](https://code.briarproject.org/briar/briar/-/issues/2095). Unlike Cwtch, to connect with a contact on Briar, you must both add each other first. You can either exchange `briar://` links or scan a contact’s QR code if they are nearby. [Briar Mailbox](https://briarproject.org/download-briar-mailbox/) enables asynchronous communication. <details> <summary><strong>Cwtch Installation on GrapheneOS</strong></summary> @@ -53,27 +53,27 @@ Any Cwtch user can turn the app on their phone or computer into an untrusted ser <details> <summary><strong>Cwtch Installation on Tails</strong></summary> <br> -<p>There is no Tor <a href="https://tails.boum.org/contribute/design/stream_isolation/">Stream Isolation</a> for Cwtch on Tails, so each session must be run in a unique Tails session, or can otherwise be associated with Tor Browser activity, etc.</p> <ul> +<li>Start Tails with an Adminstration Password.</li> <li>Download <a href="https://cwtch.im/download/#linux">Cwtch for Linux</a> using Tor Browser</li> <li>Verify the download <ul> <li>Open the folder from Tor Browser's download icon </li> <li>Right click in the file manager and select "Open a Terminal Here"</li> -<li>Run <code>sha512sum cwtch-v1.10.0.tar.gz</code> (replacing the filename as appropriate)</li> +<li>Run <code>sha512sum cwtch-VERSION-NUMBER.tar.gz</code> (replacing the filename as appropriate)</li> <li>Compare the hash of the file with what is listed on the download page </li> </ul> </li> -<li>As per our <a href="/posts/tails-best/#using-a-write-protect-switch">Tails Best Practices</a>, personal data should be stored on a second LUKS USB, not on the Tails Persistent Storage. Copy the file to a second LUKS USB and extract it with the file manager (right click, select "Extract Here"). We will not be using the Additional Software Persistent Storage feature (because it is an AppImage so doesn't require it). </li> +<li>As per our <a href="/posts/tails-best/#using-a-write-protect-switch">Tails Best Practices</a>, personal data should be stored on a second LUKS USB, not on the Tails Persistent Storage. Copy the file to such a personal data LUKS USB and extract it with the file manager (right click, select "Extract Here"). We will not be using the Additional Software Persistent Storage feature - Cwtch is an AppImage so doesn't require it. </li> <li>Run the install script<ul> <li>In the File Manager, enter to directory you just created, <code>cwtch</code>. Right click in the File Manager and select "Open a Terminal Here"</li> -<li>Run <code>install-home.sh</code></li> -<li>TODO backup for persistence? Test</li> +<li>Run <code>install-tails.sh</code></li> </ul> </li> -<li>Tails forces all networking through Tor, so <a href="https://docs.cwtch.im/docs/tor#advanced-tor-configuration">Advanced Tor Configuration</a> must be set within Cwtch:<ul> -<li>Start Cwtch by TODO. </li> +<li>As the <a href="https://docs.cwtch.im/docs/platforms/tails">documentation</a> specifies, "When launching, Cwtch on Tails should be passed the CWTCH_TAILS=true environment variable". In the Terminal, run:<ul> +<li><code>exec env CWTCH_TAILS=true LD_LIBRARY_PATH=~/.local/lib/cwtch/:~/.local/lib/cwtch/Tor ~/.local/lib/cwtch/cwtch</code></li> </ul> </li> +<li>Cwtch must be re-installed every session you need to use it. Backup <code>`$HOME/.cwtch`</code> to the personal data LUKS USB, and copy it back into <code>$HOME/</code> the next time you install Cwtch.</li> <li>Updates must be made manually - back up your profile first.</li> </ul> <br> @@ -82,34 +82,9 @@ Any Cwtch user can turn the app on their phone or computer into an untrusted ser <details> <summary><strong>Cwtch Installation on Qubes-Whonix</strong></summary> <br> -<p>Cwtch on Whonix currently has an <a href="https://git.openprivacy.ca/cwtch.im/cwtch-ui/issues/550">issue</a> - it works, but there is no Tor Stream Isolation. This is resolved by creating a dedicated Cwtch qube. Cwtch is installed in an App qube, not a Template (because it is an AppImage).</p> -<ul> -<li>Download <a href="https://cwtch.im/download/#linux">Cwtch for Linux</a> using Tor Browser in a disposable Whonix qube.</li> -<li>Verify the download:<ul> -<li>Open the folder from Tor Browser's download icon </li> -<li>Right click in the file manager and select "Open a Terminal Here"</li> -<li>Run <code>sha512sum cwtch-v1.10.0.tar.gz</code> (replacing the filename as appropriate)</li> -<li>Compare the hash of the file with what is listed on the download page </li> -</ul> -</li> -<li><a href="/posts/qubes/#how-to-organize-your-qubes">Create an App qube</a> with the Template <code>whonix-ws-16</code> and networking <code>sys-whonix</code>.</li> -<li>Copy the file to your new Cwtch App qube and extract it with the file manager (right click, select "Extract Here"). </li> -<li>Run the install script<ul> -<li>In the File Manager, enter to directory you just created, <code>cwtch</code>. Right click in the File Manager and select "Open a Terminal Here"</li> -<li>Run <code>install-home.sh</code></li> -</ul> -</li> -<li>Reboot the App qube for Cwtch to show up in the <strong>Settings > Applications</strong> tab</li> -<li><code>sys-whonix</code> forces all networking through Tor, so <a href="https://docs.cwtch.im/docs/tor#advanced-tor-configuration">Advanced Tor Configuration</a> must be set within Cwtch:<ul> -<li>TODO </li> -</ul> -</li> -<li>Updates must be made manually - back up your profile first.</li> -</ul> +<p>Cwtch on Whonix currently has an <a href="https://git.openprivacy.ca/cwtch.im/cwtch-ui/issues/550">issue</a> - support is forthcoming. </p> </details> -<br> -  # OnionShare @@ -178,11 +153,12 @@ use_proxy = on http_proxy = 127.0.0.1:8082 https_proxy = 127.0.0.1:8082 ``` -* [Create an App qube](/posts/qubes/#how-to-organize-your-qubes) with the Template `whonix-ws-16-element` and networking `sys-whonix`. +* [Create an App qube](/posts/qubes/#how-to-organize-your-qubes) with the Template `whonix-ws-16-signal` and networking `sys-whonix`. * In the new App qube's **Settings > Applications** tab, bring Signal into the Selected column, and press **OK**. * Updates will be handled by **Qubes Update** as you would expect. -Alternatively, you can install Signal Desktop in a Whonix Workstation App qube by using [Qube Apps](https://micahflee.com/2021/11/introducing-qube-apps/), and you will not need to bother with Templates. Signal Desktop on Flathub is [community maintained](https://github.com/flathub/org.signal.Signal), not official, which [is a security consideration](https://www.kicksecure.com/wiki/Install_Software#Flathub_Package_Sources_Security). +>**Alternative:** You can install Signal Desktop in a Whonix Workstation App qube by using [Qube Apps](https://micahflee.com/2021/11/introducing-qube-apps/), and you will not need to bother with Templates. Signal Desktop on Flathub is [community maintained](https://github.com/flathub/org.signal.Signal), not official, which [is a security consideration](https://www.kicksecure.com/wiki/Install_Software#Flathub_Package_Sources_Security). + <br> <br> @@ -257,7 +233,8 @@ https_proxy = 127.0.0.1:8082 * Updates will be handled by **Qubes Update** as you would expect. * Avoid pressing "Sign Out", simply shutdown the qube when finished. -Alternatively, you can install Element Desktop in a Whonix Workstation App qube by using [Qube Apps](https://micahflee.com/2021/11/introducing-qube-apps/), and you will not need to bother with Templates. Element Desktop on Flathub is [community maintained](https://github.com/flathub/im.riot.Riot), not official, which [is a security consideration](https://www.kicksecure.com/wiki/Install_Software#Flathub_Package_Sources_Security). +>**Alternative:** You can install Element Desktop in a Whonix Workstation App qube by using [Qube Apps](https://micahflee.com/2021/11/introducing-qube-apps/), and you will not need to bother with Templates. Element Desktop on Flathub is [community maintained](https://github.com/flathub/im.riot.Riot), not official, which [is a security consideration](https://www.kicksecure.com/wiki/Install_Software#Flathub_Package_Sources_Security). + <br> <br> diff --git a/content/posts/qubes/index.md b/content/posts/qubes/index.md index c22cd39..36ca5b4 100644 --- a/content/posts/qubes/index.md +++ b/content/posts/qubes/index.md @@ -228,7 +228,7 @@ If your file is opening in a different application than what you require, you'll For PDF files, right-clicking will also give the option **Convert To Trusted PDF**. This will sanitize the PDF file so that it can go from being untrusted to trusted. This is achieved by it being converted into images in a disposable, and cleaning the metadata. -Particular types of files in an App qube can be set to be opened in a disposable by default. However, if I set PDF files to always open in a disposable, this is not failsafe - some files may end in `.pdf` but in reality be something else. [This guide](https://forum.qubes-os.org/t/opening-all-files-in-disposable-qube/4674) sets all file types to open in a disposable to mitigate this possibility. If you'd nonetheless like to open only PDF files in a disposable, right-click a PDF and select **Open With Other Application > qvm-open-in-dvm**. +Particular types of files in an App qube can be set to be opened in a disposable by default. However, if I set PDF files to always open in a disposable, this is not failsafe - some files may end in `.pdf` but in reality be something else. [This guide](https://forum.qubes-os.org/t/opening-all-files-in-disposable-qube/4674) sets all file types to open in a disposable to mitigate this possibility. If you'd nonetheless like to set the default of only opening PDF files in a disposable, right-click a PDF and select **Open With Other Application > qvm-open-in-dvm**. # How to Use Devices (like USBs) To learn how to attach devices, we will format the empty USB or hard drive you will be using for backups. The USB will be attached to an offline disposable to mitigate against [BadUSB attacks](https://en.wikipedia.org/wiki/BadUSB). @@ -268,7 +268,7 @@ Adapted from the [docs](https://www.qubes-os.org/doc/how-to-back-up-restore-and- >7. Once the backup is complete, test restore your backup. Go to **Applications menu > Qubes Tools > Restore Backup**. DO NOT FORGET to select **Test restore to verify backup integrity (no data actually restored)**. A test restore is optional but strongly recommended. A backup is useless if you can’t restore your data from it, and you can’t be sure that your backup is not corrupted until you try to restore. # Whonix and Tor -The Whonix project has their own [extensive documentation](https://www.whonix.org/wiki/Documentation). When Whonix is used in Qubes OS it is sometimes referred to as Qubes-Whonix. Whonix can be used on other operating systems as well, but it's preferable to use it on Qubes OS due to the superior isolation it provides. +The Whonix project has their own [extensive documentation](https://www.whonix.org/wiki/Documentation), as does [Kicksecure](https://www.kicksecure.com/wiki/Documentation), upon which it is based. When Whonix is used in Qubes OS it is sometimes referred to as Qubes-Whonix. Whonix can be used on other operating systems as well, but it's preferable to use it on Qubes OS due to the superior isolation it provides. Different applications on a Whonix App qube are configured to use unique circuits of the [Tor network](/glossary#tor-network) so that their activity cannot be correlated - this is called [Stream Isolation](https://anonymousplanet.org/guide.html#pick-your-connectivity-method). @@ -344,7 +344,7 @@ Of the [community-recommended computers](https://forum.qubes-os.org/t/5560), the Qubes OS also applies proper software mitigation to this class of attacks at the level of the hypervisor, including [disabling HyperThreading](https://www.qubes-os.org/news/2018/09/02/qsb-43/). #### OPSEC for Memory Use -To address "future not-yet-identified vulnerabilities of this kind" on older hardware that is no longer receiving microcode updates, the OPSEC suggestion is to limit the presence of secrets in memory that could result in leaks. Every qube that is running is using memory, and a compromised qube could use such vulnerabilities to read from the memory of other qubes. Disposables will be reset after being shutdown, so we can assume that their compromise would likely be transient. Perform sensitive operations in qubes with no networking, and shutdown secure qubes when not in use. Pay attention to which qubes are running simultaneously: +To address "future not-yet-identified vulnerabilities of this kind" on older hardware that is no longer receiving microcode updates, the OPSEC suggestion is to limit the presence of secrets in memory that could result in leaks. Every qube that is running is using memory, and a compromised qube could use such vulnerabilities to read and exfiltrate the memory being used by other qubes. Disposables will be reset after being shutdown, so we can assume that their compromise would likely be transient. Perform sensitive operations in qubes with no networking, and shutdown secure qubes when not in use. Pay attention to which qubes are running simultaneously: * [vault qube](#how-to-organize-your-qubes): Do not run with an unlocked KeePassXC database at the same time as a highly-untrusted qube. * sys-usb: Disposable. Only run when needed, and shutdown when finished. * sys-net: Disposable. Only run when needed, and shutdown when finished. Shutdown when performing sensitive operations in other qubes, as far as possible. Restart before activities which require sys-net (i.e. email, ssh sessions, etc.). diff --git a/content/posts/tails-best/index.md b/content/posts/tails-best/index.md index e9e03b8..c43a185 100644 --- a/content/posts/tails-best/index.md +++ b/content/posts/tails-best/index.md @@ -140,7 +140,7 @@ Another reason to not use Persistent Storage features is that many of them persi Never reuse a password/passphrase for multiple things ("password recycling") - KeePassXC makes it easy to save unique ones that are dedicated to one purpose. [LUKS](/glossary/#luks) encryption **is only effective when the device is powered down** - when the device is on, the password can be retrieved from memory. Any encryption can be [brute-force attacked](/glossary#brute-force-attack) with [massive amounts of cloud computing](https://blog.elcomsoft.com/2020/08/breaking-luks-encryption/). The newer version of LUKS (LUKS2 using Argon2id) is [less vulnerable to brute-force attacks](https://mjg59.dreamwidth.org/66429.html); this is the default from [Tails 5.13](https://tails.boum.org/security/argon2id/index.en.html) onwards, and Qubes OS 4.1 onwards. If you'd like to learn more about this change, we recommend [Systemli's overview](https://www.systemli.org/en/2023/04/30/is-linux-hard-disk-encryption-hacked/). -Password strength is measured in "[bits of entropy](https://en.wikipedia.org/wiki/Password_strength#Entropy_as_a_measure_of_password_strength)". Your passwords/passphrases should ideally have an entropy of around 128 bits (diceware passphrases of approximately **ten words**, or passwords of **21 random characters**) and shouldn't have less than 90 bits of entropy (approximately seven words). +Password strength is measured in "[bits of entropy](https://en.wikipedia.org/wiki/Password_strength#Entropy_as_a_measure_of_password_strength)". Your passwords/passphrases should ideally have an entropy of around 128 bits (diceware passphrases of approximately **ten words**, or passwords of **21 random characters**, including uppercase, lowercase, numbers and symbols) and shouldn't have less than 90 bits of entropy (approximately seven words). What is a diceware passphrase? As [Privacy Guides notes](https://www.privacyguides.org/en/basics/passwords-overview/#diceware-passphrases), "Diceware passphrases are a great option when you need to memorize or manually input your credentials, such as for your password manager's master password or your device's encryption password. An example of a diceware passphrase is `viewable fastness reluctant squishy seventeen shown pencil`." The Password Generator feature in KeePassXC can generate diceware passphrases and random passwords. If you prefer to generate diceware passphrases using real dice, see [Privacy Guides](https://www.privacyguides.org/en/basics/passwords-overview/#diceware-passphrases). diff --git a/themes/DeepThought/static/js/site.js b/themes/DeepThought/static/js/site.js deleted file mode 100644 index ab6b766..0000000 --- a/themes/DeepThought/static/js/site.js +++ /dev/null @@ -1,330 +0,0 @@ -"use strict"; - -function debounce(func, wait) { - var timeout; - - return function () { - var context = this; - var args = arguments; - clearTimeout(timeout); - - timeout = setTimeout(function () { - timeout = null; - func.apply(context, args); - }, wait); - }; -} - -function makeTeaser(body, terms) { - var TERM_WEIGHT = 40; - var NORMAL_WORD_WEIGHT = 2; - var FIRST_WORD_WEIGHT = 8; - var TEASER_MAX_WORDS = 10; - - var stemmedTerms = terms.map(function (w) { - return elasticlunr.stemmer(w.toLowerCase()); - }); - var termFound = false; - var index = 0; - var weighted = []; - - var sentences = body.toLowerCase().split(". "); - - for (var i in sentences) { - var words = sentences[i].split(" "); - var value = FIRST_WORD_WEIGHT; - - for (var j in words) { - var word = words[j]; - - if (word.length > 0) { - for (var k in stemmedTerms) { - if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) { - value = TERM_WEIGHT; - termFound = true; - } - } - weighted.push([word, value, index]); - value = NORMAL_WORD_WEIGHT; - } - - index += word.length; - index += 1; - } - - index += 1; - } - - if (weighted.length === 0) { - return body; - } - - var windowWeights = []; - var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS); - - var curSum = 0; - for (var i = 0; i < windowSize; i++) { - curSum += weighted[i][1]; - } - windowWeights.push(curSum); - - for (var i = 0; i < weighted.length - windowSize; i++) { - curSum -= weighted[i][1]; - curSum += weighted[i + windowSize][1]; - windowWeights.push(curSum); - } - - var maxSumIndex = 0; - if (termFound) { - var maxFound = 0; - for (var i = windowWeights.length - 1; i >= 0; i--) { - if (windowWeights[i] > maxFound) { - maxFound = windowWeights[i]; - maxSumIndex = i; - } - } - } - - var teaser = []; - var startIndex = weighted[maxSumIndex][2]; - for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) { - var word = weighted[i]; - if (startIndex < word[2]) { - teaser.push(body.substring(startIndex, word[2])); - startIndex = word[2]; - } - - if (word[1] === TERM_WEIGHT) { - teaser.push("<b>"); - } - startIndex = word[2] + word[0].length; - teaser.push(body.substring(word[2], startIndex)); - - if (word[1] === TERM_WEIGHT) { - teaser.push("</b>"); - } - } - teaser.push("…"); - return teaser.join(""); -} - -function formatSearchResultItem(item, terms) { - return ( - `<article class='box'>` + - `<h1 class='title'>` + - `<a class='link' class='link' href='${item.ref}'>${item.doc.title}</a>` + - `</h1>` + - `<div class='content mt-2'>` + - `${makeTeaser(item.doc.body, terms)}` + - `<a href='${item.ref}'>` + - `Read More <span class="icon is-small"><i class="fas fa-arrow-right fa-xs"></i></span>` + - `</a>` + - `</div>` + - `</article>` - ); -} - -function search() { - var $searchInput = document.getElementById("search"); - var $searchResults = document.querySelector(".search-results"); - var $searchResultsItems = document.querySelector(".search-results__items"); - var MAX_ITEMS = 10; - - var options = { - bool: "AND", - fields: { - title: { boost: 2 }, - body: { boost: 1 }, - }, - }; - var currentTerm = ""; - var index = elasticlunr.Index.load(window.searchIndex); - - $searchInput.addEventListener( - "keyup", - debounce(function () { - var term = $searchInput.value.trim(); - if (term === currentTerm || !index) { - return; - } - $searchResults.style.display = term === "" ? "none" : "block"; - $searchResultsItems.innerHTML = ""; - if (term === "") { - return; - } - - var results = index.search(term, options); - if (results.length === 0) { - $searchResults.style.display = "none"; - return; - } - - currentTerm = term; - for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) { - var item = document.createElement("div"); - item.classList.add("mb-4"); - item.innerHTML = formatSearchResultItem(results[i], term.split(" ")); - $searchResultsItems.appendChild(item); - } - }, 150) - ); -} - -function documentReadyCallback() { - - if (localStorage.getItem("theme") === "dark") { - document.body.setAttribute("theme", "dark"); - document.querySelectorAll("img, picture, video, pre").forEach(img => img.setAttribute("theme", "dark")); - document.querySelectorAll(".vimeo, .youtube, .chart").forEach(video => video.setAttribute("theme", "dark")); - document.getElementById("dark-mode").setAttribute("title", "Switch to light theme"); - } - - document.querySelector(".navbar-burger").addEventListener("click", () => { - document.querySelector(".navbar-burger").classList.toggle("is-active"); - document.querySelector(".navbar-menu").classList.toggle("is-active"); - }); - - document.querySelectorAll("div.navbar-end > .navbar-item").forEach((el) => { - if (location.href.includes(el.getAttribute("href"))) { - document.querySelectorAll("a.navbar-item.is-active").forEach(itm => itm.classList.remove("is-active")); - el.classList.add("is-active"); - } - }) - - document.getElementById("nav-search").addEventListener("click", (evt) => { - //let target = evt.currentTarget.getAttribute("data-target"); - document.querySelector("html").classList.add("is-clipped"); - document.getElementById("search-modal").classList.add("is-active"); - - document.getElementById("search").focus(); - document.getElementById("search").select(); - }); - - document.querySelector(".modal-close").addEventListener("click", (evt) => { - document.querySelector("html").classList.remove("is-clipped"); - evt.currentTarget.parentElement.classList.remove("is-active"); - }); - - document.querySelector(".modal-background").addEventListener("click", (evt) => { - document.querySelector("html").classList.remove("is-clipped"); - evt.currentTarget.parentElement.classList.remove("is-active"); - }); - - document.getElementById("search").addEventListener("keyup", () => { - search(); - }); - - document.getElementById("dark-mode").addEventListener("click", () => { - if ( - localStorage.getItem("theme") == null || - localStorage.getItem("theme") == "light" - ) { - localStorage.setItem("theme", "dark"); - document.body.setAttribute("theme", "dark"); - document.querySelectorAll("img, picture, video, pre").forEach(img => img.setAttribute("theme", "dark")); - document.querySelectorAll(".vimeo, .youtube, .chart").forEach(video => video.setAttribute("theme", "dark")); - - document.getElementById("dark-mode").setAttribute("title", "Switch to light theme"); - } else { - localStorage.setItem("theme", "light"); - document.body.removeAttribute("theme", "dark"); - document.querySelectorAll("img, picture, video, pre").forEach(img => img.removeAttribute("theme", "dark")) - document.querySelectorAll(".vimeo, .youtube, .chart").forEach(video => video.removeAttribute("theme", "dark")); - - document.getElementById("dark-mode").setAttribute("title", "Switch to dark theme"); - } - }); - - if (typeof mermaid !== "undefined") { - mermaid.initialize({ startOnLoad: true }); - } - - if (typeof chartXkcd !== "undefined") { - document.querySelectorAll(".chart").forEach((el, i) => { - el.setAttribute("id", `chart-${i}`); - - let svg = document.getElementById(`chart-${i}`); - let { type, ...chartData } = JSON.parse(el.textContent); - new chartXkcd[type](svg, chartData); - }); - } - - if (typeof Galleria !== "undefined") { - document.querySelectorAll(".galleria").forEach((el, i) => { - el.setAttribute("id", `galleria-${i}`); - - let { images } = JSON.parse(el.textContent); - - for (let image of images) { - el.insertAdjacentHTML("beforeend", - `<a href="${image.src}"><img src="${image.src}" data-title="${image.title}" data-description="${image.description}"></a>` - ); - } - - Galleria.run(".galleria"); - }); - } - - if (typeof mapboxgl !== "undefined") { - document.querySelectorAll(".map").forEach((el, i) => { - el.setAttribute("id", `map-${i}`); - - mapboxgl.accessToken = el.querySelector(".mapbox-access-token").textContent.trim(); - let zoom = el.querySelector(".mapbox-zoom").textContent.trim(); - - let map = new mapboxgl.Map({ - container: `map-${i}`, - style: "mapbox://styles/mapbox/light-v10", - center: [-96, 37.8], - zoom: zoom, - }); - - map.addControl(new mapboxgl.NavigationControl()); - - let geojson = JSON.parse(el.querySelector(".mapbox-geojson").textContent.trim()); - - const center = [0, 0]; - - geojson.features.forEach(function (marker) { - center[0] += marker.geometry.coordinates[0]; - center[1] += marker.geometry.coordinates[1]; - - new mapboxgl.Marker() - .setLngLat(marker.geometry.coordinates) - .setPopup( - new mapboxgl.Popup({ offset: 25 }) // add popups - .setHTML( - "<h3>" + - marker.properties.title + - "</h3><p>" + - marker.properties.description + - "</p>" - ) - ) - .addTo(map); - }); - - center[0] = center[0] / geojson.features.length; - center[1] = center[1] / geojson.features.length; - - map.setCenter(center); - }); - } - - if (typeof renderMathInElement !== "undefined") { - renderMathInElement(document.body, { - delimiters: [ - { left: '$$', right: '$$', display: true }, - { left: '$', right: '$', display: false }, - { left: '\\(', right: '\\)', display: false }, - { left: '\\[', right: '\\]', display: true } - ] - }); - } -}; - -if (document.readyState === 'loading') { // Loading hasn't finished yet - document.addEventListener('DOMContentLoaded', documentReadyCallback); -} else { // `DOMContentLoaded` has already fired - documentReadyCallback(); -} diff --git a/themes/DeepThought/templates/base.html b/themes/DeepThought/templates/base.html index 4dd6a65..a15fe6e 100644 --- a/themes/DeepThought/templates/base.html +++ b/themes/DeepThought/templates/base.html @@ -5,6 +5,7 @@ <head> <meta charset="utf-8" /> + <meta http-equiv="Content-Security-Policy" content="default-src 'none'; font-src 'self'; img-src 'self'; style-src 'self';" /> <meta content="width=device-width, initial-scale=1" name="viewport" /> <meta content="#ffffff" name="theme-color" /> <meta content="#da532c" name="msapplication-TileColor" /> -- GitLab