Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Investigate blocked inline scripts in extensions #789

Closed
Jaifroid opened this issue Jan 4, 2022 · 30 comments
Closed

Investigate blocked inline scripts in extensions #789

Jaifroid opened this issue Jan 4, 2022 · 30 comments
Milestone

Comments

@Jaifroid
Copy link
Member

Jaifroid commented Jan 4, 2022

At least in the Chromium extension, our declared CSP currently restricts inline script execution. Due to the increasing (and unfortunate) use of inline scripts in ZIMs, this is causing a large number of exceptions to appear in console. So far, they don't significantly affect usability at least in Wikimedia ZIMs. EDIT: Usability is affected in non-Wikimedia ZIMs: see next comment. However, as the message suggests (see screenshot) we may be able to run them by declaring unsafe-inline in the CSP (in the manifest). Of course, this would depend on whether the Stores allow apps to be submitted with this CSP now.

Additionally, ss #752.

image

@Jaifroid
Copy link
Member Author

Jaifroid commented Jan 4, 2022

Investigating this a little further, unfortunately the CSP prevents loading the proprietary UI of Gutenberg ZIMs in the Chromium extension. Compare the PWA from Kiwix JS Windows on left, and the Chromium extension on the right in screenshot below (both running in Service Worker mode). This makes this issue rather more urgent than I thought.

image

@Jaifroid Jaifroid added this to the v3.3 milestone Jan 4, 2022
@mossroy
Copy link
Contributor

mossroy commented Jan 4, 2022

It also breaks Phet, which I reported here : openzim/phet#134

@Jaifroid
Copy link
Member Author

Jaifroid commented Jan 4, 2022

I guess this will break any ZIM UI that relies on ReactJS too, given extensive use of script as markup in React. See https://javascript.plainenglish.io/creating-a-chrome-extension-with-react-d92db20550cb#481f about creating SHA keys in order to run React, with precisely our problem.

As creating SHA keys for every inline script in every ZIM will be impossible for us, we will have to try the unsafe-inline option, like we had to do with unsafe-eval in order to load WASMs conditionally. The latter didn't block submission to the Store, so maybe we can get away with the former too, and as we know the source of our content, it doesn't seem to be risky.

@mossroy
Copy link
Contributor

mossroy commented Jan 4, 2022

The issue on Gutenberg should be reported on https://github.com/openzim/gutenberg/issues : from what I saw, it should be easy to fix for future ZIMs.

I don't think it breaks all ZIM files that use React, but it does break many existing ZIM files (because the scrapers add inline javascript)

I agree with you that, if it's technically possible, we should allow inline javascript in our extension. At least to be able to open existing or old ZIM files.
But I'm not sure the browsers will let us do that : needs to be tested.

@mossroy
Copy link
Contributor

mossroy commented Jan 4, 2022

@Jaifroid
Copy link
Member Author

Jaifroid commented Jan 4, 2022

Ah, OK. That's a shame. Neither the SHA nor the nonce- fixes seem applicable to us (because unfeasible). Which leaves us with no solution within an extension.

I'll create an issue on the Gutenberg scraper.

I guess we need to evaluate the extent of the problem. I can only see it getting worse. A workaround would be to apply the same solution we have for using Firefox in SW mode to Chromium... Though then it really would seem like a third "mode"!

Maybe Manifest v3 has some better solution...

@Jaifroid
Copy link
Member Author

Jaifroid commented Jan 4, 2022

Another solution might be feasible by sandboxing the iframe:

https://developer.chrome.com/docs/extensions/mv2/manifest/sandbox/

However, it does suggest that each file that will be loaded into the sandbox must be specified in the manifest. Whether it would be enough to specify article.html is a bit doubtful, as we load ZIM URLs into the iframe. It depends on how the sandbox is applied and monitored.

@mossroy
Copy link
Contributor

mossroy commented Jan 5, 2022

Google makes it hard to find resources on their CSP because they redirect page http://developer.chrome.com/extensions/contentSecurityPolicy.html to https://developer.chrome.com/docs/apps/contentSecurityPolicy/ (which is about deprecated Google Apps, not extensions).
But there is an archived version of the original page here : https://web.archive.org/web/20201201040815/https://developer.chrome.com/extensions/contentSecurityPolicy (as of December 2020)

This CSP for Chrome extensions does not seem to be new, and the same seems to apply on Edge : https://docs.microsoft.com/en-us/microsoft-edge/extensions-chromium/store-policies/csp

I don't understand how we did not notice that before. Did I miss something?

@mossroy
Copy link
Contributor

mossroy commented Jan 5, 2022

In any case, the sandbox manifest option you were mentioning is worth testing.
It seems to me that inline javascript is mostly used in welcome pages of ZIM files : if we're lucky we might whitelist them in the manifest

Edit : the URLs inside the iframe are prefixed by the ZIM name, which would imply to list all affected ZIM files in the manifest... Not cool

@Jaifroid
Copy link
Member Author

Jaifroid commented Jan 5, 2022

Edit : the URLs inside the iframe are prefixed by the ZIM name, which would imply to list all affected ZIM files in the manifest... Not cool

From what I understand, we'd only have to list the SHA of the script block, not give a URL in the CSP. If we're (really) lucky, the script blocks are all identical since they're produced from a universal template, and there's a small chance they might produce the same SHA. However, we do really need a more generic solution! I'll check out sandbox.

I don't understand how we did not notice that before. Did I miss something?

