diff --git a/.htaccess.disabled b/.htaccess.disabled
new file mode 100644
index 00000000..5a3abe46
--- /dev/null
+++ b/.htaccess.disabled
@@ -0,0 +1,12 @@
+RewriteEngine on
+RewriteCond !%{HTTP_USER_AGENT} "Let's Encrypt validation server" [NC]
+RewriteCond %{HTTP_USER_AGENT} ^.*(bot|spider|crawl|https?://|WhatsApp|SkypeUriPreview|facebookexternalhit) [NC]
+RewriteRule .* - [R=403,L]
+
+
+php_value max_execution_time 30
+php_value post_max_size 10M
+php_value upload_max_size 10M
+php_value upload_max_filesize 10M
+php_value max_file_uploads 100
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 085c9a5c..608716f9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
# PrivateBin version history
* **1.4 (not yet released)**
+ * CHANGED: Upgrading libraries to: DOMpurify 2.0.8
+ * CHANGED: Several translations got updated with missing messages
+ * CHANGED: Introduce HTML entity encoding on server side (#581)
+ * FIXED: HTML entity double encoding issues introduced in 1.3.2 (#560)
* **1.3.2 (2020-01-11)**
* ADDED: Translation for Ukrainian (#533)
* ADDED: Option to send a mail with the link, when creating a paste (#398)
diff --git a/INSTALL.md b/INSTALL.md
index eb2c8ea3..2f3900c6 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -187,7 +187,7 @@ CREATE INDEX parent ON prefix_comment(pasteid);
CREATE TABLE prefix_config (
id CHAR(16) NOT NULL, value TEXT, PRIMARY KEY (id)
);
-INSERT INTO prefix_config VALUES('VERSION', '1.3.2');
+INSERT INTO prefix_config VALUES('VERSION', '1.3.3');
```
In **PostgreSQL**, the data, attachment, nickname and vizhash columns needs to be TEXT and not BLOB or MEDIUMBLOB.
diff --git a/README.md b/README.md
index ff86e973..d05a8654 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# [![PrivateBin](https://cdn.rawgit.com/PrivateBin/assets/master/images/preview/logoSmall.png)](https://privatebin.info/)
-*Current version: 1.3.2*
+*Current version: 1.3.3*
**PrivateBin** is a minimalist, open source online [pastebin](https://en.wikipedia.org/wiki/Pastebin)
where the server has zero knowledge of pasted data.
@@ -95,9 +95,9 @@ file](https://github.com/PrivateBin/PrivateBin/wiki/Configuration):
## Further resources
-* [Installation guide](https://github.com/PrivateBin/PrivateBin/blob/master/INSTALL.md#installation)
+* [FAQ](https://github.com/PrivateBin/PrivateBin/wiki/FAQ)
-* [Upgrading from ZeroBin 0.19 Alpha](https://github.com/PrivateBin/PrivateBin/wiki/Upgrading-from-ZeroBin-0.19-Alpha)
+* [Installation guide](https://github.com/PrivateBin/PrivateBin/blob/master/INSTALL.md#installation)
* [Configuration guide](https://github.com/PrivateBin/PrivateBin/wiki/Configuration)
diff --git a/SECURITY.md b/SECURITY.md
index 300f6eea..f3b02da2 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -4,8 +4,8 @@
| Version | Supported |
| ------- | ------------------ |
-| 1.3.2 | :heavy_check_mark: |
-| < 1.3.2 | :x: |
+| 1.3.3 | :heavy_check_mark: |
+| < 1.3.3 | :x: |
## Reporting a Vulnerability
diff --git a/css/bootstrap/privatebin.css b/css/bootstrap/privatebin.css
index 392966f6..7bd85f55 100644
--- a/css/bootstrap/privatebin.css
+++ b/css/bootstrap/privatebin.css
@@ -6,7 +6,7 @@
* @link https://github.com/PrivateBin/PrivateBin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
- * @version 1.3.2
+ * @version 1.3.3
*/
body {
diff --git a/css/noscript.css b/css/noscript.css
index 6d71476d..3679c279 100644
--- a/css/noscript.css
+++ b/css/noscript.css
@@ -6,7 +6,7 @@
* @link https://github.com/PrivateBin/PrivateBin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
- * @version 1.3.2
+ * @version 1.3.3
*/
/* When there is no script at all other */
diff --git a/css/privatebin.css b/css/privatebin.css
index ae32f2e1..350d6217 100644
--- a/css/privatebin.css
+++ b/css/privatebin.css
@@ -6,7 +6,7 @@
* @link https://github.com/PrivateBin/PrivateBin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
- * @version 1.3.2
+ * @version 1.3.3
*/
/* CSS Reset from YUI 3.4.1 (build 4118) - Copyright 2011 Yahoo! Inc. All rights reserved.
diff --git a/i18n/bg.json b/i18n/bg.json
index 63666060..c87c1afd 100644
--- a/i18n/bg.json
+++ b/i18n/bg.json
@@ -31,8 +31,8 @@
"Невалиден код за изтриване. Информацията Ви не беше изтрита.",
"Paste was properly deleted.":
"Информацията Ви е изтрита.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "Услугата %s се нуждае от JavaScript, за да работи. Съжаляваме за неудобството.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "Услугата %s се нуждае от JavaScript, за да работи. Съжаляваме за неудобството.",
"%s requires a modern browser to work.":
"%s се нуждае от съвременен браузър за да работи.",
"New":
@@ -168,5 +168,21 @@
"Retry":
"Retry",
"Showing raw text…":
- "Showing raw text…"
+ "Showing raw text…",
+ "Notice:":
+ "Notice:",
+ "This link will expire after %s.":
+ "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone":
+ "Use Current Timezone",
+ "Convert To UTC":
+ "Convert To UTC",
+ "Close":
+ "Close"
}
diff --git a/i18n/cs.json b/i18n/cs.json
index 02b599d7..a199bc56 100644
--- a/i18n/cs.json
+++ b/i18n/cs.json
@@ -31,8 +31,8 @@
"Wrong deletion token. Paste was not deleted.",
"Paste was properly deleted.":
"Paste was properly deleted.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "JavaScript is required for %s to work. Sorry for the inconvenience.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "JavaScript is required for %s to work. Sorry for the inconvenience.",
"%s requires a modern browser to work.":
"%%s requires a modern browser to work.",
"New":
@@ -168,5 +168,21 @@
"Retry":
"Retry",
"Showing raw text…":
- "Showing raw text…"
+ "Showing raw text…",
+ "Notice:":
+ "Notice:",
+ "This link will expire after %s.":
+ "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone":
+ "Use Current Timezone",
+ "Convert To UTC":
+ "Convert To UTC",
+ "Close":
+ "Close"
}
diff --git a/i18n/de.json b/i18n/de.json
index 1d6678ab..31eec787 100644
--- a/i18n/de.json
+++ b/i18n/de.json
@@ -31,8 +31,8 @@
"Falscher Lösch-Code. Text wurde nicht gelöscht.",
"Paste was properly deleted.":
"Text wurde erfolgreich gelöscht.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "JavaScript ist eine Voraussetzung, um %s zu nutzen. Bitte entschuldige die Unannehmlichkeiten.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "JavaScript ist eine Voraussetzung, um %s zu nutzen. Bitte entschuldige die Unannehmlichkeiten.",
"%s requires a modern browser to work.":
"%s setzt einen modernen Browser voraus, um funktionieren zu können.",
"New":
@@ -168,5 +168,21 @@
"Retry":
"Wiederholen",
"Showing raw text…":
- "Zeige reinen Text an…"
+ "Zeige reinen Text an…",
+ "Notice:":
+ "Hinweis:",
+ "This link will expire after %s.":
+ "Dieser Link wird in %s ablaufen.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "Dieser Link kann nur einmal geöffnet werden, verwende nicht den \"Zurück\" oder \"Neu laden\" Knopf Deines Browsers.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Der Empfänger könnte Deine Zeitzone erfahren, möchtest Du die Zeit in UTC umwandeln?",
+ "Use Current Timezone":
+ "Aktuelle Zeitzone verwenden",
+ "Convert To UTC":
+ "In UTC Umwandeln",
+ "Close":
+ "Schliessen"
}
diff --git a/i18n/es.json b/i18n/es.json
index 564dd72e..df1e054f 100644
--- a/i18n/es.json
+++ b/i18n/es.json
@@ -31,8 +31,8 @@
"Token de eliminación erróneo. El \"paste\" no fue eliminado.",
"Paste was properly deleted.":
"El \"paste\" se ha eliminado correctamente.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "JavaScript es necesario para que %s funcione. Sentimos los inconvenientes ocasionados.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "JavaScript es necesario para que %s funcione. Sentimos los inconvenientes ocasionados.",
"%s requires a modern browser to work.":
"%s requiere un navegador moderno para funcionar.",
"New":
@@ -168,5 +168,21 @@
"Retry":
"Retry",
"Showing raw text…":
- "Showing raw text…"
+ "Showing raw text…",
+ "Notice:":
+ "Notice:",
+ "This link will expire after %s.":
+ "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone":
+ "Use Current Timezone",
+ "Convert To UTC":
+ "Convert To UTC",
+ "Close":
+ "Close"
}
diff --git a/i18n/fr.json b/i18n/fr.json
index 90eabf1d..525f04a3 100644
--- a/i18n/fr.json
+++ b/i18n/fr.json
@@ -31,8 +31,8 @@
"Jeton de suppression incorrect. Le paste n'a pas été supprimé.",
"Paste was properly deleted.":
"Le paste a été correctement supprimé.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "JavaScript est requis pour faire fonctionner %s. Désolé pour cet inconvénient.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "JavaScript est requis pour faire fonctionner %s. Désolé pour cet inconvénient.",
"%s requires a modern browser to work.":
"%s nécessite un navigateur moderne pour fonctionner.",
"New":
@@ -137,12 +137,12 @@
"Cloned: '%s'": "Cloner '%s'",
"The cloned file '%s' was attached to this paste.": "Le fichier cloné '%s' a été attaché à ce paste.",
"Attach a file": "Attacher un fichier ",
- "alternatively drag & drop a file or paste an image from the clipboard": "alternativement, glisser & déposer un fichier ou coller une image à partir du presse-papiers",
+ "alternatively drag & drop a file or paste an image from the clipboard": "au choix, glisser & déposer un fichier ou coller une image à partir du presse-papiers",
"File too large, to display a preview. Please download the attachment.": "Fichier trop volumineux, pour afficher un aperçu. Veuillez télécharger la pièce jointe.",
- "Remove attachment": "Enlever l'attachement",
+ "Remove attachment": "Enlever la pièce jointe",
"Your browser does not support uploading encrypted files. Please use a newer browser.":
"Votre navigateur ne supporte pas l'envoi de fichiers chiffrés. Merci d'utiliser un navigateur plus récent.",
- "Invalid attachment.": "Attachement invalide.",
+ "Invalid attachment.": "Pièce jointe invalide.",
"Options": "Options",
"Shorten URL": "Raccourcir URL",
"Editor": "Éditer",
@@ -158,7 +158,7 @@
"Preparing new paste…": "Préparation du paste…",
"In case this message never disappears please have a look at this FAQ for information to troubleshoot.":
"Si ce message ne disparaîssait pas, jetez un oeil à cette FAQ pour des idées de résolution (en Anglais).",
- "+++ no paste text +++": "+++ pas de paste-text +++",
+ "+++ no paste text +++": "+++ pas de texte copié +++",
"Could not get paste data: %s":
"Impossible d'obtenir les données du paste: %s",
"QR code": "QR code",
@@ -169,13 +169,29 @@
"Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.":
"Votre navigateur peut nécessiter une connexion HTTPS pour prendre en charge l’API WebCrypto. Essayez de passer en HTTPS.",
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
- "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.",
+ "Votre navigateur ne prend pas en charge WebAssembly, utilisé pour la compression zlib. Vous pouvez créer des documents non compressés, mais vous ne pouvez pas lire les documents compressés.",
"waiting on user to provide a password":
- "waiting on user to provide a password",
+ "en attendant que l'utilisateur fournisse un mot de passe",
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.":
- "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.",
+ "Impossible de décrypter les données. Vous avez saisi un mot de passe incorrect ? Réessayez avec le bouton en haut.",
"Retry":
- "Retry",
+ "Réessayer",
"Showing raw text…":
- "Showing raw text…"
+ "Affichage du texte brut…",
+ "Notice:":
+ "Avertissement :",
+ "This link will expire after %s.":
+ "Ce lien expire après le %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "Vous ne pouvez accéder à ce lien qu'une seule fois, n'utilisez pas le bouton précédent ou rafraîchir de votre navigateur.",
+ "Link:":
+ "Lien :",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Le destinataire peut connaître votre fuseau horaire, convertir l'heure au format UTC ?",
+ "Use Current Timezone":
+ "Conserver l'actuel",
+ "Convert To UTC":
+ "Convertir en UTC",
+ "Close":
+ "Fermer"
}
diff --git a/i18n/hu.json b/i18n/hu.json
index c3e11a7b..f55efdcb 100644
--- a/i18n/hu.json
+++ b/i18n/hu.json
@@ -31,7 +31,7 @@
"Hibás törlési azonosító. A bejegyzés nem lett törölve.",
"Paste was properly deleted.":
"A bejegyzés sikeresen törölve.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
"JavaScript szükséges a %s működéséhez. Elnézést a fennakadásért.",
"%s requires a modern browser to work.":
"A %s működéséhez a jelenleginél újabb böngészőre van szükség.",
@@ -168,5 +168,21 @@
"Retry":
"Retry",
"Showing raw text…":
- "Showing raw text…"
+ "Showing raw text…",
+ "Notice:":
+ "Notice:",
+ "This link will expire after %s.":
+ "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone":
+ "Use Current Timezone",
+ "Convert To UTC":
+ "Convert To UTC",
+ "Close":
+ "Close"
}
diff --git a/i18n/it.json b/i18n/it.json
index 07af891b..2a2bff50 100644
--- a/i18n/it.json
+++ b/i18n/it.json
@@ -31,8 +31,8 @@
"Codice cancellazione errato. Il messaggio NON è stato cancellato.",
"Paste was properly deleted.":
"Il messaggio è stato correttamente cancellato.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "%s funziona solo con JavaScript attivo. Ci dispiace per l'inconveniente.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "%s funziona solo con JavaScript attivo. Ci dispiace per l'inconveniente.",
"%s requires a modern browser to work.":
"%s richiede un browser moderno e aggiornato per funzionare.",
"New":
@@ -168,5 +168,21 @@
"Retry":
"Retry",
"Showing raw text…":
- "Showing raw text…"
+ "Showing raw text…",
+ "Notice:":
+ "Notice:",
+ "This link will expire after %s.":
+ "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone":
+ "Use Current Timezone",
+ "Convert To UTC":
+ "Convert To UTC",
+ "Close":
+ "Close"
}
diff --git a/i18n/nl.json b/i18n/nl.json
index 29554d85..d3bdb720 100644
--- a/i18n/nl.json
+++ b/i18n/nl.json
@@ -31,8 +31,8 @@
"Foutieve verwijdercode. Geplakte tekst is niet verwijderd.",
"Paste was properly deleted.":
"Geplakte tekst is correct verwijderd.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "JavaScript vereist om %s te laten werken. Sorry voor het ongemak.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "JavaScript vereist om %s te laten werken. Sorry voor het ongemak.",
"%s requires a modern browser to work.":
"%s vereist een moderne browser om te kunnen werken ",
"New":
@@ -168,5 +168,21 @@
"Retry":
"Retry",
"Showing raw text…":
- "Showing raw text…"
+ "Showing raw text…",
+ "Notice:":
+ "Notice:",
+ "This link will expire after %s.":
+ "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone":
+ "Use Current Timezone",
+ "Convert To UTC":
+ "Convert To UTC",
+ "Close":
+ "Close"
}
diff --git a/i18n/no.json b/i18n/no.json
index a2ad2c1a..f43ef6e4 100644
--- a/i18n/no.json
+++ b/i18n/no.json
@@ -31,8 +31,8 @@
"Feil slettingsnøkkel. Innlegg ble ikke fjernet.",
"Paste was properly deleted.":
"Innlegget er slettet.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "Javascript kreves for at %s skal fungere Beklager.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "Javascript kreves for at %s skal fungere. Beklager.",
"%s requires a modern browser to work.":
"%s krever en moderne nettleser for å fungere.",
"New":
@@ -168,5 +168,21 @@
"Retry":
"Retry",
"Showing raw text…":
- "Showing raw text…"
+ "Showing raw text…",
+ "Notice:":
+ "Notice:",
+ "This link will expire after %s.":
+ "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone":
+ "Use Current Timezone",
+ "Convert To UTC":
+ "Convert To UTC",
+ "Close":
+ "Close"
}
diff --git a/i18n/oc.json b/i18n/oc.json
index 43a0db71..c078faed 100644
--- a/i18n/oc.json
+++ b/i18n/oc.json
@@ -31,8 +31,8 @@
"Geton de supression incorrècte. Lo tèxte es pas estat suprimit.",
"Paste was properly deleted.":
"Lo tèxte es estat corrèctament suprimit.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "JavaScript es requesit per far foncionar %s. O planhèm per l’inconvenient.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "JavaScript es requesit per far foncionar %s. O planhèm per l’inconvenient.",
"%s requires a modern browser to work.":
"%s necessita un navigator modèrn per foncionar.",
"New":
@@ -169,13 +169,29 @@
"Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.":
"Se pòt que vòstre navigator faga besonh d’una connexion HTTPS per èsser compatible amb l’API WebCrypto. Ensajatz de passar al HTTPS.",
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
- "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.",
+ "Vòstre navigator es pas compatible amb WebAssembly, utilizat per la compression zlib. Podètz crear de documents pas compressat, mas ne podètz pas legir de compressats.",
"waiting on user to provide a password":
- "waiting on user to provide a password",
+ "en espèra que l’utilizaire fornisca un senhal",
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.":
- "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.",
+ "Deschiframent de las donadas impossible. Avètz picat un marrit senhal ? Tornatz ensajar amb lo boton ennaut.",
"Retry":
- "Retry",
+ "Tornar ensajar",
"Showing raw text…":
- "Showing raw text…"
+ "Afichatge del tèxte brut…",
+ "Notice:":
+ "Avertiment :",
+ "This link will expire after %s.":
+ "Aqueste ligam expirarà aprèp %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "Òm pòt pas qu’accedir a aqueste ligam qu’un còp, utilizetz pas lo boton precedent o actualizar del navigator.",
+ "Link:":
+ "Ligam :",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Lo destinatari pòt s’avisar de vòstre fus orari, convertir en UTC ?",
+ "Use Current Timezone":
+ "Utilizar l’actual",
+ "Convert To UTC":
+ "Convertir en UTC",
+ "Close":
+ "Tampar"
}
diff --git a/i18n/pl.json b/i18n/pl.json
index 6b5cbb57..3517b076 100644
--- a/i18n/pl.json
+++ b/i18n/pl.json
@@ -31,7 +31,7 @@
"Nieprawidłowy token usuwania. Wklejka nie została usunięta.",
"Paste was properly deleted.":
"Wklejka usunięta poprawnie.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
"Do działania %sa jest wymagany JavaScript. Przepraszamy za tę niedogodność.",
"%s requires a modern browser to work.":
"%s wymaga do działania nowoczesnej przeglądarki.",
@@ -168,5 +168,21 @@
"Retry":
"Retry",
"Showing raw text…":
- "Showing raw text…"
+ "Showing raw text…",
+ "Notice:":
+ "Notice:",
+ "This link will expire after %s.":
+ "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone":
+ "Use Current Timezone",
+ "Convert To UTC":
+ "Convert To UTC",
+ "Close":
+ "Close"
}
diff --git a/i18n/pt.json b/i18n/pt.json
index 253a45ee..6dc1f405 100644
--- a/i18n/pt.json
+++ b/i18n/pt.json
@@ -31,8 +31,8 @@
"Token de remoção inválido. A cópia não foi excluída.",
"Paste was properly deleted.":
"A cópia foi devidamente excluída.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "JavaScript é necessário para que %s funcione. Pedimos desculpas pela inconveniência.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "JavaScript é necessário para que %s funcione. Pedimos desculpas pela inconveniência.",
"%s requires a modern browser to work.":
"%s requer um navegador moderno para funcionar.",
"New":
@@ -151,22 +151,38 @@
"Caso essa mensagem nunca desapareça, por favor veja este FAQ para saber como resolver os problemas.",
"+++ no paste text +++": "+++ sem texto de cópia +++",
"Could not get paste data: %s":
- "Could not get paste data: %s",
- "QR code": "QR code",
+ "Não foi possível obter dados de cópia: %s",
+ "QR code": "Código QR",
"This website is using an insecure HTTP connection! Please use it only for testing.":
- "This website is using an insecure HTTP connection! Please use it only for testing.",
+ "Esse site usa uma conexão HTTP insegura! Use-o apenas para testes.",
"For more information see this FAQ entry.":
- "For more information see this FAQ entry.",
+ "Para mais informações veja esse item do FAQ.",
"Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.":
- "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.",
+ "Seu navegador pode exigir uma conexão HTTPS para dar suporte à API WebCrypto. Tente mudar para HTTPS.",
"Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.":
- "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.",
+ "Seu navagador não suporta WebAssembly, usado para compressão zlib. Você pode criar documentos não compactados, mas não pode lê-los.",
"waiting on user to provide a password":
- "waiting on user to provide a password",
+ "esperando que o usuário digite uma senha",
"Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.":
- "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.",
+ "Não foi possível decifrar os dados. Você digitou a senha corretamente? Tente novamente com o botão ao topo.",
"Retry":
- "Retry",
+ "Tentar Novamente",
"Showing raw text…":
- "Showing raw text…"
+ "Mostrando texto bruto…",
+ "Notice:":
+ "Aviso:",
+ "This link will expire after %s.":
+ "Esse link vai expirar após %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "Esse link só pode ser acessado uma vez, não utilize o botão de voltar ou atualizar do seu navegador.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "O recipiente pode ter ciência de seu fuso horário, converter hora para UTC?",
+ "Use Current Timezone":
+ "Usar Fuso Horário Atual",
+ "Convert To UTC":
+ "Converter para UTC",
+ "Close":
+ "Fechar"
}
diff --git a/i18n/ru.json b/i18n/ru.json
index 77a03f61..8973f24c 100644
--- a/i18n/ru.json
+++ b/i18n/ru.json
@@ -31,8 +31,8 @@
"Неверный ключ удаления записи. Запись не удалена.",
"Paste was properly deleted.":
"Запись была успешно удалена.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "Для работы %s требуется включенный JavaScript. Приносим извинения за неудобства.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "Для работы %s требуется включенный JavaScript. Приносим извинения за неудобства.",
"%s requires a modern browser to work.":
"Для работы %s требуется более современный браузер.",
"New":
@@ -178,5 +178,21 @@
"Retry":
"Retry",
"Showing raw text…":
- "Showing raw text…"
+ "Showing raw text…",
+ "Notice:":
+ "Notice:",
+ "This link will expire after %s.":
+ "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone":
+ "Use Current Timezone",
+ "Convert To UTC":
+ "Convert To UTC",
+ "Close":
+ "Close"
}
diff --git a/i18n/sl.json b/i18n/sl.json
index f93d475c..15e57289 100644
--- a/i18n/sl.json
+++ b/i18n/sl.json
@@ -1,7 +1,7 @@
{
"PrivateBin": "PrivateBin",
"%s is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bits AES. More information on the project page.":
- "%s je minimalističen, odprtokodni spletni 'pastebin', kjer server ne ve ničesar o prilepljenih podatkih. Podatki so zakodirani/odkodirani v brskalniku z uporabo 256 bitnega AES. Več informacij na < href=\"https://privatebin.info/\">spletni strani projekta..",
+ "%s je minimalističen, odprtokodni spletni 'pastebin', kjer server ne ve ničesar o prilepljenih podatkih. Podatki so zakodirani/odkodirani v brskalniku z uporabo 256 bitnega AES. Več informacij na spletni strani projekta..",
"Because ignorance is bliss":
"Ker kar ne veš ne boli.",
"en": "sl",
@@ -31,8 +31,8 @@
"Napačen token za izbris. Prilepek ni bil izbrisan..",
"Paste was properly deleted.":
"Prilepek je uspešno izbrisan.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "Da %s deluje, moraš vklopiti JavaScript. Oprosti za povročene nevšečnosti.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "Da %s deluje, moraš vklopiti JavaScript. Oprosti za povročene nevšečnosti.",
"%s requires a modern browser to work.":
"%s za svoje delovanje potrebuje moderen brskalnik.",
"New":
@@ -177,5 +177,21 @@
"Retry":
"Retry",
"Showing raw text…":
- "Showing raw text…"
+ "Showing raw text…",
+ "Notice:":
+ "Notice:",
+ "This link will expire after %s.":
+ "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone":
+ "Use Current Timezone",
+ "Convert To UTC":
+ "Convert To UTC",
+ "Close":
+ "Close"
}
diff --git a/i18n/uk.json b/i18n/uk.json
index 6fa2ee2c..635495d3 100644
--- a/i18n/uk.json
+++ b/i18n/uk.json
@@ -31,8 +31,8 @@
"Неправильний ключ вилучення допису. Допис не вилучено.",
"Paste was properly deleted.":
"Допис був вилучений повністю.",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "Для роботи %s потрібен увімкнутий JavaScript. Вибачте.",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "Для роботи %s потрібен увімкнутий JavaScript. Вибачте.",
"%s requires a modern browser to work.":
"Для роботи %s потрібен більш сучасний переглядач.",
"New":
@@ -178,5 +178,21 @@
"Retry":
"Retry",
"Showing raw text…":
- "Showing raw text…"
+ "Showing raw text…",
+ "Notice:":
+ "Notice:",
+ "This link will expire after %s.":
+ "This link will expire after %s.",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "This link can only be accessed once, do not use back or refresh button in your browser.",
+ "Link:":
+ "Link:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "Recipient may become aware of your timezone, convert time to UTC?",
+ "Use Current Timezone":
+ "Use Current Timezone",
+ "Convert To UTC":
+ "Convert To UTC",
+ "Close":
+ "Close"
}
diff --git a/i18n/zh.json b/i18n/zh.json
index b4bccbde..749d611a 100644
--- a/i18n/zh.json
+++ b/i18n/zh.json
@@ -31,8 +31,8 @@
"错误的删除token,粘贴内容没有被删除。",
"Paste was properly deleted.":
"粘贴内容已被正确删除。",
- "JavaScript is required for %s to work. Sorry for the inconvenience.":
- "%s需要JavaScript来进行加解密。 给你带来的不便敬请谅解。",
+ "JavaScript is required for %s to work. Sorry for the inconvenience.":
+ "%s需要JavaScript来进行加解密。 给你带来的不便敬请谅解。",
"%s requires a modern browser to work.":
"%s需要在现代浏览器上工作。",
"New":
@@ -168,5 +168,21 @@
"Retry":
"重试",
"Showing raw text…":
- "显示原始文字…"
+ "显示原始文字…",
+ "Notice:":
+ "注意:",
+ "This link will expire after %s.":
+ "这个链接将会在 %s 过期。",
+ "This link can only be accessed once, do not use back or refresh button in your browser.":
+ "这个链接只能被访问一次,请勿使用浏览器中的返回和刷新按钮。",
+ "Link:":
+ "链接地址:",
+ "Recipient may become aware of your timezone, convert time to UTC?":
+ "收件人可能会知道您的时区,将时间转换为UTC?",
+ "Use Current Timezone":
+ "使用当前时区",
+ "Convert To UTC":
+ "转换为UTC",
+ "Close":
+ "关闭"
}
diff --git a/index.php b/index.php
index 09e5e684..f346a598 100644
--- a/index.php
+++ b/index.php
@@ -7,7 +7,7 @@
* @link https://github.com/PrivateBin/PrivateBin
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
- * @version 1.3.2
+ * @version 1.3.3
*/
// change this, if your php files and data is outside of your webservers document root
diff --git a/js/privatebin.js b/js/privatebin.js
index dc02e8fd..e76bf98c 100644
--- a/js/privatebin.js
+++ b/js/privatebin.js
@@ -6,7 +6,7 @@
* @see {@link https://github.com/PrivateBin/PrivateBin}
* @copyright 2012 Sébastien SAUVAGE ({@link http://sebsauvage.net})
* @license {@link https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License}
- * @version 1.3.2
+ * @version 1.3.3
* @name PrivateBin
* @namespace
*/
@@ -189,6 +189,26 @@ jQuery.PrivateBin = (function($, RawDeflate) {
const Helper = (function () {
const me = {};
+ /**
+ * character to HTML entity lookup table
+ *
+ * @see {@link https://github.com/janl/mustache.js/blob/master/mustache.js#L60}
+ * @name Helper.entityMap
+ * @private
+ * @enum {Object}
+ * @readonly
+ */
+ const entityMap = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ '/': '/',
+ '`': '`',
+ '=': '='
+ };
+
/**
* cache for script location
*
@@ -302,19 +322,12 @@ jQuery.PrivateBin = (function($, RawDeflate) {
let format = args[0],
i = 1;
return format.replace(/%(s|d)/g, function (m) {
- // m is the matched format, e.g. %s, %d
let val = args[i];
- // A switch statement so that the formatter can be extended.
- switch (m)
- {
- case '%d':
- val = parseFloat(val);
- if (isNaN(val)) {
- val = 0;
- }
- break;
- default:
- // Default is %s
+ if (m === '%d') {
+ val = parseFloat(val);
+ if (isNaN(val)) {
+ val = 0;
+ }
}
++i;
return val;
@@ -393,15 +406,21 @@ jQuery.PrivateBin = (function($, RawDeflate) {
};
/**
- * resets state, used for unit testing
+ * convert all applicable characters to HTML entities
*
- * @name Helper.reset
+ * @see {@link https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html}
+ * @name Helper.htmlEntities
* @function
+ * @param {string} str
+ * @return {string} escaped HTML
*/
- me.reset = function()
- {
- baseUri = null;
- };
+ me.htmlEntities = function(str) {
+ return String(str).replace(
+ /[&<>"'`=\/]/g, function(s) {
+ return entityMap[s];
+ }
+ );
+ }
/**
* calculate expiration date given initial date and expiration period
@@ -443,29 +462,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
};
/**
- * encode all applicable characters to HTML entities
+ * resets state, used for unit testing
*
- * @see {@link https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html}
- *
- * @name Helper.htmlEntities
+ * @name Helper.reset
* @function
- * @param string str
- * @return string escaped HTML
*/
- me.htmlEntities = function(str) {
- // using textarea, since other tags may allow and execute scripts, even when detached from DOM
- let holder = document.createElement('textarea');
- holder.textContent = str;
- // as per OWASP recommendation, also encoding quotes and slash
- return holder.innerHTML.replace(
- /["'\/]/g,
- function(s) {
- return {
- '"': '"',
- "'": ''',
- '/': '/'
- }[s];
- });
+ me.reset = function()
+ {
+ baseUri = null;
};
return me;
@@ -538,10 +542,14 @@ jQuery.PrivateBin = (function($, RawDeflate) {
*
* Optionally pass a jQuery element as the first parameter, to automatically
* let the text of this element be replaced. In case the (asynchronously
- * loaded) language is not downloadet yet, this will make sure the string
- * is replaced when it is actually loaded.
- * So for easy translations passing the jQuery object to apply it to is
- * more save, especially when they are loaded in the beginning.
+ * loaded) language is not downloaded yet, this will make sure the string
+ * is replaced when it eventually gets loaded. Using this is both simpler
+ * and more secure, as it avoids potential XSS when inserting text.
+ * The next parameter is the message ID, matching the ones found in
+ * the translation files under the i18n directory.
+ * Any additional parameters will get inserted into the message ID in
+ * place of %s (strings) or %d (digits), applying the appropriate plural
+ * in case of digits. See also Helper.sprintf().
*
* @name I18n.translate
* @function
@@ -619,31 +627,39 @@ jQuery.PrivateBin = (function($, RawDeflate) {
}
// messageID may contain links, but should be from a trusted source (code or translation JSON files)
- let containsNoLinks = args[0].indexOf(' 0) may never contain HTML as they may come from untrusted parties
- if (i > 0 || containsNoLinks) {
- args[i] = Helper.htmlEntities(args[i]);
+ let containsLinks = args[0].indexOf(' 0) may never contain HTML as they may come from untrusted parties
+ if ((containsLinks ? i > 1 : i > 0) || !containsLinks) {
+ args[i] = Helper.htmlEntities(args[i]);
+ }
}
}
-
// format string
let output = Helper.sprintf.apply(this, args);
- // if $element is given, apply text to element
+ if (containsLinks) {
+ // only allow tags/attributes we actually use in translations
+ output = DOMPurify.sanitize(
+ output, {
+ ALLOWED_TAGS: ['a', 'i', 'span'],
+ ALLOWED_ATTR: ['href', 'id']
+ }
+ );
+ }
+
+ // if $element is given, insert translation
if ($element !== null) {
- if (containsNoLinks) {
- // avoid HTML entity encoding if translation contains links
- $element.text(output);
+ if (containsLinks) {
+ $element.html(output);
} else {
- // only allow tags/attributes we actually use in our translations
- $element.html(
- DOMPurify.sanitize(output, {
- ALLOWED_TAGS: ['a', 'br', 'i', 'span'],
- ALLOWED_ATTR: ['href', 'id']
- })
- );
+ // text node takes care of entity encoding
+ $element.text(output);
}
+ return '';
}
return output;
@@ -1876,11 +1892,10 @@ jQuery.PrivateBin = (function($, RawDeflate) {
return a.length - b.length;
})[0];
if (typeof shortUrl === 'string' && shortUrl.length > 0) {
- $('#pastelink').html(
- I18n._(
- 'Your paste is %s(Hit [Ctrl]+[c] to copy)',
- shortUrl, shortUrl
- )
+ I18n._(
+ $('#pastelink'),
+ 'Your paste is %s(Hit [Ctrl]+[c] to copy)',
+ shortUrl, shortUrl
);
// we disable the button to avoid calling shortener again
$shortenButton.addClass('buttondisabled');
@@ -1935,11 +1950,10 @@ jQuery.PrivateBin = (function($, RawDeflate) {
*/
me.createPasteNotification = function(url, deleteUrl)
{
- $('#pastelink').html(
- I18n._(
- 'Your paste is %s(Hit [Ctrl]+[c] to copy)',
- url, url
- )
+ I18n._(
+ $('#pastelink'),
+ 'Your paste is %s(Hit [Ctrl]+[c] to copy)',
+ url, url
);
// save newly created element
$pasteUrl = $('#pasteurl');
@@ -1947,7 +1961,8 @@ jQuery.PrivateBin = (function($, RawDeflate) {
$pasteUrl.click(pasteLinkClick);
// delete link
- $('#deletelink').html('' + I18n._('Delete data') + '');
+ $('#deletelink').html('');
+ I18n._($('#deletelink a').first(), 'Delete data');
// enable shortener button
$shortenButton.removeClass('buttondisabled');
@@ -2205,6 +2220,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
$messageEdit.addClass('active');
$messagePreview.removeClass('active');
+ $('#messageedit').attr('aria-selected','true');
+ $('#messagepreview').attr('aria-selected','false');
+
PasteViewer.hide();
// reshow input
@@ -2234,6 +2252,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
$messageEdit.removeClass('active');
$messagePreview.addClass('active');
+ $('#messageedit').attr('aria-selected','false');
+ $('#messagepreview').attr('aria-selected','true');
+
// hide input as now preview is shown
$message.addClass('hidden');
@@ -2402,10 +2423,13 @@ jQuery.PrivateBin = (function($, RawDeflate) {
}
// escape HTML entities, link URLs, sanitize
- const escapedLinkedText = Helper.urls2links(
- Helper.htmlEntities(text)
- ),
- sanitizedLinkedText = DOMPurify.sanitize(escapedLinkedText);
+ const escapedLinkedText = Helper.urls2links(text),
+ sanitizedLinkedText = DOMPurify.sanitize(
+ escapedLinkedText, {
+ ALLOWED_TAGS: ['a'],
+ ALLOWED_ATTR: ['href', 'rel']
+ }
+ );
$plainText.html(sanitizedLinkedText);
$prettyPrint.html(sanitizedLinkedText);
@@ -3219,7 +3243,10 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// set & parse text
$commentEntryData.html(
DOMPurify.sanitize(
- Helper.urls2links(commentText)
+ Helper.urls2links(commentText), {
+ ALLOWED_TAGS: ['a'],
+ ALLOWED_ATTR: ['href', 'rel']
+ }
)
);
@@ -3704,8 +3731,9 @@ jQuery.PrivateBin = (function($, RawDeflate) {
const $emailconfirmmodal = $('#emailconfirmmodal');
if ($emailconfirmmodal.length > 0) {
if (expirationDate !== null) {
- $emailconfirmmodal.find('#emailconfirm-display').text(
- I18n._('Recipient may become aware of your timezone, convert time to UTC?')
+ I18n._(
+ $emailconfirmmodal.find('#emailconfirm-display'),
+ 'Recipient may become aware of your timezone, convert time to UTC?'
);
const $emailconfirmTimezoneCurrent = $emailconfirmmodal.find('#emailconfirm-timezone-current');
const $emailconfirmTimezoneUtc = $emailconfirmmodal.find('#emailconfirm-timezone-utc');
@@ -3905,9 +3933,7 @@ jQuery.PrivateBin = (function($, RawDeflate) {
});
} catch (error) {
console.error(error);
- Alert.showError(
- I18n._('Cannot calculate expiration date.')
- );
+ Alert.showError('Cannot calculate expiration date.');
}
}
@@ -5197,7 +5223,10 @@ jQuery.PrivateBin = (function($, RawDeflate) {
// first load translations
I18n.loadTranslations();
- DOMPurify.setConfig({SAFE_FOR_JQUERY: true});
+ DOMPurify.setConfig({
+ ALLOWED_URI_REGEXP: /^(?:(?:(?:f|ht)tps?|mailto|magnet):)/i,
+ SAFE_FOR_JQUERY: true
+ });
// center all modals
$('.modal').on('show.bs.modal', function(e) {
diff --git a/js/purify-2.0.7.js b/js/purify-2.0.7.js
deleted file mode 100644
index 815d4b89..00000000
--- a/js/purify-2.0.7.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.DOMPurify=t()}(this,function(){"use strict";function e(e,t){y&&y(e,null);for(var n=t.length;n--;){var r=t[n];if("string"==typeof r){var o=r.toLowerCase();o!==r&&(Object.isFrozen(t)||(t[n]=o),r=o)}e[r]=!0}return e}function t(e){var t={},n=void 0;for(n in e)g(h,e,[n])&&(t[n]=e[n]);return t}function n(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t0&&void 0!==arguments[0]?arguments[0]:O(),u=function(e){return r(e)};if(u.version="2.0.7",u.removed=[],!o||!o.document||9!==o.document.nodeType)return u.isSupported=!1,u;var h=o.document,y=!1,g=!1,v=o.document,R=o.DocumentFragment,D=o.HTMLTemplateElement,C=o.Node,H=o.NodeFilter,F=o.NamedNodeMap,z=void 0===F?o.NamedNodeMap||o.MozNamedAttrMap:F,I=o.Text,j=o.Comment,U=o.DOMParser,P=o.TrustedTypes;if("function"==typeof D){var W=v.createElement("template");W.content&&W.content.ownerDocument&&(v=W.content.ownerDocument)}var B=N(P,h),q=B?B.createHTML(""):"",G=v,V=G.implementation,Y=G.createNodeIterator,K=G.getElementsByTagName,X=G.createDocumentFragment,$=h.importNode,J={};u.isSupported=V&&void 0!==V.createHTMLDocument&&9!==v.documentMode;var Q=b,Z=T,ee=A,te=x,ne=L,re=E,oe=S,ie=null,ae=e({},[].concat(n(i),n(a),n(l),n(s),n(c))),le=null,se=e({},[].concat(n(d),n(f),n(m),n(p))),ce=null,ue=null,de=!0,fe=!0,me=!1,pe=!1,he=!1,ye=!1,ge=!1,ve=!1,be=!1,Te=!1,Ae=!1,xe=!1,Se=!0,Le=!0,Ee=!1,Me={},ke=e({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","plaintext","script","style","svg","template","thead","title","video","xmp"]),we=e({},["audio","video","img","source","image"]),_e=null,Oe=e({},["alt","class","for","id","label","name","pattern","placeholder","summary","title","value","style","xmlns"]),Ne=null,Re=v.createElement("form"),De=function(r){Ne&&Ne===r||(r&&"object"===(void 0===r?"undefined":M(r))||(r={}),ie="ALLOWED_TAGS"in r?e({},r.ALLOWED_TAGS):ae,le="ALLOWED_ATTR"in r?e({},r.ALLOWED_ATTR):se,_e="ADD_URI_SAFE_ATTR"in r?e(t(Oe),r.ADD_URI_SAFE_ATTR):Oe,ce="FORBID_TAGS"in r?e({},r.FORBID_TAGS):{},ue="FORBID_ATTR"in r?e({},r.FORBID_ATTR):{},Me="USE_PROFILES"in r&&r.USE_PROFILES,de=!1!==r.ALLOW_ARIA_ATTR,fe=!1!==r.ALLOW_DATA_ATTR,me=r.ALLOW_UNKNOWN_PROTOCOLS||!1,pe=r.SAFE_FOR_JQUERY||!1,he=r.SAFE_FOR_TEMPLATES||!1,ye=r.WHOLE_DOCUMENT||!1,be=r.RETURN_DOM||!1,Te=r.RETURN_DOM_FRAGMENT||!1,Ae=r.RETURN_DOM_IMPORT||!1,xe=r.RETURN_TRUSTED_TYPE||!1,ve=r.FORCE_BODY||!1,Se=!1!==r.SANITIZE_DOM,Le=!1!==r.KEEP_CONTENT,Ee=r.IN_PLACE||!1,oe=r.ALLOWED_URI_REGEXP||oe,he&&(fe=!1),Te&&(be=!0),Me&&(ie=e({},[].concat(n(c))),le=[],!0===Me.html&&(e(ie,i),e(le,d)),!0===Me.svg&&(e(ie,a),e(le,f),e(le,p)),!0===Me.svgFilters&&(e(ie,l),e(le,f),e(le,p)),!0===Me.mathMl&&(e(ie,s),e(le,m),e(le,p))),r.ADD_TAGS&&(ie===ae&&(ie=t(ie)),e(ie,r.ADD_TAGS)),r.ADD_ATTR&&(le===se&&(le=t(le)),e(le,r.ADD_ATTR)),r.ADD_URI_SAFE_ATTR&&e(_e,r.ADD_URI_SAFE_ATTR),Le&&(ie["#text"]=!0),ye&&e(ie,["html","head","body"]),ie.table&&(e(ie,["tbody"]),delete ce.tbody),_&&_(r),Ne=r)},Ce=function(e){u.removed.push({element:e});try{e.parentNode.removeChild(e)}catch(t){e.outerHTML=q}},He=function(e,t){try{u.removed.push({attribute:t.getAttributeNode(e),from:t})}catch(e){u.removed.push({attribute:null,from:t})}t.removeAttribute(e)},Fe=function(t){var n=void 0,r=void 0;if(ve)t=""+t;else{var o=t.match(/^[\s]+/);(r=o&&o[0])&&(t=t.slice(r.length))}if(y)try{n=(new U).parseFromString(t,"text/html")}catch(e){}if(g&&e(ce,["title"]),!n||!n.documentElement){var i=(n=V.createHTMLDocument("")).body;i.parentNode.removeChild(i.parentNode.firstElementChild),i.outerHTML=B?B.createHTML(t):t}return t&&r&&n.body.insertBefore(v.createTextNode(r),n.body.childNodes[0]||null),K.call(n,ye?"html":"body")[0]};u.isSupported&&(function(){try{Fe('