Either the use of inline JS is relatively recent in these ZIMs, or (more likely) we just happened to have focused on Wikimedia ZIMs when testing the Chromium extension in SW mode. Also, because the Firefox Extension doesn't support SW mode natively, we were probably focusing on testing extensions in jQuery mode. Personally I know I test mostly in Chromium, and rarely in the extension itself: I wrongly assumed that if it works in the former it will work in the latter.

Also remember SW mode was officially marked as experimental and buggy till a couple (?) of years ago. We're only now moving, tentatively, towards making it the default, so it's growing in importance.

@mossroy
Copy link
Contributor

mossroy commented Jan 5, 2022

dirtybiology ZIM files are also affected (tested with both an old one and https://download.kiwix.org/zim/other/dirtybiology_fr_all_2021-10.zim), but it's not a blocker. It seems to "only" affect the drop-down list of the home page, and loading of the webp polyfill

@kelson42
Copy link
Collaborator

kelson42 commented Jan 5, 2022

@mossroy I would recommend to open a ticket for openzim/youtube2zim... and each other scraper creating broken ZIM files for Kiwix JS.

@mossroy
Copy link
Contributor

mossroy commented Jan 5, 2022

Let's maintain a list of affected ZIM files, with corresponding tickets :

(IMHO we should check each line only when we could verify it is solved with a new ZIM file, because we might discover other inline javascript after the tickets are closed)

The following ZIM files seem unaffected :

  • old ted ZIM files
  • old sotoki-based ZIM files
  • old mwoffliner-based ZIM files

@mossroy
Copy link
Contributor

mossroy commented Jan 5, 2022

OK I've tested a few different ZIM files, and opened many tickets in corresponding github projects (see previous comment, that I'll try to keep updated). There are certainly a few more to create.
All recent ZIM files I tested are affected. Some much older ones are not.
In most of affected files, the impact does not seem major, in the sense that the content can be browsed, and the main usage looks OK.
But in some of them (at least nautilus, phet, gutenberg), it breaks the main page, which gives a very poor user experience.

@Jaifroid
Copy link
Member Author

I've tried adding the sandbox attribute to the manifest:

    "content_security_policy": "script-src 'self' 'unsafe-eval' 'unsafe-inline'; object-src 'self'",
    "sandbox": {
        "pages": [
          "www/article.html"
        ]
    },

Unfortunately, the result is that all access to the iframe is blocked (see screenshot below) as Cross Origin. To overcome this, we would need to install a service in article.html that would communicate with the back end via messaging and insert the contents requested in a further iframe embedded in article.html. And it's doubtful whether that would work anyway. It would be a massive coding effort, when we already have a ready-made solution which is to run the Chromium extension as a PWA in SW mode, like the Firefox extension. However, before considering that, we would try the SHA solution.

image

@Jaifroid
Copy link
Member Author

Jaifroid commented Jan 15, 2022

I've tried to produce a base64-encoded SHA256 of the contents of the main script block on the landing page of a Gutenberg English ZIM. This calculation gave me:

"content_security_policy": "script-src 'self' 'unsafe-eval' 'unsafe-inline' 'unsafe-hashes' 'sha256-UUkU0O5trcDPjwjddycRyyaAUG790u7rAC3zAR6j/tE='; object-src 'self'",

However, this has no effect on the error messages in Console. In Windows at least it's quite hard to get this number. Chrome is supposed to calculate it for you, but all I got was 'sha256-...' appearing in the Console, so it didn't calculate it. I calculated by taking the contents of the inline script and putting it through https://report-uri.com/home/hash . However, it strikes me that there are many things that could go wrong, like newline conversion, etc. I've no confidence that the calculated SHA256 is correct. Additionally I'm a bit lost as to which other scripts I'd need to do and how to get small bits of inline JS working, such as onload='runThis():'. A message somewhere says that we have to use 'unsafe-hashes' in the CSP (which I included).

I have to say this feels like a bit of an unlikely solution as I've fallen at the first hurdle.

@Jaifroid
Copy link
Member Author

The given SHA256 above is supposed to represent the SHA256 (base64-encoded) of this, which is the content of the major inline script:

{"default_locale": "en", "locales": {"en": {"isocode": {"textContent": "en"}, "autonym": {"textContent": "English"}, "homepage": {"alt": "Homepage", "title": "Homepage"}, "choose-language": {"placeholder": "Choose a language..."}, "ui-language-switcher": {"title": "Interface language"}, "search": {"textContent": "Search"}, "author": {"textContent": "Author", "placeholder": "Search by author name"}, "title": {"textContext": "Title", "placeholder": "Search by title"}, "cover-img": {"alt": "Book Cover", "title": "Book Cover"}, "cover": {"textContent": "(cover)"}, "filter": {"textContent": "Filter:"}, "popularity": {"textContent": "Popularity"}, "sort": {"textContent": "Sort:"}, "sort-popularity": {"textContent": "Sort books by popularity"}, "sort-title": {"textContent": "Sort books by title"}, "format": {"textContent": "Format"}, "loading": {"textContent": "Loading..."}, "no-data": {"textContent": "No data available in table"}, "prev": {"textContent": "Previous"}, "next": {"textContent": "Next"}, "language": {"textContent": "Language"}, "any-languages": {"textContent": "Choose the Catalog"}, "main-languages": {"textContent": "Main languages"}, "other-languages": {"textContent": "Other languages"}, "author-anonymous": {"textContent": "Anonymous"}, "author-various": {"textContent": "Various"}, "license": {"textContent": "License"}, "license-pd": {"textContent": "Public domain in the USA."}, "nb-downloads": {"textContent": "{[ plural(nb) ]}"}, "nb-downloads[one]": {"textContent": "1 download in the last 30 days."}, "nb-downloads[other]": {"textContent": "{{nb}} downloads in the last 30 days."}, "top-title": {"textContent": "Project Gutenberg Library"}, "sub-title": {"textContent": "The first producer of free ebooks"}, "table-previous": {"textContent": "Previous"}, "table-next": {"textContent": "Next"}, "about-1": {"textContent": "Project Gutenberg offers over 60,000 free ebooks"}, "about-2": {"textContent": "Choose among free epub books, free kindle books."}, "about-3": {"textContent": "Download them or read them online."}, "about-4": {"textContent": "We carry high quality ebooks"}, "about-5": {"textContent": "All our ebooks were previously published by bona fide publishers."}, "about-6": {"textContent": "We digitized and diligently proofread them with the help of thousands of volunteers."}, "bookshelves_title": {"textContent": "Bookshelves (A-Z)"}, "bookshelf_link": {"textContent": "Browse by Bookshelf"}, "home_link": {"textContent": "Go Home"}, "bookshelf": {"placeholder": "Bookshelf Search"}}, "fr": {"isocode": {"textContent": "fr"}, "autonym": {"textContent": "Fran\u00e7ais"}, "homepage": {"alt": "Page d\u2019accueil", "title": "Page d\u2019accueil"}, "choose-language": {"placeholder": "Choisissez une langue..."}, "ui-language-switcher": {"title": "Langue de l\u2019interface"}, "search": {"textContent": "Rechercher"}, "author": {"textContent": "Auteur", "placeholder": "Rechercher par nom d'auteur"}, "cover-img": {"title": "Couverture du livre", "alt": "Couverture du livre"}, "cover": {"textContent": "(couverture)"}, "filter": {"textContent": "Filtrer :"}, "popularity": {"textContent": "Popularit\u00e9"}, "sort": {"textContent": "Trier :"}, "sort-popularity": {"textContent": "Trier les livres par popularit\u00e9"}, "sort-title": {"textContent": "Trier les livres par titre"}, "title": {"textContent": "Titre", "placeholder": "Rechercher des livres par titre "}, "format": {"textContent": "Format"}, "loading": {"textContent": "Chargement..."}, "no-data": {"textContent": "Pas de donn\u00e9es disponibles"}, "prev": {"textContent": "Pr\u00e9c\u00e9dent"}, "next": {"textContent": "Suivant"}, "language": {"textContent": "Langue"}, "any-languages": {"textContent": "Choisir le catalogue"}, "main-languages": {"textContent": "Langues principales"}, "other-languages": {"textContent": "Autres langues"}, "author-anonymous": {"textContent": "Anonyme"}, "author-various": {"textContent": "Plusieurs"}, "license": {"textContent": "Licence"}, "license-pd": {"textContent": "Domaine public aux \u00c9tats-Unis."}, "nb-downloads": {"textContent": "{[ plural(nb) ]}"}, "nb-downloads[one]": {"textContent": "1 t\u00e9l\u00e9chargement au cours des 30 derniers jours."}, "nb-downloads[other]": {"textContent": "{{nb}} t\u00e9l\u00e9chargements au cours des 30 derniers jours."}, "top-title": {"textContent": "Biblioth\u00e8que du projet Gutenberg"}, "sub-title": {"textContent": "Premier producteur d\u2019ebooks gratuits"}, "table-previous": {"textContent": "Pr\u00e9d\u00e9dent"}, "table-next": {"textContent": "Suivant"}, "about-1": {"textContent": "Le Projet Gutenberg offre plus de 60 000 ebooks gratuits"}, "about-2": {"textContent": "Choisissez parmi les livres epub ou kindle gratuits."}, "about-3": {"textContent": "T\u00e9l\u00e9chargez-les ou lisez-les en ligne."}, "about-4": {"textContent": "Nous attachons beaucoup d\u2019importance \u00e0 la qualit\u00e9 des ebooks"}, "about-5": {"textContent": "Tous nos livres \u00e9lectroniques ont \u00e9t\u00e9 auparavant publi\u00e9s par des \u00e9diteurs classiques."}, "about-6": {"textContent": "Nous les num\u00e9risons et les relisons soigneusement avec l\u2019aide de milliers de volontaires."}, "bookshelves_title": {"textContent": "Rayons (A-Z)"}, "bookshelf_link": {"textContent": "Parcourir par rayon"}, "home_link": {"textContent": "Retour"}, "bookshelf": {"placeholder": "Recherche de rayon"}}, "de": {"isocode": {"textContent": "de"}, "autonym": {"textContent": "Deutsch"}, "homepage": {"alt": "Homepage", "title": "Homepage"}, "choose-language": {"placeholder": "Sprache ausw\u00e4hlen..."}, "ui-language-switcher": {"title": "Sprache der Oberfl\u00e4che"}, "search": {"textContent": "Suchen"}, "author": {"textContent": "Autor", "placeholder": "B\u00fccherliste eines einzelnen Autors"}, "cover-img": {"alt": "Einband", "title": "Einband"}, "cover": {"textContent": "(Einband)"}, "filter": {"textContent": "Filter:"}, "popularity": {"textContent": "Beliebtheit"}, "sort": {"textContent": "Sortieren:"}, "sort-popularity": {"textContent": "Sortiere B\u00fccher nach Beliebtheit"}, "sort-title": {"textContent": "Sortiere B\u00fccher nach Titel"}, "title": {"textContent": "Titel", "placeholder": "Suche nach B\u00fcchern nach Titel"}, "format": {"textContent": "Format"}, "loading": {"textContent": "Lade..."}, "no-data": {"textContent": "Keine Daten zum Anzeigen"}, "prev": {"textContent": "Zur\u00fcck"}, "next": {"textContent": "Weiter"}, "language": {"textContent": "Sprache"}, "any-languages": {"textContent": "Katalog w\u00e4hlen"}, "main-languages": {"textContent": "Hauptsprachen"}, "other-languages": {"textContent": "Andere Sprachen"}, "author-anonymous": {"textContent": "Anonym"}, "author-various": {"textContent": "Diverse"}, "license": {"textContent": "Lizenz"}, "license-pd": {"textContent": "Public domain in den USA."}, "nb-downloads": {"textContent": "{[ plural(nb) ]}"}, "nb-downloads[one]": {"textContent": "download in den letzten 30 Tagen."}, "nb-downloads[other]": {"textContent": "{{nb}} downloads in den letzten 30 Tagen."}, "top-title": {"textContent": "Projekt Gutenberg"}, "sub-title": {"textContent": "Der erste Produzent freier Ebooks"}, "table-previous": {"textContent": "Zur\u00fcck"}, "table-next": {"textContent": "Weiter"}, "about-1": {"textContent": "Projekt Gutenberg bietet mehr als 60,000 freie Ebooks"}, "about-2": {"textContent": "Aus freien B\u00fcchern ausw\u00e4hlen."}, "about-3": {"textContent": "Lade sie herunter oder lies online."}, "about-4": {"textContent": "Wir haben hochwertige Ebooks"}, "about-5": {"textContent": "Alle unsere Ebooks waren vorher gemeinn\u00fctzig ver\u00f6ffentlicht."}, "about-6": {"textContent": "Wir digitalisierten und pr\u00fcften sie sorgf\u00e4ltig mit der Hilfe von tausenden Freiwilligen."}, "bookshelves_title": {"textContent": "B\u00fccherregale (A-Z)"}, "bookshelf_link": {"textContent": "Durchsuchen Sie das B\u00fccherregal"}, "home_link": {"textContent": "Nach Hause gehen"}, "bookshelf": {"placeholder": "B\u00fccherregalsuche"}}, "it": {"isocode": {"textContent": "it"}, "autonym": {"textContent": "Italiano"}, "homepage": {"alt": "Homepage", "title": "Homepage"}, "choose-language": {"placeholder": "Scegli la lingua..."}, "ui-language-switcher": {"title": "Interfaccia lingua"}, "search": {"textContent": "Cerca"}, "author": {"textContent": "Autore", "placeholder": "Elenca i libri di un singolo Autore"}, "cover-img": {"alt": "Copertina del Libro", "title": "Copertina del Libro"}, "cover": {"textContent": "(copertina)"}, "filter": {"textContent": "Filtro:"}, "popularity": {"textContent": "Popolarit\u00e0"}, "sort": {"textContent": "Ordina:"}, "sort-popularity": {"textContent": "Ordina libri per popolarit\u00e0"}, "sort-title": {"textContent": "Ordina libri per titolo"}, "title": {"textContent": "Titolo", "placeholder": "Cerca libri per titolo"}, "format": {"textContent": "Formato"}, "loading": {"textContent": "Caricamento in corso..."}, "no-data": {"textContent": "Nessun dato disponibile nella tabella"}, "prev": {"textContent": "Precedente"}, "next": {"textContent": "Successivo"}, "language": {"textContent": "Linguaggio"}, "any-languages": {"textContent": "Scegli il CatalogoChoose the Catalog"}, "main-languages": {"textContent": "Lingue Principali"}, "other-languages": {"textContent": "Altre lingue"}, "author-anonymous": {"textContent": "Anonimo"}, "author-various": {"textContent": "Vari"}, "license": {"textContent": "Licenza"}, "license-pd": {"textContent": "Di pubblico dominio negli USA."}, "nb-downloads": {"textContent": "{[ plurale(nb) ]}"}, "nb-downloads[one]": {"textContent": "1 download negli ultimi 30 giorni."}, "nb-downloads[other]": {"textContent": "{{nb}} download negli ultimi 30 giorni."}, "top-title": {"textContent": "Libreria del Progetto Gutenberg"}, "sub-title": {"textContent": "Il primo produttore di ebooks gratuiti"}, "table-previous": {"textContent": "Precedente"}, "table-next": {"textContent": "Successivo"}, "about-1": {"textContent": "Il Progetto Gutenberg offre pi\u00f9 di 60,000 ebooks gratuiti"}, "about-2": {"textContent": "Scegli fra file epub gratuiti, file kindle gratuiti."}, "about-3": {"textContent": "Scaricali o leggili online."}, "about-4": {"textContent": "Noi forniamo ebook di alta qualit\u00e0"}, "about-5": {"textContent": "Tutti i nostri ebook erano precedentemente pubblicati da editori."}, "about-6": {"textContent": "Li abbiamo digitalizzati e verificati diligentemente con l'aiuto di migliaia di volontari."}, "bookshelves_title": {"textContent": "Scaffali per libri (A-Z)"}, "bookshelf_link": {"textContent": "Sfoglia per libreria"}, "home_link": {"textContent": "Andare a casa"}, "bookshelf": {"placeholder": "Ricerca in libreria"}}, "ar": {"isocode": {"textContent": "ar"}, "autonym": {"textContent": "\u0627\u0644\u0639\u0631\u0628\u064a\u0629"}, "homepage": {"alt": "\u0627\u0644\u0635\u0641\u062d\u0629 \u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629", "title": "\u0627\u0644\u0635\u0641\u062d\u0629 \u0627\u0644\u0631\u0626\u064a\u0633\u064a\u0629"}, "choose-language": {"placeholder": "\u0623\u062e\u062a\u0631 \u0644\u063a\u0629..."}, "ui-language-switcher": {"title": "\u0644\u063a\u0629 \u0627\u0644\u0648\u0627\u062c\u0647\u0629"}, "search": {"textContent": "\u0628\u062d\u062b"}, "author": {"textContent": "\u0627\u0644\u0645\u0624\u0644\u0641", "placeholder": "\u062c\u062f\u0648\u0644\u0629 \u0643\u062a\u0628 \u0623\u062d\u062f \u0627\u0644\u0645\u0624\u0644\u0641\u064a\u0646"}, "cover-img": {"alt": "\u063a\u0644\u0627\u0641 \u0627\u0644\u0643\u062a\u0627\u0628", "title": "\u063a\u0644\u0627\u0641 \u0627\u0644\u0643\u062a\u0627\u0628"}, "cover": {"textContent": "(\u0627\u0644\u063a\u0644\u0627\u0641)"}, "filter": {"textContent": "\u0641\u0644\u062a\u0631:"}, "popularity": {"textContent": "\u0627\u0644\u0634\u0639\u0628\u064a\u0629"}, "sort": {"textContent": "\u062a\u0631\u062a\u064a\u0628:"}, "sort-popularity": {"textContent": "\u0631\u062a\u0628 \u0627\u0644\u0643\u062a\u0628 \u0628\u062d\u0633\u0628 \u0627\u0644\u0634\u0639\u0628\u064a\u0629"}, "sort-title": {"textContent": "\u0631\u062a\u0628 \u0627\u0644\u0643\u062a\u0628 \u0628\u062d\u0633\u0628 \u0627\u0644\u0639\u0646\u0648\u0627\u0646"}, "title": {"textContent": "\u0627\u0644\u0639\u0646\u0648\u0627\u0646", "placeholder": "\u0627\u0628\u062d\u062b \u0639\u0646 \u0627\u0644\u0643\u062a\u0628 \u062d\u0633\u0628 \u0627\u0644\u0639\u0646\u0648\u0627\u0646"}, "format": {"textContent": "\u0627\u0644\u0635\u064a\u063a\u0629"}, "loading": {"textContent": "\u062c\u0627\u0631 \u0627\u0644\u062a\u062d\u0645\u064a\u0644..."}, "no-data": {"textContent": "\u0644\u0627 \u062a\u0648\u062c\u062f \u0645\u0639\u0644\u0648\u0645\u0627\u062a \u0641\u064a \u0627\u0644\u062c\u062f\u0648\u0644"}, "prev": {"textContent": "\u0627\u0644\u0633\u0627\u0628\u0642"}, "next": {"textContent": "\u0627\u0644\u062a\u0627\u0644\u064a"}, "language": {"textContent": "\u0627\u0644\u0644\u063a\u0629"}, "any-languages": {"textContent": "\u0623\u062e\u062a\u0631 \u0642\u0627\u0626\u0645\u0629"}, "main-languages": {"textContent": "\u0627\u0644\u0644\u063a\u0627\u062a \u0627\u0644\u0623\u0633\u0627\u0633\u064a\u0629"}, "other-languages": {"textContent": "\u0627\u0644\u0644\u063a\u0627\u062a \u0627\u0644\u0623\u062e\u0631\u0649"}, "author-anonymous": {"textContent": "\u0645\u062c\u0647\u0648\u0644"}, "author-various": {"textContent": "\u0645\u062a\u0646\u0648\u0639"}, "license": {"textContent": "\u0627\u0644\u0631\u062e\u0635\u0629"}, "license-pd": {"textContent": "\u0645\u062c\u0627\u0644 \u0639\u0627\u0645 \u0641\u064a \u0627\u0644\u0648\u0644\u0627\u064a\u0627\u062a \u0627\u0644\u0645\u062a\u062d\u062f\u0629 \u0627\u0644\u0623\u0645\u0631\u064a\u0643\u064a\u0629."}, "nb-downloads": {"textContent": "{[ plural(nb) ]}"}, "nb-downloads[one]": {"textContent": "\u062a\u062d\u0645\u064a\u0644 \u0648\u0627\u062d\u062f \u0636\u0645\u0646 \u0622\u062e\u0631 30 \u064a\u0648\u0645."}, "nb-downloads[other]": {"textContent": "{{nb}} \u062a\u062d\u0645\u064a\u0644 \u0636\u0645\u0646 \u0622\u062e\u0631 30 \u064a\u0648\u0645."}, "top-title": {"textContent": "\u0645\u0634\u0631\u0648\u0639 \u0645\u0643\u062a\u0628\u0629 Gutenberg"}, "sub-title": {"textContent": "\u0627\u0644\u0645\u0635\u0646\u0639 \u0627\u0644\u0623\u0648\u0644 \u0644\u0644\u0643\u062a\u0628 \u0627\u0644\u0623\u0643\u062a\u0631\u0648\u0646\u064a\u0629 \u0627\u0644\u0645\u062c\u0627\u0646\u064a\u0629"}, "table-previous": {"textContent": "\u0627\u0644\u0633\u0627\u0628\u0642"}, "table-next": {"textContent": "\u0627\u0644\u062a\u0627\u0644\u064a"}, "about-1": {"textContent": "\u0645\u0634\u0631\u0648\u0639 Gutenberg \u064a\u0648\u0641\u0631 \u0623\u0643\u062b\u0631 \u0645\u064660,000 \u0643\u062a\u0627\u0628 \u0623\u0644\u0643\u062a\u0631\u0648\u0646\u064a \u0645\u062c\u0627\u0646\u064a"}, "about-2": {"textContent": "\u0623\u062e\u062a\u0631 \u0645\u0646 \u0636\u0645\u0646 \u0643\u062a\u0628 \u0627\u0644 epub, \u0643\u062a\u0628 kindle \u0645\u062c\u0627\u0646\u064a\u0629."}, "about-3": {"textContent": "\u0642\u0645 \u0628\u062a\u0646\u0632\u064a\u0644\u0647\u0645 \u0627\u0648 \u0642\u0631\u0627\u0626\u062a\u0647\u0645 \u0639\u0628\u0631 \u0627\u0644\u0623\u0646\u062a\u0631\u0646\u062a."}, "about-4": {"textContent": "\u0646\u062d\u0646 \u0644\u062f\u064a\u0646\u0627 \u0643\u062a\u0628 \u0623\u0644\u0643\u062a\u0631\u0648\u0646\u064a\u0629 \u0639\u0627\u0644\u064a\u0629 \u0627\u0644\u062c\u0648\u062f\u0629"}, "about-5": {"textContent": "\u0643\u0644 \u0643\u062a\u0628\u0646\u0627 \u0643\u0627\u0646\u062a \u0645\u0633\u0628\u0642\u0627 \u0645\u0646\u0634\u0648\u0631\u0629 \u0645\u0646 \u0642\u0628\u0644 \u0646\u0627\u0634\u0631\u064a\u0646 bona fide."}, "about-6": {"textContent": "\u0644\u0642\u062f \u0631\u0642\u0645\u0646\u0627\u0647\u0645 \u0648 \u0642\u0631\u0623\u0646\u0627\u0647\u0645 \u0628\u062c\u062f \u0648 \u0630\u0644\u0643 \u0628\u0645\u0633\u0627\u0639\u062f\u0629 \u0645\u0646 \u0627\u0644\u0622\u0644\u0627\u0641 \u0645\u0646 \u0627\u0644\u0645\u062a\u0637\u0648\u0639\u064a\u0646."}, "bookshelves_title": {"textContent": "\u0623\u0631\u0641\u0641 \u0627\u0644\u0643\u062a\u0628 (\u0623-\u064a)"}, "bookshelf_link": {"textContent": "\u062a\u0635\u0641\u062d \u062d\u0633\u0628 \u0631\u0641 \u0627\u0644\u0643\u062a\u0628"}, "home_link": {"textContent": "\u0627\u0630\u0647\u0628 \u0644\u0644\u0645\u0646\u0632\u0644"}, "bookshelf": {"placeholder": "\u0628\u062d\u062b \u0631\u0641 \u0627\u0644\u0643\u062a\u0628"}}, "nl": {"isocode": {"textContent": "nl"}, "autonym": {"textContent": "Nederlands"}, "homepage": {"alt": "Startpagina", "title": "Startpagina"}, "choose-language": {"placeholder": "Kies een taal..."}, "ui-language-switcher": {"title": "Interfacetaal"}, "search": {"textContent": "Zoek"}, "author": {"textContent": "Auteur", "placeholder": "Lijst van boeken van een enkele auteur"}, "cover-img": {"alt": "Boekomslag", "title": "Boekomslag"}, "cover": {"textContent": "(omslag)"}, "filter": {"textContent": "Filter:"}, "popularity": {"textContent": "Populariteit"}, "sort": {"textContent": "Sorteer:"}, "sort-popularity": {"textContent": "Sorteer boeken op populariteit"}, "sort-title": {"textContent": "Sorteer boeken op titel"}, "title": {"textContent": "Titel", "placeholder": "Zoek boeken op titel"}, "format": {"textContent": "Formaat"}, "loading": {"textContent": "Aan het laden..."}, "no-data": {"textContent": "Geen gegevens beschikbaar in tabel"}, "prev": {"textContent": "Vorige"}, "next": {"textContent": "Volgende"}, "language": {"textContent": "Taal"}, "any-languages": {"textContent": "Selecteer de Catalogus"}, "main-languages": {"textContent": "Belangrijkste talen"}, "other-languages": {"textContent": "Andere talen"}, "author-anonymous": {"textContent": "Anoniem"}, "author-various": {"textContent": "Verschillende"}, "license": {"textContent": "Licentie"}, "license-pd": {"textContent": "Publiek domein in de VS."}, "nb-downloads": {"textContent": "{[ plural(nb) ]}"}, "nb-downloads[one]": {"textContent": "1 download in de afgelopen 30 dagen."}, "nb-downloads[other]": {"textContent": "{{nb}} downloads in de afgelopen 30 dagen."}, "top-title": {"textContent": "Project Gutenberg Bibliotheek"}, "sub-title": {"textContent": "De eerste producent van gratis eboeken"}, "table-previous": {"textContent": "Vorige"}, "table-next": {"textContent": "Volgende"}, "about-1": {"textContent": "Project Gutenberg bevat meer dan 60,000 gratis eboeken"}, "about-2": {"textContent": "Kies uit gratis epub boeken, gratis kindle boeken."}, "about-3": {"textContent": "Download ze of lees ze online."}, "about-4": {"textContent": "Wij bieden eboeken van hoge kwaliteit"}, "about-5": {"textContent": "Al onze eboeken werden oorspronkelijk uitgegeven door bona fide uitgevers."}, "about-6": {"textContent": "We hebben ze gedigitaliseerd en zorgvuldig gecorrigeerd met behulp van duizenden vrijwilligers."}, "bookshelves_title": {"textContent": "Boekenplanken (A-Z)"}, "bookshelf_link": {"textContent": "Bladeren op boekenplank"}, "home_link": {"textContent": "Ga naar huis"}, "bookshelf": {"placeholder": "Boekenplank zoeken"}}, "es": {"isocode": {"textContent": "es"}, "autonym": {"textContent": "Spanish"}, "homepage": {"alt": "Inicio", "title": "Inicio"}, "choose-language": {"placeholder": "Elija un idioma..."}, "ui-language-switcher": {"title": "Idioma de la interfaz"}, "search": {"textContent": "Buscar"}, "author": {"textContent": "Autor", "placeholder": "Lista de libros de un autor"}, "cover-img": {"alt": "Portada del libro", "title": "Portada del libro"}, "cover": {"textContent": "(portada)"}, "filter": {"textContent": "Filtro:"}, "popularity": {"textContent": "Popularidad"}, "sort": {"textContent": "Ordenar:"}, "sort-popularity": {"textContent": "Ordenar libros por popularidad"}, "sort-title": {"textContent": "Ordenar libros por t\u00edtulo"}, "title": {"textContent": "T\u00edtulo", "placeholder": "Buscar libros por t\u00edtulo"}, "format": {"textContent": "Formato"}, "loading": {"textContent": "Cargando..."}, "no-data": {"textContent": "No hay datos disponibles en la tabla"}, "prev": {"textContent": "Anterior"}, "next": {"textContent": "Siguiente"}, "language": {"textContent": "Idioma"}, "any-languages": {"textContent": "Elija en el cat\u00e1logo"}, "main-languages": {"textContent": "Idiomas principales"}, "other-languages": {"textContent": "Otros idiomas"}, "author-anonymous": {"textContent": "An\u00f3nimo"}, "author-various": {"textContent": "Varios"}, "license": {"textContent": "Licencia"}, "license-pd": {"textContent": "De dominio p\u00fablico en EEUU."}, "nb-downloads": {"textContent": "{[ plural(nb) ]}"}, "nb-downloads[one]": {"textContent": "Una descarga en los \u00faltimos 30 d\u00edas."}, "nb-downloads[other]": {"textContent": "{{nb}} descargas en los \u00faltimos 30 d\u00edas."}, "top-title": {"textContent": "Librer\u00eda del Proyecto Gutenberg"}, "sub-title": {"textContent": "El primer editor de ebooks libres"}, "table-previous": {"textContent": "Anterior"}, "table-next": {"textContent": "Siguiente"}, "about-1": {"textContent": "El Proyecto Gutenberg ofrece m\u00e1s de60,000 ebooks libres"}, "about-2": {"textContent": "Elija entre ebooks epub libres yebooks kindle libres."}, "about-3": {"textContent": "Desc\u00e1rgalos o l\u00e9elos online."}, "about-4": {"textContent": "Tenemos ebooks de gran calidad"}, "about-5": {"textContent": "Todos nuestros ebooks han sido publicadosantes por editoriales de prestigio."}, "about-6": {"textContent": "Los digitalizamos y comprobamoscuidadosamente con la ayuda de miles de voluntarios."}, "bookshelves_title": {"textContent": "Estantes (A-Z)"}, "bookshelf_link": {"textContent": "Buscar por estanter\u00eda"}, "home_link": {"textContent": "Vete a casa"}, "bookshelf": {"placeholder": "B\u00fasqueda de estanter\u00eda"}}}}

@Jaifroid
Copy link
Member Author

Jaifroid commented Jan 15, 2022

So I've now added the SHA256 of the body onload="..." inline attribute which. The original script is init(); showBooks(); (including whitespace that doesn't show up properly here), and the online calculator gave me sha256-zZyBtdCWppHUHL/OIOwo8kQ05mpMFDoD6usm8BFm/ig=. Unfortunately, this makes no difference either when added to the CSP.

@mossroy
Copy link
Contributor

mossroy commented Jan 15, 2022

For some reason, the SHA256 is not always displayed as a hint in Chromium dev console.
To help understanding what's wrong, you could try with another ZIM file. For example, when using https://download.kiwix.org/zim/phet/phet_fr_2021-08.zim , Chromium gives me the SHA256 :

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-eval'". Either the 'unsafe-inline' keyword, a hash ('sha256-h1G5vTT/RuoHFKih3ibsUNr2b7WX7UpZMbYt5ioKXWc='), or a nonce ('nonce-...') is required to enable inline execution.

@Jaifroid Jaifroid modified the milestones: v3.3, v3.4 Jan 31, 2022
@Jaifroid Jaifroid changed the title Allow inline scripts in extensions Allow inline scripts in extensions or provide workaround Feb 5, 2022
@mossroy mossroy mentioned this issue Feb 5, 2022
18 tasks
@mossroy
Copy link
Contributor

mossroy commented Feb 6, 2022

As discussed in #802 (comment) , we might have no better solution than trying to detect the CSP violation and warn the user.
It should be possible with https://developer.mozilla.org/en-US/docs/Web/API/SecurityPolicyViolationEvent , that works on recent browsers: https://caniuse.com/mdn-api_securitypolicyviolationevent

@rgaudin
Copy link
Member

rgaudin commented Mar 4, 2022

* [ ]  dirtybiology/audiobooks (are they generated by zimwriterfs? Where should I create the ticket?) : "only" affects the drop-down list of the home page, and loading of the webp-polyfill

That would be on the youtube scraper.

* [ ]  mooc : there seems to be a lot of blocked inline javascript, at least in https://download.kiwix.org/zim/mooc/phzh_core-english-one_en_2021-07.zim . Where should I open the ticket?

This would be openedx. Chances of fix are low for this one.

@mossroy
Copy link
Contributor

mossroy commented Mar 4, 2022

I've created the missing github issues (thanks for your help @rgaudin), and updated the check-list above.

I also add a link provided by @rgaudin for a list of scrapers: https://github.com/openzim/overview/wiki/Scrapers-list , in case we need it in the future.

@mossroy mossroy modified the milestones: v3.4, v3.5 Apr 9, 2022
@mossroy
Copy link
Contributor

mossroy commented May 30, 2022

I've tried to allow some inline javascript through the use of hashes: if there's a specific javascript piece of code that you need to allow, you can set its hash in the manifest.json, and the browser accepts to execute it.
See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#unsafe_inline_script

It might be useful punctually, but it won't help in most cases. First because the inline javascript is often variable (not exactly the same content in all pages, so different hashes). Then because it does not apply to event handlers (like inline onclick or onload events).

The nonce feature could be more useful, as it can whitelist some in-line javascript blocks (but still not event handlers), regardless of their content. We set some acceptable nonces in the manifest.json, and all javascript blocks marked with this nonce will be accepted. But it needs to modify the ZIM contents (to add the nonce), so the scrapers anyway. So it might be useful, but it might be not much more complicated to remove the inline javascript (which solves the issue much more cleanly)

To sum up, except for a few cases like ted (that seems to always use the same inline javascript blocks), we don't have a way to avoid blocked inline javascript without modifying the ZIM files

@Jaifroid
Copy link
Member Author

@mossroy Thanks for the tests. We'll have to weigh up whether the inability to run dynamic content in a number of modern ZIMs in the Chromium extension is enough of a disadvantage to merit using the Firefox solution of entering SW mode as an offline-first PWA, or, alternatively, of providing that as a third option.

It's untested as yet in a Chromium extension (it needs to be possible to communicate between the PWA and the installed Chromium extension, and this is what would need testing).

To be discussed!

@Jaifroid
Copy link
Member Author

PS One advantage of using the PWA for SW mode in the Chormium extension is that it would allow us to implement #656 for the Chromium extension. AFAIK, this API would not work with the chorme-extension: protocol, as it requires a secure origin. Note that it is Chromium-specific, and Mozilla has declined to support this API.

@mossroy
Copy link
Contributor

mossroy commented May 30, 2022

Currently, when some inline javascript is blocked, it "only" displays an error in the console, with no user feedback.
I would find it useful to tell the user that something might not work as expected, and invite him/her to download a newer version of the ZIM file (where the problem would have hopefully been fixed). A bit like we currently warn the user when there is javascript in jQuery mode.

I've tried to handle the securitypolicyviolation event to do that, but did not manage to make it triggered. It's probably because the event happens in the iframe (not in the surrounding document).
I'll try harder, but it might be not possible at all, see https://stackoverflow.com/questions/44949085/is-there-any-event-fired-when-x-frame-options-content-security-policy-are-viol

@mossroy
Copy link
Contributor

mossroy commented May 30, 2022

The PWA would avoid many problems, for sure. And it's certainly possible to use it for Chromium-based browsers, too.
But I would find it sad to "force" the users to have the one-time Internet access (which prevents some fully offline usages), just because we did not manage to remove inline javascript from our ZIM files.

@Jaifroid
Copy link
Member Author

Have you tried adding error catching to the iframe (an .onerror event)? Something like:

                    articleWindow.onerror = function (msg, url, line, col, error) {
                        console.error('Error caught in ZIM contents [' + url + ':' + line + ']:\n' + msg, error);
                        // Alert user in some other way, depending on error type?
                        return true;
                    };

It might be necessary to add this event to the window of the document inside the iframe. Not sure if it's enough to add it to the iframe.

@mossroy
Copy link
Contributor

mossroy commented May 30, 2022

I've tried many combinations, trapping error, securitypolicyviolation, and even readystatechange on the iframe, but did not manage to trigger them on the iframe.

We use the same domain for the iframe, so I don't think it's a cross-domain issue.
But the securitypolicyviolation event is fired before the load event (because the javascript content is interpreted early), and I don't manage to set its handler soon enough (load event is the first I manage to trap for the iframe). Probably because, from the perspective of the surrounding document, the iframe content is still the previous one.
It would certainly work if this handler was set by the iframe itself (but, of course, we can't do that without modifying the ZIM content)

So I currently don't manage to warn the user that some inline javascript has been blocked.
That's annoying because, even if inline javascript was to be removed from all recent ZIM files, blocked inline javascript will still occur in some older ones. And it's a bad user experience to have broken content with no explanation.

@mossroy
Copy link
Contributor

mossroy commented Jun 2, 2022

This ticket has become too cluttered.
To make things more clear, I'll close it and rename it to "Investigate on ...".
And create another one to follow the progress of removing inline javascript

@mossroy mossroy closed this as completed Jun 2, 2022
@mossroy mossroy changed the title Allow inline scripts in extensions or provide workaround Investigate blocked inline scripts in extensions Jun 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants