diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh index a59fbfbf..67ee2279 100755 --- a/.devcontainer/postCreateCommand.sh +++ b/.devcontainer/postCreateCommand.sh @@ -8,8 +8,7 @@ ln -s ./conf.sample.php cfg/conf.php composer install --no-dev --optimize-autoloader # for PHP unit testing -# composer require google/cloud-storage -# composer install --optimize-autoloader +composer require --global google/cloud-storage sudo chmod a+x "$(pwd)" && sudo rm -rf /var/www/html && sudo ln -s "$(pwd)" /var/www/html diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 32ada5b6..f3a07b52 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,3 +15,10 @@ This PR fixes * [ ] * [ ] * [ ] + +## Disclosure + +* [x] I do have used an AI/LLM tool for the work in this PR. +* [ ] I have **not** used an AI/LLM tool for the work in this PR. + + diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..e004b740 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,119 @@ +# Copilot Coding Agent Onboarding for PrivateBin + +## Repository Overview + +**PrivateBin** is a minimalist, open-source online pastebin where the server has zero knowledge of the pasted data. All data is encrypted/decrypted in the browser via 256-bit AES (Galois Counter Mode). The project is a refactored fork of ZeroBin focused on extensibility and additional features. + +- **Main Use Case:** Secure, ephemeral sharing of text/code, with encryption happening exclusively client-side. +- **Languages:** PHP (~48%), JavaScript (~35%), CSS (~17%), with some legacy browser support (see `legacy.js`). +- **Type:** Web application (pastebin), with both server (PHP) and client (JavaScript) components. + +## Build & Validation Instructions + +### Prerequisites + +- **PHP:** 7.4+ (recommended: latest stable 7.x or 8.x) +- **Composer:** For dependency management (`composer install`) +- **Node.js & npm:** Only required for running JavaScript unit tests. **Main JS logic must remain browser-compatible!** +- **Recommended Environment:** Unix-like OS (Linux, macOS) with Apache or Nginx for full demo. + +### Bootstrap & Install + +1. **Clone the repository** and enter its root directory. +2. **Install PHP dependencies:** + ```sh + composer require --global google/cloud-storage phpunit/phpunit + ``` + - Always run this before building or testing PHP code. + - If you receive permission errors, verify `vendor/` is writable. + +3. **Install JS dependencies (for test only):** + ```sh + cd ./js + npm install + ``` + - Only required for running JS tests. Not needed for building or running the app. + +### Build + +- **No explicit build step** for PHP. The web app is served directly from source. + - This means **composer directory** need to be comitted (_except_ of big optional dependences like Google Cloud like GCS support or similar!) +- **For JavaScript:** There is no webpack/bundler step for release; browser JS is written in compatible ES6+ syntax, except in `legacy.js` (which must be designed to run cleanly even on ancient IE4 or Netscape to display the error message that a browser upgrade is necessary). We are trying to avoid jQuery in any new code and would like to eventually drop use of jQuery. We are considering modularizing the JS logic, but need to ensure to do so in a way that will work both in the browser as well as for node JS driven unit tests. + +### Run + +- **PHP Server Mode:** Use Apache/Nginx with PHP, pointing the web root to the repo root. +- **Demo:** Open the root directory served by the web server in a browser. This should call the index.php in the repositories root directory. + +### Test + +- **PHP Unit Tests:** + ```sh + vendor/bin/phpunit + ``` + - Always run after code changes to backend logic. + - If `vendor/bin/phpunit` does not exist, ensure `composer install` completed without errors. + + ```sh + cd ./js + npm run test + ``` + - Runs Mocha-based tests in Node.js context. Tests are implemented in BDD-Style or using jsVerify fixtures for property-based tests. + - Note: **Production JS must not use Node-only APIs.** Test code may use Node.js features, but main JS logic must remain browser-compatible. + - If you encounter `ReferenceError` for browser features, ensure only test code uses Node.js APIs. + +### Lint + +- **PHP:** Run (if `phpcs.xml` or similar config exists): + ```sh + vendor/bin/phpcs + ``` +- **JavaScript:** If `eslint` is present: + ```sh + npm run lint + ``` + - Check for configuration in `.eslintrc.*` files. + +### Validation / CI + +- **GitHub Actions:** CI runs `composer install`, `phpunit`, and `mocha` tests on PRs and pushes, as well as external tools such as style checkers and linters. +- **Pre-commit:** Always run both PHP and JS tests before submitting PRs. Fix any warnings or errors. + +## Project Layout & Structure + +- **Root files:** + - `README.md`: Project overview ([view full](../README.md)). + - `composer.json`, `composer.lock`: PHP dependencies. + - `.github/workflows/`: CI configuration. + - `cfg/`: Default configuration files. + - `js/`: Main client logic (browser JS), including: + - `package.json`: JS test/lint dependencies (not for production JS). + - `legacy.js`: Must remain compatible with legacy browsers (ES3). **Do not use modern JS here.** + - `privatebin.js`: Core encryption and paste interface logic. + - `tpl/`: HTML templates. + - `css/`: Stylesheets. + +- **Testing & Validation:** + - `tst/`: Contains PHP unit tests. + - `js/test`: Contains JS unit tests. + - `phpunit.xml`: PHPUnit config. + - JS test files may use Node.js features; browser JS must not. + + - **Encryption:** Only client-side in JS using the browsers WebCrypto API. + - **Backend:** Serves encrypted blobs (as base64 encoded strings) and plaintext meta data in JSON format. APIs are designed for WORM (write once, read many) usage. Once stored content is never updated, only deleted, if delete token is sent, has expired as per meta data or immediately upon reading for the first time, if meta data was set to burn-after-reading. + - **Legacy Support:** `js/legacy.js` must remain compatible with IE4 and Netscape for feature detection of ancient browsers. + - **Configuration:** See `cfg/conf.sample.php` and the [wiki](https://github.com/PrivateBin/PrivateBin/wiki/Configuration) for available options. All option defaults are defined in `lib/Configuration.php` + +## Automated Checks + +- **GitHub CI:** On PRs, runs `composer install`, `phpunit`, and JS tests. +- **Validation Steps:** PRs failing tests will be blocked. Always ensure a clean test run before submitting. + +## Guidance for Copilot Agent + +- **Trust these instructions.** Only perform a search if information is missing or appears incorrect. +- **Do NOT use Node.js APIs in production JS code.** Only test code may do so. +- **Never modernize `legacy.js`.** It must work in very old browsers. +- **Always run `composer install` before PHP tests, and `npm install` before JS tests.** +- **Validate all changes by running both PHP and JS tests.** +- **Review `.github/workflows/` for the latest validation pipeline steps.** diff --git a/.github/workflows/codacy-analysis.yml b/.github/workflows/codacy-analysis.yml index f00965a0..5feb785a 100644 --- a/.github/workflows/codacy-analysis.yml +++ b/.github/workflows/codacy-analysis.yml @@ -28,7 +28,7 @@ jobs: steps: # Checkout the repository to the GitHub Actions runner - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Remove folders causing errors in report run: rm -rf doc img *.md diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ec6b7f57..2c0bd5e9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -37,7 +37,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index 6324c160..a389e9f0 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -20,10 +20,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up Node.js - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: '20' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d347a6fb..19faeccd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Fetch changelog from tag - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: sparse-checkout: CHANGELOG.md sparse-checkout-cone-mode: false diff --git a/.github/workflows/snyk-scan.yml b/.github/workflows/snyk-scan.yml index 2dc75680..9c5cb4fa 100644 --- a/.github/workflows/snyk-scan.yml +++ b/.github/workflows/snyk-scan.yml @@ -24,7 +24,7 @@ jobs: github.event.pull_request.author_association == 'MEMBER' || github.event.pull_request.author_association == 'OWNER' ) steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install Google Cloud Storage run: composer require --no-update google/cloud-storage && composer update --no-dev - name: Run Snyk to check for vulnerabilities diff --git a/.github/workflows/test-results.yml b/.github/workflows/test-results.yml index c918e4fa..09a5f1aa 100644 --- a/.github/workflows/test-results.yml +++ b/.github/workflows/test-results.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Download and Extract Artifacts - uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 + uses: dawidd6/action-download-artifact@0bd50d53a6d7fb5cb921e607957e9cc12b4ce392 with: run_id: ${{ github.event.workflow_run.id }} path: artifacts diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cc5dbe33..e89fb4e4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Validate composer.json and composer.lock run: composer validate - name: Install dependencies @@ -27,11 +27,11 @@ jobs: continue-on-error: "${{ matrix.experimental }}" strategy: matrix: - php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] experimental: [false] # uncomment this to start testing on development release # include: -# - php-versions: '8.5' # development release, things can break +# - php-versions: '8.6' # development release, things can break # experimental: true env: extensions: gd, sqlite3 @@ -41,7 +41,7 @@ jobs: # let's get started! - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 # cache PHP extensions - name: Setup cache environment @@ -53,7 +53,7 @@ jobs: key: ${{ runner.os }}-${{ env.extensions-cache-key }} - name: Cache extensions - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ steps.extcache.outputs.dir }} key: ${{ steps.extcache.outputs.key }} @@ -89,7 +89,7 @@ jobs: shell: bash - name: Cache dependencies - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: "${{ steps.composer-cache.outputs.dir }}" key: "${{ runner.os }}-composer-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/composer.json') }}" @@ -112,7 +112,7 @@ jobs: - name: Upload Test Results if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: Test Results (PHP ${{ matrix.php-versions }}) path: tst/results.xml @@ -129,7 +129,7 @@ jobs: # let's get started! - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 # cache PHP extensions - name: Setup cache environment @@ -141,7 +141,7 @@ jobs: key: ${{ runner.os }}-${{ env.extensions-cache-key }} - name: Cache extensions - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ steps.extcache.outputs.dir }} key: ${{ steps.extcache.outputs.key }} @@ -177,7 +177,7 @@ jobs: shell: bash - name: Cache dependencies - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: "${{ steps.composer-cache.outputs.dir }}" key: "${{ runner.os }}-composer-${{ steps.get-date.outputs.date }}-${{ hashFiles('**/composer.json') }}" @@ -203,7 +203,7 @@ jobs: - name: Upload Test Results if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: Test Results path: tst/results.xml @@ -213,10 +213,10 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Setup Node - uses: actions/setup-node@v5 + uses: actions/setup-node@v6 with: node-version: '18' cache: 'npm' @@ -235,7 +235,7 @@ jobs: - name: Upload Test Results if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: Test Results (Mocha) path: js/mocha-results.xml @@ -245,7 +245,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Upload - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: Event File path: "${{ github.event_path }}" diff --git a/CHANGELOG.md b/CHANGELOG.md index 7683a081..d4b61755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # PrivateBin version history +## 2.0.4 (not yet released) +* ADDED: Translations for Swedish +* CHANGED: Deduplicate JSON error message translations +* CHANGED: Refactored translation of exception messages +* FIXED: Some exceptions not getting translated +* FIXED: Attachment disappears after a "paste" in the message area (#1731) +* FIXED: The content format is not reset when creating a new document (#1707) + +## 1.7.9 (2025-11-13) +* CHANGED: Upgrading libraries to: base-x 5.0.1, bootstrap 5.3.8, DOMpurify 3.2.7, ip-lib 1.21.0 & kjua 0.10.0 +* CHANGED: Refactored jQuery DOM element creation into plain JavaScript +* FIXED: Prevent arbitrary PHP file inclusion when enabling template switching ([CVE-2025-64714](https://privatebin.info/reports/vulnerability-2025-11-12-templates.html)) +* FIXED: Malicious filename can be used for self-XSS / HTML injection locally for users ([CVE-2025-64711](https://privatebin.info/reports/vulnerability-2025-11-12-drag-drop.html)) +* FIXED: Sanitize file name in attachment size hint ([CVE-2025-62796](https://privatebin.info/reports/vulnerability-2025-10-28.html)) +* FIXED: Unable to create a new paste from the cloned one when a JSON file attached (#1585) +* FIXED: traffic limiter not working when using Filesystem storage and PHP opcache +* FIXED: Configuration combinations test errors + +## 2.0.3 (2025-11-12) +* FIXED: Prevent arbitrary PHP file inclusion when enabling template switching ([CVE-2025-64714](https://privatebin.info/reports/vulnerability-2025-11-12-templates.html)) +* FIXED: Malicious filename can be used for self-XSS / HTML injection locally for users ([CVE-2025-64711](https://privatebin.info/reports/vulnerability-2025-11-12-drag-drop.html)) +* FIXED: Unable to create a new paste from the cloned one when a JSON file attached (#1585) + +## 2.0.2 (2025-10-28) +* CHANGED: Upgrading libraries to: DOMpurify 3.3.0 +* CHANGED: Refactored jQuery DOM element creation into plain JavaScript +* FIXED: Sanitize file name in attachment size hint ([CVE-2025-62796](https://privatebin.info/reports/vulnerability-2025-10-28.html)) +* FIXED: PHP OPcache module is optional again (#1679) +* FIXED: bootstrap template password peek input group display + ## 2.0.1 (2025-10-12) * ADDED: Auto shorten URLs with config option `shortenbydefault` (#1627) * ADDED: Added `shortenviashlink` endpoint with an `shlink` configuration section @@ -35,7 +65,7 @@ * FIXED: Page template scripts loading order (#1579) ## 1.7.7 (2025-06-28) -* ADDED: Switching templates using the web ui (#1501) +* ADDED: Switching templates using the web UI (#1501) * ADDED: Show file name and size on download page (#603) * CHANGED: Passing large data structures by reference to reduce memory consumption (#858) * CHANGED: Removed use of ctype functions and polyfill library for ctype @@ -73,7 +103,7 @@ * FIXED: Reset password input field on creation of new paste (#1194) * FIXED: Allow database schema upgrade to skip versions (#1343) * FIXED: `bootstrap5` dark mode toggle unset on dark browser preference (#1340) -* FIXED: Prevent bypassing YOURLS proxy URL filter, allowing to shorten non-self URLs +* FIXED: Prevent bypassing YOURLS proxy URL filter, allowing to shorten non-self URLs ([CVE-2024-39899](https://privatebin.info/reports/vulnerability-2024-07-09.html)) ## 1.7.3 (2024-05-13) * CHANGED: Various tweaks of the `bootstrap5` template, suggested by the community @@ -153,7 +183,7 @@ * ADDED: Oracle database support (#868) * ADDED: Configuration option to limit paste creation and commenting to certain IPs (#883) * ADDED: Set CSP also as meta tag, to deal with misconfigured webservers mangling the HTTP header -* ADDED: Sanitize SVG preview, preventing script execution in instance context +* ADDED: Sanitize SVG preview, preventing script execution in instance context ([CVE-2022-24833](https://privatebin.info/reports/vulnerability-2022-04-09.html)) * CHANGED: Language selection cookie only transmitted over HTTPS (#472) * CHANGED: Upgrading libraries to: base-x 4.0.0, bootstrap 3.4.1 (JS), DOMpurify 2.3.6, ip-lib 1.18.0, jQuery 3.6.0, random_compat 2.0.21, Showdown 2.0.3 & zlib 1.2.12 * CHANGED: Removed automatic `.ini` configuration file migration (#808) @@ -205,12 +235,12 @@ * ADDED: Option to send a mail with the link, when creating a paste (#398) * ADDED: Add support for CONFIG_PATH environment variable (#552) * CHANGED: Upgrading libraries to: base-x 3.0.7, DOMpurify 2.0.7 & Showdown 1.9.1 -* FIXED: HTML injection via unescaped attachment filename (#554) +* FIXED: HTML injection via unescaped attachment filename (#554) ([CVE-2020-5223](https://privatebin.info/reports/vulnerability-2020-01-11.html)) * FIXED: Password disabling option (#527) ## 1.2.2 (2020-01-11) * CHANGED: Upgrading libraries to: bootstrap 3.4.1 (CSS), DOMpurify 2.0.7, jQuery 3.4.1, kjua 0.6.0, Showdown 1.9.1 & SJCL 1.0.8 -* FIXED: HTML injection via unescaped attachment filename (#554) +* FIXED: HTML injection via unescaped attachment filename (#554) ([CVE-2020-5223](https://privatebin.info/reports/vulnerability-2020-01-11.html)) ## 1.3.1 (2019-09-22) * ADDED: Translation for Bulgarian (#455) @@ -254,7 +284,7 @@ * CHANGED: Added some missing Russian translations (#348) * CHANGED: Minor PHP refactoring: Rename PrivateBin class to Controller, improved logic of some persistence classes (#342) * CHANGED: Upgrading DOMpurify library to 1.0.7 -* FIXED: Ensure legacy browsers without webcrypto support can't create paste keys with insufficient entropy (#346) +* FIXED: Ensure legacy browsers without webcrypto support can't create paste keys with [insufficient entropy](https://privatebin.info/reports/vulnerability-2018-08-11.html) (#346) * FIXED: Re-add support for old browsers (Firefox<21, Chrome<31, Safari<7, IE<11), broken in 1.2, will be removed again in 1.3 ## 1.2 (2018-07-22) @@ -273,7 +303,7 @@ * FIXED: To counteract regressions introduced by the refactoring, we finally introduced property based unit testing for the JavaScript code, this caught several regressions, but also some very old bugs not found so far (#32) ## 1.1.1 (2017-10-06) -* CHANGED: Switched to `.php` file extension for configuration file, to avoid leaking configuration data in unprotected installation. +* CHANGED: Switched to `.php` file extension for configuration file, to avoid [leaking configuration data](https://privatebin.info/reports/vulnerability-2017-09-29.html) in unprotected installation. ## 1.1 (2016-12-26) * ADDED: Translations for Italian and Russian @@ -314,7 +344,7 @@ * FIXED: Removed unused code detected with the help of various code review tools * FIXED: Table format for PostgreSQL, making it possible to use PostgreSQL as backend in addition to MySQL, SQLite and flat files -## 0.22 (2015-11-09): +## 0.22 (2015-11-09) * ADDED: Tab character input support * ADDED: Dark bootstrap theme * ADDED: Option to hide clone button on expiring pastes @@ -330,13 +360,13 @@ * CHANGED: Database structure to store attachments, allowing larger attachments to be stored (depending on maximum BLOB size of database backend) * CHANGED: Refactored data model, traffic limiting & request handling -## 0.21.1 (2015-09-21): +## 0.21.1 (2015-09-21) * FIXING: lost meta data when using DB model instead of flat files * FIXING: mobile navbar getting triggered on load * CHANGED: database table "paste" gets automatically extended with a "meta" column * CHANGED: navbar of "bootstrap" template now spans full width of view port on large screens -## 0.21 (2015-09-19): +## 0.21 (2015-09-19) * ADDED: Translations for German, French and Polish, language selection menu (optional) * ADDED: File upload and image display support (optional) * ADDED: Markdown format support @@ -354,7 +384,7 @@ encryption), i18n (translation, counterpart of i18n.php) and helper (stateless u * [Translation](https://github.com/PrivateBin/PrivateBin/wiki/Translation) * [Templates](https://github.com/PrivateBin/PrivateBin/wiki/Templates) -## 0.20 (2015-09-03): +## 0.20 (2015-09-03) * ADDED: Password protected pastes (optional) * ADDED: configuration options for highlighting, password, discussions, expiration times, rate limiting * ADDED: JSON-only retrieval of paste incl. discussion, used to be able to refresh paste when posting a comment @@ -365,11 +395,11 @@ encryption), i18n (translation, counterpart of i18n.php) and helper (stateless u * updated JS libraries: jquery to 1.11.3, sjcl to 1.0.2, base64.js to 2.1.9, deflate to 0.5, inflate to 0.3 and prettify to latest * generally improved documentation, both inline phpdoc / JSdoc source code documentation, as well as Wiki pages on installation, configuration, development and JSON-API -## Alpha 0.19 (2013-07-05): +## Alpha 0.19 (2013-07-05) * Corrected XSS security flaw which affected IE<10. Other browsers were not affected. * Corrected spacing display in IE<10. -## Alpha 0.18 (2013-02-24): +## Alpha 0.18 (2013-02-24) * ADDED: The resulting URL is automatically selected after pressing "Send". You just have to press CTRL+C. * ADDED: Automatic syntax highlighting for 53 languages using highlight.js * ADDED: "5 minutes" and "1 week" expirations. @@ -383,32 +413,32 @@ encryption), i18n (translation, counterpart of i18n.php) and helper (stateless u * ADDED: Added version to js/css assets URLs in order to prevent some abusive caches to serve an obsolete version of these files when ZeroBin is upgraded. * "Burn after reading" option has been moved out of Expiration combo to a separate checkbox. Reason is: You can prevent a read-once paste to be available ad vitam eternam on the net. -## Alpha 0.17 (2013-02-23): +## Alpha 0.17 (2013-02-23) * ADDED: Deletion URL. * small refactoring. * improved regex checks. * larger server alt on installation. -## Alpha 0.16: +## Alpha 0.16 * FIXED minor php warnings. * FIXED: zerobin.js reformated and properly commented. * FIXED: Directory structure re-organized. * CHANGED: URL shortening button was removed. (It was bad for privacy.) -## Alpha 0.15 (2012-04-20): +## Alpha 0.15 (2012-04-20) * FIXED: 2 minor corrections to avoid notices in php log. * FIXED: Sources converted to UTF-8. -## Alpha 0.14 (2012-04-20): +## Alpha 0.14 (2012-04-20) * ADDED: GD presence is checked. * CHANGED: Traffic limiter data files moved to data/ (→easier rights management) * ADDED: "Burn after reading" implemented. Opening the URL will display the paste and immediately destroy it on server. -## Alpha 0.13 (2012-04-18): +## Alpha 0.13 (2012-04-18) * FIXED: ''imageantialias()'' call removed because it's not really usefull and can be a problem on most hosts (if GD is not compiled in php). * FIXED: $error not properly initialized in index.php -## Alpha 0.12 (2012-04-18): +## Alpha 0.12 (2012-04-18) ## DISCUSSIONS ! Now you can enable discussions on your pastes. Of course, posted comments and nickname are also encrypted and the server cannot see them. * This feature implies a change in storage format. You will have to delete all previous pastes in your ZeroBin. * Added [[php:vizhash_gd|Vizhash]] as avatars, so you can match posters IP addresses without revealing them. (Same image = same IP). Of course the IP address cannot be deduced from the Vizhash. @@ -416,17 +446,17 @@ encryption), i18n (translation, counterpart of i18n.php) and helper (stateless u * Explicit tags were added to CSS and jQuery selectors (eg. div#aaa instead of #aaa) to speed up browser. * Better cleaning of the URL (to make sure the key is not broken by some stupid redirection service) -## Alpha 0.11 (2012-04-12): +## Alpha 0.11 (2012-04-12) * Automatically ignore parameters (such as &utm_source=...) added //after// the anchor by some stupid Web 2.0 services. * First public release. -## Alpha 0.10 (2012-04-12): +## Alpha 0.10 (2012-04-12) * IE9 does not seem to correctly support ''pre-wrap'' either. Special handling mode activated for all version of IE<10. (Note: ALL other browsers correctly support this feature.) -## Alpha 0.9 (2012-04-11): +## Alpha 0.9 (2012-04-11) * Oh bummer... IE 8 is as shitty as IE6/7: Its does not seem to support ''white-space:pre-wrap'' correctly. I had to activate the special handling mode. I still have to test IE 9. -## Alpha 0.8 (2012-04-11): +## Alpha 0.8 (2012-04-11) * Source code not published yet. * Interface completely redesigned. Icons added. * Now properly supports IE6/7 (ugly display, but it works. "Clone" button is disabled though.) diff --git a/CREDITS.md b/CREDITS.md index dd6729bd..d4c35485 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -69,3 +69,4 @@ * Nicolas Le Gall - Japanese * lazerns - Arabic * Edward205 - Romanian +* babiloof - Swedish diff --git a/Makefile b/Makefile index 9a833116..86165d7c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .PHONY: all coverage coverage-js coverage-php doc doc-js doc-php increment sign test test-js test-php help -CURRENT_VERSION = 2.0.1 -VERSION ?= 2.0.1 +CURRENT_VERSION = 2.0.3 +VERSION ?= 2.0.4 VERSION_FILES = README.md SECURITY.md doc/Installation.md js/package.json lib/Controller.php Makefile REGEX_CURRENT_VERSION := $(shell echo $(CURRENT_VERSION) | sed "s/\./\\\./g") REGEX_VERSION := $(shell echo $(VERSION) | sed "s/\./\\\./g") diff --git a/README.md b/README.md index 8f508758..9ea3adf4 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: 2.0.1* +*Current version: 2.0.3* **PrivateBin** is a minimalist, open source online [pastebin](https://en.wikipedia.org/wiki/Pastebin) diff --git a/SECURITY.md b/SECURITY.md index b0846030..5b37c50d 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -4,8 +4,8 @@ | Version | Supported | | ------- | ------------------ | -| 2.0.1 | :heavy_check_mark: | -| < 2.0.1 | :x: | +| 2.0.3 | :heavy_check_mark: | +| < 2.0.3 | :x: | ## Reporting a Vulnerability diff --git a/cfg/conf.sample.php b/cfg/conf.sample.php index 8fb5288b..b2456a4e 100644 --- a/cfg/conf.sample.php +++ b/cfg/conf.sample.php @@ -280,7 +280,7 @@ dir = PATH "data" ; "urlshortener" needs to point to the base URL of your PrivateBin ; instance with "?shortenviashlink&link=" appended. For example: ; urlshortener = "${basepath}?shortenviashlink&link=" -; This URL will in turn call YOURLS on the server side, using the URL from +; This URL will in turn call Shlink on the server side, using the URL from ; "apiurl" and the API Key from the "apikey" parameters below. ; apiurl = "https://shlink.example.com/rest/v3/short-urls" ; apikey = "your_api_key" diff --git a/composer.lock b/composer.lock index dac377f9..a7a66ba0 100644 --- a/composer.lock +++ b/composer.lock @@ -397,16 +397,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.6.1", + "version": "v5.6.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2" + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", - "reference": "f103601b29efebd7ff4a1ca7b3eeea9e3336a2a2", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", "shasum": "" }, "require": { @@ -449,9 +449,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" }, - "time": "2025-08-13T20:13:15+00:00" + "time": "2025-10-21T19:32:17+00:00" }, { "name": "phar-io/manifest", @@ -2014,16 +2014,16 @@ }, { "name": "theseer/tokenizer", - "version": "1.2.3", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" + "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/d74205c497bfbca49f34d4bc4c19c17e22db4ebb", + "reference": "d74205c497bfbca49f34d4bc4c19c17e22db4ebb", "shasum": "" }, "require": { @@ -2052,7 +2052,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" + "source": "https://github.com/theseer/tokenizer/tree/1.3.0" }, "funding": [ { @@ -2060,7 +2060,7 @@ "type": "github" } ], - "time": "2024-03-03T12:36:25+00:00" + "time": "2025-11-13T13:44:09+00:00" } ], "aliases": [], diff --git a/doc/Installation.md b/doc/Installation.md index f8f97500..f5c508bf 100644 --- a/doc/Installation.md +++ b/doc/Installation.md @@ -30,7 +30,7 @@ for more information. ### Optional Requirements - PHP with GD extension (when using identicon or vizhash icons, jdenticon works - without it) + without it) and OPcache (for better performance) - a database supported by [PHP PDO](https://php.net/manual/book.pdo.php) and the PHP PDO extension (when using database storage) - a Ceph cluster with Rados gateway or AWS S3 storage (when using S3 storage) @@ -120,11 +120,13 @@ More details can be found in the ### Web server configuration -A `robots.txt` file is provided in the root dir of PrivateBin. It disallows all -robots from accessing your pastes. It is recommend to place it into the root of -your web directory if you have installed PrivateBin in a subdirectory. Make sure -to adjust it, so that the file paths match your installation. Of course also -adjust the file, if you already use a `robots.txt`. +A `robots.txt` file is provided in the root dir of PrivateBin. It requests bots to +not access (and potentially burn) your pastes. It is recommend to place it into the +root of your web directory if you have installed PrivateBin in a subdirectory. +Make sure to adjust it, so that the file paths match your installation. +Of course also adjust the file, if you already use a `robots.txt`. +More bot detection is implemented in JavaScript, but note none of these mechanisms is a +100% fail-safe way to prevent non-human visitors on your site. A `.htaccess.disabled` file is provided in the root dir of PrivateBin. It blocks some known robots and link-scanning bots. If you use Apache, you can rename the @@ -203,7 +205,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', '2.0.1'); +INSERT INTO prefix_config VALUES('VERSION', '2.0.3'); ``` In **PostgreSQL**, the `data`, `attachment`, `nickname` and `vizhash` columns diff --git a/doc/README.md b/doc/README.md index d809cd6b..8ab58404 100644 --- a/doc/README.md +++ b/doc/README.md @@ -4,7 +4,7 @@ Please have a look at these questions *before* opening an issue in this repo. -## [Installation guide](https://github.com/PrivateBin/PrivateBin/blob/master/doc/Installation.md#installation) +## [Installation guide](Installation.md#installation) Minimal requirements, hardening and securing your installation and initial configuration. @@ -26,12 +26,12 @@ How to help translate PrivateBin and technical background on it's implementation Know how for participating in PrivateBins development. -### [Generating Source Code Documentation](https://github.com/PrivateBin/PrivateBin/blob/master/doc/Generating%20Source%20Code%20Documentation.md#generating-source-code-documentation) +### [Generating Source Code Documentation](Generating%20Source%20Code%20Documentation.md#generating-source-code-documentation) How to generate the source code API documentation, as found on the project website for [PHP](https://privatebin.info/codedoc/) and [JS](https://privatebin.info/jsdoc/) -### [Running Unit Tests](https://github.com/PrivateBin/PrivateBin/blob/master/doc/Running Unit Tests.md#running-all-unit-tests) +### [Running Unit Tests](Running%20Unit%20Tests.md#running-all-unit-tests) How to run the PHP & JS unit tests, including a brief introduction to property based unit testing. diff --git a/i18n/co.json b/i18n/co.json index 73dbd0fe..5ceea497 100644 --- a/i18n/co.json +++ b/i18n/co.json @@ -165,7 +165,7 @@ "ZB": "Zo", "YB": "Yo", "Format": "Furmatu", - "Plain Text": "Testu in chjaru", + "Plain Text": "Testu rozu", "Source Code": "Codice di fonte", "Markdown": "Markdown", "Download attachment": "Scaricà a pezza ghjunta", diff --git a/i18n/fi.json b/i18n/fi.json index bb7bb44b..304c64ab 100644 --- a/i18n/fi.json +++ b/i18n/fi.json @@ -210,13 +210,13 @@ "Encrypted note on %s": "Salattu viesti %sissä", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Vieraile tässä linkissä nähdäksesi viestin. URL:n antaminen kenellekään antaa heidänkin päästä katsomaan viestiä.", "URL shortener may expose your decrypt key in URL.": "URL-lyhentäjä voi paljastaa purkuavaimesi URL:ssä.", - "URL shortener is enabled by default.": "URL shortener is enabled by default.", + "URL shortener is enabled by default.": "URL-lyhennys on oletuksena käytössä.", "Save document": "Tallenna asiakirja", "Your IP is not authorized to create documents.": "IP:llesi ei ole annettu oikeutta luoda pasteja.", "Trying to shorten a URL that isn't pointing at our instance.": "Yritetään lyhentää URL-osoite, joka ei osoita meidän instanssiiin.", "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Virhe kutsuttaessa YOURLS. Luultavasti asetusongelma kuten väärä tai puuttuuva \"apiurl\" tai \"signature\".", "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Virhe jäsennettäessä YOURLS-vastausta.", - "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.", + "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Välityspalvelinvirhe: Virheellinen vastaus. Tämä voi olla asetusongelma, kuten väärä tai puuttuva asetus, tai väliaikainen katkos.", "This secret message can only be displayed once. Would you like to see it now?": "Tämä salainen viesti voidaan näyttää vain kerran. Haluatko nähdä sen nyt?", "Yes, see it": "Kyllä, näet sen", "Dark Mode": "Tumma tila", @@ -229,7 +229,7 @@ "Link copied to clipboard": "Linkki kopioitu leikepöydälle", "Document text": "Liitä teksti", "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Tabulaattori toimii merkkinä (Paina Ctrl+m tai Esc vaihtaaksesi)", - "Show password": "Show password", - "Hide password": "Hide password", + "Show password": "Näytä salasana", + "Hide password": "Piilota salasana", "Theme": "Teema" } diff --git a/i18n/hu.json b/i18n/hu.json index d284ee18..203d4cf0 100644 --- a/i18n/hu.json +++ b/i18n/hu.json @@ -1,10 +1,10 @@ { "PrivateBin": "PrivateBin", - "%s is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "A %s egy minimalista, nyílt forráskódú adattároló szoftver, ahol a szerver semmilyen információt nem tárol a feltett adatról. Azt ugyanis a %sböngésződ%s segítségével titkosítja és oldja fel 256 bit hosszú titkosítási kulcsú AES-t használva.", + "%s is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "A %s minimalista, nyílt forráskódú adattároló szoftver, ahol a szerver semmilyen információt nem tárol a rábízott adatról. Azt ugyanis a %sböngésződ%s segítségével titkosítja és oldja fel 256 bit hosszú titkosítási kulcsú AES-t használva.", "More information on the project page.": "További információt a projekt oldalán találsz.", "Because ignorance is bliss": "A titok egyfajta hatalom.", "Document does not exist, has expired or has been deleted.": "A bejegyzés nem létezik, lejárt vagy törölve lett.", - "%s requires php %s or above to work. Sorry.": "Bocs, de a %s működéséhez %s vagy ezt meghaladó verziójú php-s környezet szükséges.", + "%s requires php %s or above to work. Sorry.": "Sajnáljuk, de a %s működéséhez %s vagy ezt meghaladó verziójú php-s környezet szükséges.", "%s requires configuration section [%s] to be present in configuration file.": "A %s megfelelő működéséhez a konfigurációs fájlban a [%s] résznek léteznie kell.", "Please wait %d seconds between each post.": [ "Kérlek várj %d másodpercet két beküldés között.", @@ -92,7 +92,7 @@ "%d év" ], "Never": "Soha", - "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Megjegyzés: ez egy teszt szolgáltatás, az adatok bármikor törlődhetnek. Ha visszaélsz vele, kiscicák bánhatják! :)", + "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Megjegyzés: ezen szolgáltatás egy teszt, az adatok bármikor törlődhetnek. Ha visszaélsz vele, kiscicák bánhatják.", "This document will expire in %d seconds.": [ "Ez a bejegyzés %d másodperc múlva megsemmisül.", "Ez a bejegyzés %d másodperc múlva megsemmisül.", @@ -142,7 +142,7 @@ "Anonymous": "Névtelen", "Avatar generated from IP address": "Avatar (az IP cím alapján generáljuk)", "Add comment": "Hozzászólok", - "Optional nickname…": "Becenév (már ha meg akarod adni)", + "Optional nickname…": "Becenév (amennyiben meg akarod adni)", "Post comment": "Beküld", "Sending comment…": "Beküldés alatt...", "Comment posted.": "A hozzászólás beküldve.", @@ -154,7 +154,7 @@ "Your document is %s (Hit Ctrl+c to copy)": "A bejegyzésed a %s címen elérhető. Ctrl+c-vel tudod vágólapra másolni.", "Delete data": "Adat törlése", "Could not create document: %s": "Nem tudtuk létrehozni a bejegyzést: %s", - "Cannot decrypt document: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Nem tudjuk visszafejteni a bejegyzést: a dekódoláshoz szükséges kulcs hiányzik a címből. Talán URL rövidítőt használtál ami kivágta azt belőle?", + "Cannot decrypt document: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Nem tudjuk visszafejteni a bejegyzést: a dekódoláshoz szükséges kulcs hiányzik a címből. Talán URL rövidítőt használtál, amely azt kivágta belőle?", "B": "B", "kB": "kB", "MB": "MB", @@ -190,13 +190,13 @@ "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "Abban az esetben, ha ez az üzenet mindig látható lenne, látogass el a Gyakran Ismételt Kérdések szekcióba a megoldásához.", "+++ no document text +++": "+++ nincs beillesztett szöveg +++", "Could not get document data: %s": "Az adat megszerzése nem sikerült: %s", - "QR code": "QR kód", - "This website is using an insecure HTTP connection! Please use it only for testing.": "Ez a weboldal nem biztonságos HTTP kapcsolatot használ! Emiatt csak teszt célokra ajánljuk.", + "QR code": "QR-kód", + "This website is using an insecure HTTP connection! Please use it only for testing.": "Ez a weboldal nem biztonságos (HTTP) kapcsolatot használ! Emiatt csak teszt céljára ajánljuk.", "For more information see this FAQ entry.": "További információ ebben a GyIK bejegyzésben található (angolul).", "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": "A WebCrypto API használatához a böngésződ számára esetleg HTTPS kapcsolat szükséges. Ezért próbálj meg HTTPS-re váltani.", - "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "A böngésződ nem támogatja a WebAssemblyt, ami a zlib tömörítéshez kell. Létre tudsz hozni tömörítetlen dokumentumokat, de tömörítetteket nem tudsz olvasni.", - "waiting on user to provide a password": "Várakozás a felhasználóra jelszó megadása okán", - "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Nem lehetett visszafejteni az adatot. Rossz jelszót ütöttél be? Ismételd meg a fent található gombbal.", + "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "A böngésződ nem támogatja a WebAssemblyt, amely zlib-tömörítéshez szükséges. Létrehozhatsz tömörítetlen bejegyzést, de tömörítetteket nem tudsz elolvasni.", + "waiting on user to provide a password": "Várakozás jelszó megadására", + "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Nem sikerült visszafejteni az adatot. Rossz jelszót ütöttél be? Ismételd meg a fentebbi gombbal.", "Retry": "Újrapróbálkozás", "Showing raw text…": "Nyers szöveg mutatása…", "Notice:": "Megjegyzés:", @@ -209,27 +209,27 @@ "Close": "Bezárás", "Encrypted note on %s": "Titkosított jegyzet a %s", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Látogasd meg ezt a hivatkozást a bejegyzés megtekintéséhez. Ha mások számára is megadod ezt a linket, azzal hozzáférnek ők is.", - "URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.", - "URL shortener is enabled by default.": "URL shortener is enabled by default.", - "Save document": "Save document", + "URL shortener may expose your decrypt key in URL.": "Az URL-rövidítő kiszolgáltathatja dekódolókulcsod.", + "URL shortener is enabled by default.": "Az URL-rövidítő alapértelmezetten engedélyezett.", + "Save document": "Bejegyzés mentése", "Your IP is not authorized to create documents.": "Your IP is not authorized to create documents.", "Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.", "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.", "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.", "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.", - "This secret message can only be displayed once. Would you like to see it now?": "This secret message can only be displayed once. Would you like to see it now?", - "Yes, see it": "Yes, see it", + "This secret message can only be displayed once. Would you like to see it now?": "Ez az üzenet csak egyszer jeleníthető meg. Szeretnéd megnézni?", + "Yes, see it": "Igen", "Dark Mode": "Sötét mód", - "Error compressing document, due to missing WebAssembly support.": "Error compressing document, due to missing WebAssembly support.", + "Error compressing document, due to missing WebAssembly support.": "WebAssembly-támogatás hiánya miatt nem tömöríthetjük a dokumentumot.", "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.": "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.", - "Start over": "Start over", - "Document copied to clipboard": "Document copied to clipboard", - "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c": "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c", - "Copy link": "Copy link", - "Link copied to clipboard": "Link copied to clipboard", - "Document text": "Document text", - "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)", - "Show password": "Show password", - "Hide password": "Hide password", - "Theme": "Theme" + "Start over": "Újrakezdés", + "Document copied to clipboard": "Bejegyzés másolva", + "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c": "Másoláshoz használd a Ctrl+c/Cmd+c billentyűkombinációt", + "Copy link": "Link másolása", + "Link copied to clipboard": "Link másolva", + "Document text": "Bejegyzés szövege", + "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "A tabulátor karakternek használható (nyomd le a Ctrl+m vagy az Esc to billentyűket ennek megszüntetéséhez).", + "Show password": "Jelszó megjelenítése", + "Hide password": "Jelszó elrejtése", + "Theme": "Téma" } diff --git a/i18n/lt.json b/i18n/lt.json index e611d979..d2da30ab 100644 --- a/i18n/lt.json +++ b/i18n/lt.json @@ -3,7 +3,7 @@ "%s is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s yra minimalistinis, atvirojo kodo internetinis įdėjimų dėklas, kurį naudojant, serveris nieko nenutuokia apie įdėtus duomenis. Duomenys yra šifruojami/iššifruojami %snaršyklėje%s naudojant 256 bitų AES.", "More information on the project page.": "Daugiau informacijos rasite projekto puslapyje.", "Because ignorance is bliss": "Nes nežinojimas yra palaima", - "Document does not exist, has expired or has been deleted.": "Įdėjimo nėra, jis nebegalioja arba buvo ištrintas.", + "Document does not exist, has expired or has been deleted.": "Dokumento nėra, jis nebegalioja arba buvo ištrintas.", "%s requires php %s or above to work. Sorry.": "%s savo darbui reikalauja php %s arba naujesnės versijos. Apgailestaujame.", "%s requires configuration section [%s] to be present in configuration file.": "%s reikalauja, kad konfigūracijos faile būtų [%s] konfigūracijos sekcija.", "Please wait %d seconds between each post.": [ @@ -14,15 +14,15 @@ "Tarp kiekvieno įrašo palaukite %d sekundžių.", "Tarp kiekvieno įrašo palaukite %d sekundžių." ], - "Document is limited to %s of encrypted data.": "Įdėjimas yra apribotas iki %s šifruotų duomenų.", + "Document is limited to %s of encrypted data.": "Dokumentas yra apribotas iki %s šifruotų duomenų.", "Invalid data.": "Neteisingi duomenys.", "You are unlucky. Try again.": "Jums nesiseka. Bandykite dar kartą.", "Error saving comment. Sorry.": "Klaida įrašant komentarą. Apgailestaujame.", - "Error saving document. Sorry.": "Klaida įrašant įdėjimą. Apgailestaujame.", - "Invalid document ID.": "Neteisingas įdėjimo ID.", - "Document is not of burn-after-reading type.": "Įdėjimo tipas nėra „Perskaičius sudeginti“.", - "Wrong deletion token. Document was not deleted.": "Neteisingas ištrynimo prieigos raktas. Įdėjimas nebuvo ištrintas.", - "Document was properly deleted.": "Įdėjimas buvo tinkamai ištrintas.", + "Error saving document. Sorry.": "Klaida įrašant dokumentą. Apgailestaujame.", + "Invalid document ID.": "Neteisingas dokumento ID.", + "Document is not of burn-after-reading type.": "Dokumento tipas nėra „Perskaičius sudeginti“.", + "Wrong deletion token. Document was not deleted.": "Neteisingas ištrynimo prieigos raktas. Dokumentas nebuvo ištrintas.", + "Document was properly deleted.": "Dokumentas buvo tinkamai ištrintas.", "JavaScript is required for %s to work. Sorry for the inconvenience.": "%s darbui reikalinga JavaScript. Atsiprašome už nepatogumus.", "%s requires a modern browser to work.": "%s savo darbui reikalauja šiuolaikinės naršyklės.", "New": "Naujas", @@ -133,9 +133,9 @@ "Šis dokumentas nustos galioti po %d mėnesių.", "Šis dokumentas nustos galioti po %d mėnesių." ], - "Please enter the password for this document:": "Įveskite šio įdėjimo slaptažodį:", + "Please enter the password for this document:": "Įveskite šio dokumento slaptažodį:", "Could not decrypt data (Wrong key?)": "Nepavyko iššifruoti duomenų (Neteisingas raktas?)", - "Could not delete the document, it was not stored in burn after reading mode.": "Nepavyko ištrinti įdėjimo, jis nebuvo saugomas „Perskaičius sudeginti“ veiksenoje.", + "Could not delete the document, it was not stored in burn after reading mode.": "Nepavyko ištrinti dokumento, jis nebuvo saugomas „Perskaičius sudeginti“ veiksenoje.", "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "SKIRTA TIK JŪSŲ AKIMS. Neužverkite šio lango, šis pranešimas negalės būti rodomas dar kartą.", "Could not decrypt comment; Wrong key?": "Nepavyko iššifruoti komentaro; Neteisingas raktas?", "Reply": "Atsakyti", @@ -150,11 +150,11 @@ "unknown status": "nežinoma būsena", "server error or not responding": "serverio klaida arba jis neatsako", "Could not post comment: %s": "Nepavyko paskelbti komentaro: %s", - "Sending document…": "Siunčiamas įdėjimas…", - "Your document is %s (Hit Ctrl+c to copy)": "Jūsų įdėjimas yra %s (Paspauskite Vald+c norėdami nukopijuoti)", + "Sending document…": "Siunčiamas dokumentas…", + "Your document is %s (Hit Ctrl+c to copy)": "Jūsų dokumentas yra %s (Paspauskite Ctrl+c norėdami nukopijuoti)", "Delete data": "Ištrinti duomenis", - "Could not create document: %s": "Nepavyko sukurti įdėjimo: %s", - "Cannot decrypt document: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Nepavyksta iššifruoti įdėjimo: URL adrese trūksta iššifravimo rakto (Ar naudojote peradresavimo ar URL trumpinimo įrankį, kuris pašalina URL dalį?)", + "Could not create document: %s": "Nepavyko sukurti dokumento: %s", + "Cannot decrypt document: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Nepavyksta iššifruoti dokumento: URL adrese trūksta iššifravimo rakto (Ar naudojote peradresavimo ar URL trumpinimo įrankį, kuris pašalina URL dalį?)", "B": "B", "kB": "kB", "MB": "MB", @@ -170,7 +170,7 @@ "Markdown": "„Markdown“", "Download attachment": "Atsisiųsti priedą", "Cloned: '%s'": "Dubliuota: „%s“", - "The cloned file '%s' was attached to this document.": "Dubliuotas failas „%s“ buvo pridėtas į šį įdėjimą.", + "The cloned file '%s' was attached to this document.": "Dubliuotas failas „%s“ buvo pridėtas į šį dokumentą.", "Attach a file": "Pridėti failą", "alternatively drag & drop a file or paste an image from the clipboard": "arba kitaip - tempkite failą arba įdėkite paveikslą iš iškarpinės", "File too large, to display a preview. Please download the attachment.": "Failas per didelis, kad būtų rodoma peržiūra. Atsisiųskite priedą.", @@ -185,11 +185,11 @@ "Decrypt": "Iššifruoti", "Enter password": "Įveskite slaptažodį", "Loading…": "Įkeliama…", - "Decrypting document…": "Iššifruojamas įdėjimas…", - "Preparing new document…": "Ruošiamas naujas įdėjimas…", + "Decrypting document…": "Iššifruojamas dokumentas…", + "Preparing new document…": "Ruošiamas naujas dokumentas…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "Jeigu šis pranešimas niekada nedingsta, pasižiūrėkite šį DUK skyrių, kuriame yra informacija apie nesklandumų šalinimą.", - "+++ no document text +++": "+++ nėra įdėjimo teksto +++", - "Could not get document data: %s": "Nepavyko gauti įdėjimo duomenų: %s", + "+++ no document text +++": "+++ nėra dokumento teksto +++", + "Could not get document data: %s": "Nepavyko gauti dokumento duomenų: %s", "QR code": "QR kodas", "This website is using an insecure HTTP connection! Please use it only for testing.": "Ši internetinė svetainė naudoja nesaugų HTTP ryšį! Naudokite ją tik bandymams.", "For more information see this FAQ entry.": "Išsamesnei informacijai, žiūrėkite šį DUK įrašą.", @@ -210,26 +210,26 @@ "Encrypted note on %s": "Šifruoti užrašai ties %s", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Norėdami matyti užrašus, aplankykite šį tinklalapį. Pasidalinus šiuo URL adresu su kitais žmonėmis, jiems taip pat bus leidžiama prieiga prie šių užrašų.", "URL shortener may expose your decrypt key in URL.": "URL trumpinimo įrankis gali atskleisti URL adrese jūsų iššifravimo raktą.", - "URL shortener is enabled by default.": "URL shortener is enabled by default.", - "Save document": "Įrašyti įdėjimą", - "Your IP is not authorized to create documents.": "Jūsų IP adresas neturi įgaliojimų kurti įdėjimų.", + "URL shortener is enabled by default.": "URL trumpinimo įrankis pagal numatymą įjungtas.", + "Save document": "Įrašyti dokumentą", + "Your IP is not authorized to create documents.": "Jūsų IP adresas neturi įgaliojimų kurti dokumentus.", "Trying to shorten a URL that isn't pointing at our instance.": "Bandoma sutrumpinti URL adresą, kuris nenurodo į mūsų egzempliorių.", - "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Klaida iškviečiant YOURLS. Tikriausiai, konfigūracijos klaida, pavyzdžiui, neteisingi „apiurl“ ar „signature“, arba jų nėra.", - "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Klaida nagrinėjant YOURLS atsaką.", - "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.", + "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Įgaliotojo serverio klaida: Įgaliotojo serverio URL yra tuščias. Tai gali būti konfigūracijos sukelta problema, pavyzdžiui, neteisingi arba trūkstami konfigūracijos raktai.", + "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Įgaliotojo serverio klaida: Klaida nagrinėjant įgaliotojo serverio atsaką. Tai gali būti konfigūracijos sukelta problema, pavyzdžiui, neteisingi arba trūkstami konfigūracijos raktai.", + "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Įgaliotojo serverio klaida: Blogas atsakas. Tai gali būti konfigūracijos sukelta problema, pavyzdžiui, neteisingi arba trūkstami konfigūracijos raktai, arba laikinas avarinis atjungimas.", "This secret message can only be displayed once. Would you like to see it now?": "Ši slapta žinutė gali būti parodyta tik vieną kartą. Ar norėtumėte ją dabar pamatyti?", "Yes, see it": "Taip, pamatyti", "Dark Mode": "Tamsi veiksena", - "Error compressing document, due to missing WebAssembly support.": "Klaida glaudinant įdėjimą, nes trūksta WebAssembly palaikymo.", - "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.": "Klaida išglaudinant įdėjimą, jūsų naršyklė nepalaiko WebAssembly. Norėdami peržiūrėti šį įdėjimą, naudokite kitą naršyklę.", - "Start over": "Start over", - "Document copied to clipboard": "Document copied to clipboard", - "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c": "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c", - "Copy link": "Copy link", - "Link copied to clipboard": "Link copied to clipboard", - "Document text": "Document text", - "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)", - "Show password": "Show password", - "Hide password": "Hide password", - "Theme": "Theme" + "Error compressing document, due to missing WebAssembly support.": "Klaida glaudinant dokumentą, nes trūksta „WebAssembly“ palaikymo.", + "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.": "Klaida išglaudinant dokumentą, jūsų naršyklė nepalaiko „WebAssembly“. Norėdami peržiūrėti šį dokumentą, naudokite kitą naršyklę.", + "Start over": "Pradėti iš naujo", + "Document copied to clipboard": "Dokumentas nukopijuotas į iškarpinę", + "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c": "Norėdami nukopijuoti dokumentą paspauskite kopijavimo mygtuką arba naudokite iškarpinės sparčiuosius klavišus Ctrl+c/Cmd+c", + "Copy link": "Kopijuoti nuorodą", + "Link copied to clipboard": "Nuoroda nukopijuota į iškarpinę", + "Document text": "Dokumento tekstas", + "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Tabuliatoriaus klavišas tarnauja kaip simbolis (Paspauskite Ctrl+m arba Esc norėdami perjungti)", + "Show password": "Rodyti slaptažodį", + "Hide password": "Slėpti slaptažodį", + "Theme": "Apipavidalinimas" } diff --git a/i18n/pl.json b/i18n/pl.json index 4e4c8431..ec85e946 100644 --- a/i18n/pl.json +++ b/i18n/pl.json @@ -229,7 +229,7 @@ "Link copied to clipboard": "Link skopiowany do schowka", "Document text": "Treść dokumentu", "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Klawisz Tabulatora służy jako znak (użyj Ctrl+m lub Esc, aby przełączyć tryb)", - "Show password": "Show password", - "Hide password": "Hide password", + "Show password": "Pokaż hasło", + "Hide password": "Ukryj hasło", "Theme": "Motyw" } diff --git a/i18n/sl.json b/i18n/sl.json index 4dae8239..34a7d654 100644 --- a/i18n/sl.json +++ b/i18n/sl.json @@ -164,16 +164,16 @@ "EB": "EB", "ZB": "ZB", "YB": "YB", - "Format": "Format", + "Format": "Vrsta", "Plain Text": "Surov tekst", "Source Code": "Odprta koda", "Markdown": "Markdown", "Download attachment": "Pretoči priponko", "Cloned: '%s'": "'%s' klonirana", - "The cloned file '%s' was attached to this document.": "The cloned file '%s' was attached to this document.", + "The cloned file '%s' was attached to this document.": "Klonirana datoteka '%s' je bila priložena temu dokumentu.", "Attach a file": "Pripni datoteko", - "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard", - "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.", + "alternatively drag & drop a file or paste an image from the clipboard": "lahko pa datoteko povlečete in spustite ali pa prilepite sliko iz odložišča", + "File too large, to display a preview. Please download the attachment.": "Datoteka je prevelika za prikaz predogleda. Prenesite prilogo.", "Remove attachment": "Odstrani priponko", "Your browser does not support uploading encrypted files. Please use a newer browser.": "Tvoj brskalnik ne omogoča nalaganje zakodiranih datotek. Prosim uporabi novejši brskalnik.", "Invalid attachment.": "Neveljavna priponka.", @@ -181,55 +181,55 @@ "Shorten URL": "Skrajšajte URL", "Editor": "Uredi", "Preview": "Predogled", - "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.", - "Decrypt": "Decrypt", + "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s zahteva, da se POT konča z \"%s\". Prosimo, posodobite POT v datoteki index.php.", + "Decrypt": "Dešifriraj", "Enter password": "Prosim vnesi geslo", - "Loading…": "Loading…", - "Decrypting document…": "Decrypting document…", - "Preparing new document…": "Preparing new document…", + "Loading…": "Nalaganje…", + "Decrypting document…": "Dešifriranje dokumenta…", + "Preparing new document…": "Pripravljanje novega dokumenta…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "In case this message never disappears please have a look at this FAQ for information to troubleshoot (in English).", - "+++ no document text +++": "+++ no document text +++", - "Could not get document data: %s": "Could not get document data: %s", - "QR code": "QR code", - "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.", - "For more information see this FAQ entry.": "For more information see this FAQ entry.", - "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.", - "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.", - "waiting on user to provide a password": "waiting on user to provide a password", - "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.", - "Retry": "Retry", - "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", - "Encrypted note on %s": "Encrypted note on %s", - "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.", - "URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.", - "URL shortener is enabled by default.": "URL shortener is enabled by default.", - "Save document": "Save document", - "Your IP is not authorized to create documents.": "Your IP is not authorized to create documents.", - "Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.", - "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.", - "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.", - "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.", - "This secret message can only be displayed once. Would you like to see it now?": "This secret message can only be displayed once. Would you like to see it now?", - "Yes, see it": "Yes, see it", + "+++ no document text +++": "+++ ni besedila dokumenta +++", + "Could not get document data: %s": "Podatkov dokumenta ni bilo mogoče pridobiti: %s", + "QR code": "QR koda", + "This website is using an insecure HTTP connection! Please use it only for testing.": "To spletno mesto uporablja nezaščiteno povezavo HTTP! Prosimo, uporabite jo samo za testiranje.", + "For more information see this FAQ entry.": "Za več informacij glejte ta vnos s pogostimi vprašanji.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": "Vaš brskalnik morda zahteva povezavo HTTPS za podporo API-ja WebCrypto. Poskusite preklopiti na HTTPS.", + "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Vaš brskalnik ne podpira WebAssemblyja, ki se uporablja za stiskanje zlib. Nestisnjene dokumente lahko ustvarite, stisnjenih pa ne morete brati.", + "waiting on user to provide a password": "čakanje na uporabnika, da vnese geslo", + "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Podatkov ni bilo mogoče dešifrirati. Ste vnesli napačno geslo? Poskusite znova z gumbom na vrhu.", + "Retry": "Poskusi ponovno", + "Showing raw text…": "Prikaz surovega besedila…", + "Notice:": "Obvestilo:", + "This link will expire after %s.": "Ta povezava bo potekla čez %s.", + "This link can only be accessed once, do not use back or refresh button in your browser.": "Do te povezave lahko dostopate samo enkrat, ne uporabljajte gumba za nazaj ali osvežitev v brskalniku.", + "Link:": "Povezava:", + "Recipient may become aware of your timezone, convert time to UTC?": "Prejemnik lahko izve vaš časovni pas in pretvori čas v UTC?", + "Use Current Timezone": "Uporabi trenutni časovni pas", + "Convert To UTC": "Pretvori v UTC", + "Close": "Zapri", + "Encrypted note on %s": "Šifrirana opomba na %s", + "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Za ogled opombe obiščite to povezavo. Če URL posredujete komurkoli, jim omogočite dostop do opombe.", + "URL shortener may expose your decrypt key in URL.": "Okrajševalec URL-jev lahko razkrije vaš ključ za dešifriranje v URL-ju.", + "URL shortener is enabled by default.": "Okrajševalec URL-jev je privzeto omogočen.", + "Save document": "Shrani dokument", + "Your IP is not authorized to create documents.": "Vaš IP ni pooblaščen za ustvarjanje dokumentov.", + "Trying to shorten a URL that isn't pointing at our instance.": "Poskus skrajšanja URL, ki ne kaže na naš primerek.", + "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Napaka posredniškega strežnika: URL posredniškega strežnika je prazen. To je lahko težava s konfiguracijo, na primer napačni ali manjkajoči konfiguracijski ključi.", + "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Napaka posredniškega strežnika: Napaka pri razčlenjevanju odgovora posredniškega strežnika. To je lahko težava s konfiguracijo, na primer napačni ali manjkajoči konfiguracijski ključi.", + "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Napaka posredniškega strežnika: Slab odgovor. To je lahko težava s konfiguracijo, na primer napačni ali manjkajoči konfiguracijski ključi ali začasni izpad.", + "This secret message can only be displayed once. Would you like to see it now?": "To skrivno sporočilo je mogoče prikazati samo enkrat. Ali ga želite videti zdaj?", + "Yes, see it": "Da, pokaži", "Dark Mode": "Temni način", - "Error compressing document, due to missing WebAssembly support.": "Error compressing document, due to missing WebAssembly support.", - "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.": "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.", - "Start over": "Start over", - "Document copied to clipboard": "Document copied to clipboard", - "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c": "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c", - "Copy link": "Copy link", - "Link copied to clipboard": "Link copied to clipboard", - "Document text": "Document text", - "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)", - "Show password": "Show password", - "Hide password": "Hide password", - "Theme": "Theme" + "Error compressing document, due to missing WebAssembly support.": "Napaka pri stiskanju dokumenta zaradi manjkajoče podpore za WebAssembly.", + "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.": "Napaka pri razpakiranju dokumenta, vaš brskalnik ne podpira WebAssembly. Za ogled tega dokumenta uporabite drug brskalnik.", + "Start over": "Začni znova", + "Document copied to clipboard": "Dokument kopiran v odložišče", + "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c": "Za kopiranje dokumenta pritisnite gumb za kopiranje ali uporabite bližnjico odložišča Ctrl+c/Cmd+c", + "Copy link": "Kopiraj povezavo", + "Link copied to clipboard": "Povezava kopirana v odložišče", + "Document text": "Besedilo dokumenta", + "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Tabulatorska tipka služi kot znak (za preklop pritisnite Ctrl+m ali Esc)", + "Show password": "Pokaži geslo", + "Hide password": "Skrij geslo", + "Theme": "Tema" } diff --git a/i18n/sv.json b/i18n/sv.json index 09219db1..553632ec 100644 --- a/i18n/sv.json +++ b/i18n/sv.json @@ -1,160 +1,160 @@ { "PrivateBin": "PrivateBin", - "%s is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.", - "More information on the project page.": "More information on the project page.", - "Because ignorance is bliss": "Because ignorance is bliss", - "Document does not exist, has expired or has been deleted.": "Document does not exist, has expired or has been deleted.", - "%s requires php %s or above to work. Sorry.": "%s requires php %s or above to work. Sorry.", - "%s requires configuration section [%s] to be present in configuration file.": "%s requires configuration section [%s] to be present in configuration file.", + "%s is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s är en minimalistisk, öppen källkods-pastebin där servern inte har någon kunskap om lagrad data. Data krypteras/dekrypteras %si webbläsaren%s med AES 256-bit.", + "More information on the project page.": "Mer information på projektsidan.", + "Because ignorance is bliss": "Okunnighet är salighet", + "Document does not exist, has expired or has been deleted.": "Dokumentet finns inte, har gått ut eller har raderats.", + "%s requires php %s or above to work. Sorry.": "%s kräver PHP %s eller senare för att fungera.", + "%s requires configuration section [%s] to be present in configuration file.": "%s kräver att konfigurationsavsnittet [%s] finns i konfigurationsfilen.", "Please wait %d seconds between each post.": [ - "Please wait %d second between each post. (singular)", - "Please wait %d seconds between each post. (1st plural)", - "Please wait %d seconds between each post. (2nd plural)", - "Please wait %d seconds between each post. (3rd plural)", - "Please wait %d seconds between each post. (4th plural)", - "Please wait %d seconds between each post. (5th plural)" + "Vänta %d sekund mellan varje inlägg.", + "Vänta %d sekunder mellan varje inlägg.", + "Vänta %d sekunder mellan varje inlägg.", + "Vänta %d sekunder mellan varje inlägg.", + "Vänta %d sekunder mellan varje inlägg.", + "Vänta %d sekunder mellan varje inlägg." ], - "Document is limited to %s of encrypted data.": "Document is limited to %s of encrypted data.", - "Invalid data.": "Invalid data.", - "You are unlucky. Try again.": "You are unlucky. Try again.", - "Error saving comment. Sorry.": "Error saving comment. Sorry.", - "Error saving document. Sorry.": "Error saving document. Sorry.", - "Invalid document ID.": "Invalid document ID.", - "Document is not of burn-after-reading type.": "Document is not of burn-after-reading type.", - "Wrong deletion token. Document was not deleted.": "Wrong deletion token. Document was not deleted.", - "Document was properly deleted.": "Document 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.", - "%s requires a modern browser to work.": "%s requires a modern browser to work.", - "New": "New", + "Document is limited to %s of encrypted data.": "Dokumentet är begränsat till %s krypterad data.", + "Invalid data.": "Ogiltig data.", + "You are unlucky. Try again.": "Otur. Försök igen.", + "Error saving comment. Sorry.": "Fel vid sparande av kommentar.", + "Error saving document. Sorry.": "Fel vid sparande av dokument.", + "Invalid document ID.": "Ogiltigt dokument-ID.", + "Document is not of burn-after-reading type.": "Dokumentet är inte av typen ”bränn efter läsning”.", + "Wrong deletion token. Document was not deleted.": "Fel raderingstoken. Dokumentet raderades inte.", + "Document was properly deleted.": "Dokumentet har raderats.", + "JavaScript is required for %s to work. Sorry for the inconvenience.": "JavaScript krävs för att %s ska fungera. Ursäkta besväret.", + "%s requires a modern browser to work.": "%s kräver en modern webbläsare.", + "New": "Ny", "Create": "Skapa", - "Clone": "Clone", - "Raw text": "Raw text", - "Expires": "Expires", - "Burn after reading": "Burn after reading", - "Open discussion": "Open discussion", - "Password (recommended)": "Password (recommended)", - "Discussion": "Discussion", - "Toggle navigation": "Toggle navigation", + "Clone": "Klona", + "Raw text": "Råtext", + "Expires": "Går ut", + "Burn after reading": "Bränn efter läsning", + "Open discussion": "Öppen diskussion", + "Password (recommended)": "Lösenord (rekommenderas)", + "Discussion": "Diskussion", + "Toggle navigation": "Växla navigering", "%d seconds": [ - "%d second (singular)", - "%d seconds (1st plural)", - "%d seconds (2nd plural)", - "%d seconds (3rd plural)", - "%d seconds (4th plural)", - "%d seconds (5th plural)" + "%d sekund", + "%d sekunder", + "%d sekunder", + "%d sekunder", + "%d sekunder", + "%d sekunder" ], "%d minutes": [ - "%d minute (singular)", - "%d minutes (1st plural)", - "%d minutes (2nd plural)", - "%d minutes (3rd plural)", - "%d minutes (4th plural)", - "%d minutes (5th plural)" + "%d minut", + "%d minuter", + "%d minuter", + "%d minuter", + "%d minuter", + "%d minuter" ], "%d hours": [ - "%d hour (singular)", - "%d hours (1st plural)", - "%d hours (2nd plural)", - "%d hours (3rd plural)", - "%d hours (4th plural)", - "%d hours (5th plural)" + "%d timme", + "%d timmar", + "%d timmar", + "%d timmar", + "%d timmar", + "%d timmar" ], "%d days": [ - "%d day (singular)", - "%d days (1st plural)", - "%d days (2nd plural)", - "%d days (3rd plural)", - "%d days (4th plural)", - "%d days (5th plural)" + "%d dag", + "%d dagar", + "%d dagar", + "%d dagar", + "%d dagar", + "%d dagar" ], "%d weeks": [ - "%d week (singular)", - "%d weeks (1st plural)", - "%d weeks (2nd plural)", - "%d weeks (3rd plural)", - "%d weeks (4th plural)", - "%d weeks (5th plural)" + "%d vecka", + "%d veckor", + "%d veckor", + "%d veckor", + "%d veckor", + "%d veckor" ], "%d months": [ - "%d month (singular)", - "%d months (1st plural)", - "%d months (2nd plural)", - "%d months (3rd plural)", - "%d months (4th plural)", - "%d months (5th plural)" + "%d månad", + "%d månader", + "%d månader", + "%d månader", + "%d månader", + "%d månader" ], "%d years": [ - "%d year (singular)", - "%d years (1st plural)", - "%d years (2nd plural)", - "%d years (3rd plural)", - "%d years (4th plural)", - "%d years (5th plural)" + "%d år", + "%d år", + "%d år", + "%d år", + "%d år", + "%d år" ], - "Never": "Never", - "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.", + "Never": "Aldrig", + "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Observera: Detta är en testtjänst. Data kan raderas när som helst. Missbruka inte denna tjänst.", "This document will expire in %d seconds.": [ - "This document will expire in %d second. (singular)", - "This document will expire in %d seconds. (1st plural)", - "This document will expire in %d seconds. (2nd plural)", - "This document will expire in %d seconds. (3rd plural)", - "This document will expire in %d seconds. (4th plural)", - "This document will expire in %d seconds. (5th plural)" + "Detta dokument går ut om %d sekund.", + "Detta dokument går ut om %d sekunder.", + "Detta dokument går ut om %d sekunder.", + "Detta dokument går ut om %d sekunder.", + "Detta dokument går ut om %d sekunder.", + "Detta dokument går ut om %d sekunder." ], "This document will expire in %d minutes.": [ - "This document will expire in %d minute. (singular)", - "This document will expire in %d minutes. (1st plural)", - "This document will expire in %d minutes. (2nd plural)", - "This document will expire in %d minutes. (3rd plural)", - "This document will expire in %d minutes. (4th plural)", - "This document will expire in %d minutes. (5th plural)" + "Detta dokument går ut om %d minut.", + "Detta dokument går ut om %d minuter.", + "Detta dokument går ut om %d minuter.", + "Detta dokument går ut om %d minuter.", + "Detta dokument går ut om %d minuter.", + "Detta dokument går ut om %d minuter." ], "This document will expire in %d hours.": [ - "This document will expire in %d hour. (singular)", - "This document will expire in %d hours. (1st plural)", - "This document will expire in %d hours. (2nd plural)", - "This document will expire in %d hours. (3rd plural)", - "This document will expire in %d hours. (4th plural)", - "This document will expire in %d hours. (5th plural)" + "Detta dokument går ut om %d timme.", + "Detta dokument går ut om %d timmar.", + "Detta dokument går ut om %d timmar.", + "Detta dokument går ut om %d timmar.", + "Detta dokument går ut om %d timmar.", + "Detta dokument går ut om %d timmar." ], "This document will expire in %d days.": [ - "This document will expire in %d day. (singular)", - "This document will expire in %d days. (1st plural)", - "This document will expire in %d days. (2nd plural)", - "This document will expire in %d days. (3rd plural)", - "This document will expire in %d days. (4th plural)", - "This document will expire in %d days. (5th plural)" + "Detta dokument går ut om %d dag.", + "Detta dokument går ut om %d dagar.", + "Detta dokument går ut om %d dagar.", + "Detta dokument går ut om %d dagar.", + "Detta dokument går ut om %d dagar.", + "Detta dokument går ut om %d dagar." ], "This document will expire in %d months.": [ - "This document will expire in %d month. (singular)", - "This document will expire in %d months. (1st plural)", - "This document will expire in %d months. (2nd plural)", - "This document will expire in %d months. (3rd plural)", - "This document will expire in %d months. (4th plural)", - "This document will expire in %d months. (5th plural)" + "Detta dokument går ut om %d månad.", + "Detta dokument går ut om %d månader.", + "Detta dokument går ut om %d månader.", + "Detta dokument går ut om %d månader.", + "Detta dokument går ut om %d månader.", + "Detta dokument går ut om %d månader." ], - "Please enter the password for this document:": "Please enter the password for this document:", - "Could not decrypt data (Wrong key?)": "Could not decrypt data (Wrong key?)", - "Could not delete the document, it was not stored in burn after reading mode.": "Could not delete the document, it was not stored in burn after reading mode.", - "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.", - "Could not decrypt comment; Wrong key?": "Could not decrypt comment; Wrong key?", - "Reply": "Reply", - "Anonymous": "Anonymous", - "Avatar generated from IP address": "Avatar generated from IP address", - "Add comment": "Add comment", - "Optional nickname…": "Optional nickname…", - "Post comment": "Post comment", - "Sending comment…": "Sending comment…", - "Comment posted.": "Comment posted.", - "Could not refresh display: %s": "Could not refresh display: %s", - "unknown status": "unknown status", - "server error or not responding": "server error or not responding", - "Could not post comment: %s": "Could not post comment: %s", - "Sending document…": "Sending document…", - "Your document is %s (Hit Ctrl+c to copy)": "Your document is %s (Hit Ctrl+c to copy)", - "Delete data": "Delete data", - "Could not create document: %s": "Could not create document: %s", - "Cannot decrypt document: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Cannot decrypt document: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)", + "Please enter the password for this document:": "Ange lösenordet för detta dokument:", + "Could not decrypt data (Wrong key?)": "Kunde inte dekryptera data (fel nyckel?)", + "Could not delete the document, it was not stored in burn after reading mode.": "Kunde inte radera dokumentet, det lagrades inte i läget bränn efter läsning.", + "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "ENDAST FÖR DINA ÖGON. Stänger du fönstret så visas detta meddelande inte igen.", + "Could not decrypt comment; Wrong key?": "Kunde inte dekryptera kommentar; fel nyckel?", + "Reply": "Svara", + "Anonymous": "Anonym\n", + "Avatar generated from IP address": "Avatar genererad från IP-adress", + "Add comment": "Lägg till kommentar", + "Optional nickname…": "Valfritt smeknamn…", + "Post comment": "Publicera kommentar", + "Sending comment…": "Skickar kommentar…", + "Comment posted.": "Kommentar publicerad.", + "Could not refresh display: %s": "Kunde inte uppdatera visningen: %s", + "unknown status": "okänd status", + "server error or not responding": "serverfel eller inget svar", + "Could not post comment: %s": "Kunde inte publicera kommentar: %s", + "Sending document…": "Skickar dokument…", + "Your document is %s (Hit Ctrl+c to copy)": "Ditt dokument är %s (Tryck Ctrl+c för att kopiera)", + "Delete data": "Radera data", + "Could not create document: %s": "Kunde inte skapa dokument: %s", + "Cannot decrypt document: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Kan inte dekryptera dokument: Dekrypteringsnyckel saknas i URL:en (använde du en omdirigerare eller URL-förkortare som tar bort delar av URL:en?)", "B": "B", "kB": "kB", "MB": "MB", @@ -165,71 +165,71 @@ "ZB": "ZB", "YB": "YB", "Format": "Format", - "Plain Text": "Plain Text", - "Source Code": "Source Code", + "Plain Text": "Ren text", + "Source Code": "Källkod", "Markdown": "Markdown", - "Download attachment": "Download attachment", - "Cloned: '%s'": "Cloned: '%s'", - "The cloned file '%s' was attached to this document.": "The cloned file '%s' was attached to this document.", - "Attach a file": "Attach a file", - "alternatively drag & drop a file or paste an image from the clipboard": "alternatively drag & drop a file or paste an image from the clipboard", - "File too large, to display a preview. Please download the attachment.": "File too large, to display a preview. Please download the attachment.", - "Remove attachment": "Remove attachment", - "Your browser does not support uploading encrypted files. Please use a newer browser.": "Your browser does not support uploading encrypted files. Please use a newer browser.", - "Invalid attachment.": "Invalid attachment.", - "Options": "Options", - "Shorten URL": "Shorten URL", - "Editor": "Editor", - "Preview": "Preview", - "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.", - "Decrypt": "Decrypt", - "Enter password": "Enter password", - "Loading…": "Loading…", - "Decrypting document…": "Decrypting document…", - "Preparing new document…": "Preparing new document…", - "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "In case this message never disappears please have a look at this FAQ for information to troubleshoot.", - "+++ no document text +++": "+++ no document text +++", - "Could not get document data: %s": "Could not get document data: %s", - "QR code": "QR code", - "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.", - "For more information see this FAQ entry.": "For more information see this FAQ entry.", - "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.", - "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.", - "waiting on user to provide a password": "waiting on user to provide a password", - "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.", - "Retry": "Retry", - "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", - "Encrypted note on %s": "Encrypted note on %s", - "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.", - "URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.", - "URL shortener is enabled by default.": "URL shortener is enabled by default.", - "Save document": "Save document", - "Your IP is not authorized to create documents.": "Your IP is not authorized to create documents.", - "Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.", - "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.", - "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.", - "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.", - "This secret message can only be displayed once. Would you like to see it now?": "This secret message can only be displayed once. Would you like to see it now?", - "Yes, see it": "Yes, see it", + "Download attachment": "Ladda ned bilaga", + "Cloned: '%s'": "Klonad: '%s'", + "The cloned file '%s' was attached to this document.": "Den klonade filen '%s' bifogades till detta dokument.", + "Attach a file": "Bifoga en fil", + "alternatively drag & drop a file or paste an image from the clipboard": "alternativt dra och släpp en fil eller klistra in en bild från urklipp", + "File too large, to display a preview. Please download the attachment.": "Filen är för stor för att visa en förhandsvisning. Ladda ned bilagan.", + "Remove attachment": "Ta bort bilaga", + "Your browser does not support uploading encrypted files. Please use a newer browser.": "Din webbläsare stöder inte uppladdning av krypterade filer. Använd en nyare webbläsare.", + "Invalid attachment.": "Ogiltig bilaga.", + "Options": "Alternativ", + "Shorten URL": "Förkorta URL", + "Editor": "Redigerare", + "Preview": "Förhandsvisning", + "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "%s kräver att sökvägen slutar med \\\"%s\\\". Uppdatera sökvägen i din index.php.", + "Decrypt": "Dekryptera", + "Enter password": "Ange lösenord", + "Loading…": "Läser in...", + "Decrypting document…": "Dekrypterar dokument…", + "Preparing new document…": "Förbereder nytt dokument…", + "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "Om detta meddelande inte försvinner, se denna FAQ för felsökning.", + "+++ no document text +++": "+++ ingen dokumenttext +++", + "Could not get document data: %s": "Kunde inte hämta dokumentdata: %s", + "QR code": "QR-kod", + "This website is using an insecure HTTP connection! Please use it only for testing.": "Denna webbplats använder en osäker HTTP-anslutning! Använd den endast för testning.", + "For more information see this FAQ entry.": "För mer information se denna FAQ-post.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": "Din webbläsare kan kräva en HTTPS-anslutning för att stödja WebCrypto-API:t. Försök byta till HTTPS.", + "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Din webbläsare stöder inte WebAssembly, som används för zlib-komprimering. Du kan skapa okomprimerade dokument, men kan inte läsa komprimerade.", + "waiting on user to provide a password": "väntar på att användaren anger ett lösenord", + "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Kunde inte dekryptera data. Angav du fel lösenord? Försök igen med knappen högst upp.", + "Retry": "Försök igen", + "Showing raw text…": "Visar råtext…", + "Notice:": "Observera:", + "This link will expire after %s.": "Denna länk upphör efter %s.", + "This link can only be accessed once, do not use back or refresh button in your browser.": "Denna länk kan endast öppnas en gång, använd inte tillbaka- eller uppdatera-knappen i din webbläsare.", + "Link:": "Länk:", + "Recipient may become aware of your timezone, convert time to UTC?": "Mottagaren kan se din tidszon, konvertera tiden till UTC?", + "Use Current Timezone": "Använd aktuell tidszon", + "Convert To UTC": "Konvertera till UTC", + "Close": "Stäng", + "Encrypted note on %s": "Krypterad anteckning på %s", + "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besök denna länk för att se anteckningen. Ger du URL:en till någon kan de också få tillgång till den.", + "URL shortener may expose your decrypt key in URL.": "URL-förkortare kan avslöja din dekrypteringsnyckel i URL:en.", + "URL shortener is enabled by default.": "URL-förkortare är aktiverad som standard.", + "Save document": "Spara dokument", + "Your IP is not authorized to create documents.": "Din IP-adress är inte auktoriserad att skapa dokument.", + "Trying to shorten a URL that isn't pointing at our instance.": "Försöker förkorta en URL som inte pekar på vår instans.", + "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Proxyfel: Proxy-URL är tom. Detta kan vara ett konfigurationsfel, t.ex. felaktiga eller saknade nycklar.", + "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Proxyfel: Fel vid tolkning av proxy-svar. Detta kan vara ett konfigurationsfel, t.ex. felaktiga eller saknade nycklar.", + "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Proxyfel: Felaktigt svar. Detta kan vara ett konfigurationsproblem, t.ex. fel eller saknade nycklar, eller ett tillfälligt avbrott.", + "This secret message can only be displayed once. Would you like to see it now?": "Detta hemliga meddelande kan bara visas en gång. Vill du se det nu?", + "Yes, see it": "Ja, visa", "Dark Mode": "Mörkt Läge", - "Error compressing document, due to missing WebAssembly support.": "Error compressing document, due to missing WebAssembly support.", - "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.": "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.", - "Start over": "Start over", - "Document copied to clipboard": "Document copied to clipboard", - "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c": "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c", - "Copy link": "Copy link", - "Link copied to clipboard": "Link copied to clipboard", - "Document text": "Document text", - "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)", - "Show password": "Show password", - "Hide password": "Hide password", - "Theme": "Theme" + "Error compressing document, due to missing WebAssembly support.": "Fel vid komprimering av dokumentet på grund av saknat stöd för WebAssembly.", + "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.": "Fel vid dekomprimering av dokumentet, din webbläsare stöder inte WebAssembly. Använd en annan webbläsare för att visa detta dokument.", + "Start over": "Börja om", + "Document copied to clipboard": "Dokument kopierat till urklipp", + "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c": "För att kopiera dokumentet, klicka på kopieringsknappen eller använd kortkommandot Ctrl+c/Cmd+c", + "Copy link": "Kopiera länk", + "Link copied to clipboard": "Länk kopierad till urklipp", + "Document text": "Dokumenttext", + "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Tabb-tangenten fungerar som tecken (Tryck Ctrl+m eller Esc för att växla)", + "Show password": "Visa lösenord", + "Hide password": "Dölj lösenord", + "Theme": "Tema" } diff --git a/i18n/uk.json b/i18n/uk.json index 5cfa63b5..45ef2286 100644 --- a/i18n/uk.json +++ b/i18n/uk.json @@ -1,10 +1,10 @@ { "PrivateBin": "PrivateBin", - "%s is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s це мінімалістичний Open Source проєкт для створення нотаток, де сервер не знає нічого про дані, що зберігаються. Дані шифруються/розшифровуються %sу браузері%s з використанням 256-бітного шифрування AES.", - "More information on the project page.": "Подробиці можна дізнатися на сайті проєкту.", - "Because ignorance is bliss": "Бо незнання - благо", - "Document does not exist, has expired or has been deleted.": "Допис не існує, протермінований чи був видалений.", - "%s requires php %s or above to work. Sorry.": "Для роботи %s потрібен php %s и вище. Вибачте.", + "%s is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted %sin the browser%s using 256 bits AES.": "%s — мінімалістична вільна програма для створення нотаток, сервер якої не знає нічого про дані, що зберігаються. Дані шифруються/розшифровуються %sу браузері%s з використанням 256-бітного шифрування AES.", + "More information on the project page.": "Докладніше — на сайті проєкту.", + "Because ignorance is bliss": "Менше знаєш — краще спиш", + "Document does not exist, has expired or has been deleted.": "Документ не існує. Можливо, його протерміновано чи видалено.", + "%s requires php %s or above to work. Sorry.": "Для роботи %s потрібен php %s чи вище. Вибачте.", "%s requires configuration section [%s] to be present in configuration file.": "%s потрібна секція [%s] в конфігураційному файлі.", "Please wait %d seconds between each post.": [ "Будь ласка, зачекайте %d секунду між створеннями.", @@ -14,18 +14,18 @@ "Будь ласка, зачекайте %d секунд між створеннями.", "Будь ласка, зачекайте %d секунд між створеннями." ], - "Document is limited to %s of encrypted data.": "Розмір допису обмежений %s зашифрованих даних.", + "Document is limited to %s of encrypted data.": "Розмір документа обмежено %s зашифрованих даних.", "Invalid data.": "Неправильні дані.", "You are unlucky. Try again.": "Якась халепа! Спробуйте ще раз.", - "Error saving comment. Sorry.": "Помилка при збереженні коментаря. Вибачте.", - "Error saving document. Sorry.": "Помилка при збереженні допису. Вибачте.", - "Invalid document ID.": "Неправильний ID допису.", - "Document is not of burn-after-reading type.": "Тип допису не \"Знищити після прочитання\".", - "Wrong deletion token. Document was not deleted.": "Неправильний жетон вилучення допису. Допис не вилучено.", - "Document was properly deleted.": "Допис був вилучений повністю.", + "Error saving comment. Sorry.": "Не вдалося зберегти коментар. Вибачте.", + "Error saving document. Sorry.": "Не вдалося зберегти документ. Вибачте.", + "Invalid document ID.": "Хибний ідентифікатор документа.", + "Document is not of burn-after-reading type.": "Тип документа не «Знищити після прочитання».", + "Wrong deletion token. Document was not deleted.": "Хибний токен видалення. Документ не видалено.", + "Document was properly deleted.": "Документ видалено повністю.", "JavaScript is required for %s to work. Sorry for the inconvenience.": "Для роботи %s потрібен увімкнутий JavaScript. Вибачте.", "%s requires a modern browser to work.": "Для роботи %s потрібен більш сучасний браузер.", - "New": "Новий допис", + "New": "Новий документ", "Create": "Створити", "Clone": "Дублювати", "Raw text": "Початковий текст", @@ -36,8 +36,8 @@ "Discussion": "Обговорення", "Toggle navigation": "Перемкнути навігацію", "%d seconds": [ - "%d секунд", - "%d секунд", + "%d секунду", + "%d секунди", "%d секунд", "%d секунд", "%d секунд", @@ -92,144 +92,144 @@ "%d років" ], "Never": "Ніколи", - "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Примітка: Це тестовий сервіс: Дані можуть бути вилучені в будь який момент. Кошенята помруть, якщо ви будете зловживати сервісом.", + "Note: This is a test service: Data may be deleted anytime. Kittens will die if you abuse this service.": "Примітка: це тестовий сервіс, і дані можуть бути видалені в будь-який момент. Будете зловживати — пси вам марша зіграють.", "This document will expire in %d seconds.": [ - "Документ буде вилучений через %d секунду.", - "Документ буде вилучений через %d секунди.", - "Документ буде вилучений через %d секунд.", - "Документ буде вилучений через %d секунд.", - "Документ буде вилучений через %d секунд.", - "Документ буде вилучений через %d секунд." + "Документ буде видалено через %d секунду.", + "Документ буде видалено через %d секунди.", + "Документ буде видалено через %d секунд.", + "Документ буде видалено через %d секунд.", + "Документ буде видалено через %d секунд.", + "Документ буде видалено через %d секунд." ], "This document will expire in %d minutes.": [ - "Документ буде вилучений через %d хвилину.", - "Документ буде вилучений через %d хвилини.", - "Документ буде вилучений через %d хвилин.", - "Документ буде вилучений через %d хвилин.", - "Документ буде вилучений через %d хвилин.", - "Документ буде вилучений через %d хвилин." + "Документ буде видалено через %d хвилину.", + "Документ буде видалено через %d хвилини.", + "Документ буде видалено через %d хвилин.", + "Документ буде видалено через %d хвилин.", + "Документ буде видалено через %d хвилин.", + "Документ буде видалено через %d хвилин." ], "This document will expire in %d hours.": [ - "Документ буде вилучений через %d годину.", - "Документ буде вилучений через %d години.", - "Документ буде вилучений через %d годин.", - "Документ буде вилучений через %d годин.", - "Документ буде вилучений через %d годин.", - "Документ буде вилучений через %d годин." + "Документ буде видалено через %d годину.", + "Документ буде видалено через %d години.", + "Документ буде видалено через %d годин.", + "Документ буде видалено через %d годин.", + "Документ буде видалено через %d годин.", + "Документ буде видалено через %d годин." ], "This document will expire in %d days.": [ - "Документ буде вилучений через %d день.", - "Документ буде вилучений через %d дні.", - "Документ буде вилучений через %d днів.", - "Документ буде вилучений через %d днів.", - "Документ буде вилучений через %d днів.", - "Документ буде вилучений через %d днів." + "Документ буде видалено через %d день.", + "Документ буде видалено через %d дні.", + "Документ буде видалено через %d днів.", + "Документ буде видалено через %d днів.", + "Документ буде видалено через %d днів.", + "Документ буде видалено через %d днів." ], "This document will expire in %d months.": [ - "Документ буде вилучений через %d місяць.", - "Документ буде вилучений через %d місяці.", - "Документ буде вилучений через %d місяців.", - "Документ буде вилучений через %d місяців.", - "Документ буде вилучений через %d місяців.", - "Документ буде вилучений через %d місяців." + "Документ буде видалено через %d місяць.", + "Документ буде видалено через %d місяці.", + "Документ буде видалено через %d місяців.", + "Документ буде видалено через %d місяців.", + "Документ буде видалено через %d місяців.", + "Документ буде видалено через %d місяців." ], - "Please enter the password for this document:": "Будь ласка, введіть пароль від допису:", - "Could not decrypt data (Wrong key?)": "Неможливо розшифрувати дані (можливо, невірний ключ?)", - "Could not delete the document, it was not stored in burn after reading mode.": "Неможливо вилучити допис, він не був збережений в режимі знищити після прочитання.", - "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "ЛИШЕ ДЛЯ ВАШИХ ОЧЕЙ. Не закривайте це вікно, це повідомлення не може бути показано знову.", - "Could not decrypt comment; Wrong key?": "Неможливо розшифрувати коментар; Неправильний ключ?", + "Please enter the password for this document:": "Введіть пароль від документа:", + "Could not decrypt data (Wrong key?)": "Неможливо розшифрувати дані (хибний ключ?)", + "Could not delete the document, it was not stored in burn after reading mode.": "Неможливо видалити документ, його не збережено в режимі «Знищити після прочитання».", + "FOR YOUR EYES ONLY. Don't close this window, this message can't be displayed again.": "ЛИШЕ ДЛЯ ВАС. Не закривайте вікно, це повідомлення одноразове.", + "Could not decrypt comment; Wrong key?": "Неможливо розшифрувати коментар. Хибний ключ?", "Reply": "Відповісти", "Anonymous": "Анонім", - "Avatar generated from IP address": "Аватар зґенерований з IP-адреси", + "Avatar generated from IP address": "Аватар зґенеровано з IP-адреси", "Add comment": "Додати коментар", "Optional nickname…": "Необов’язкове прізвисько…", - "Post comment": "Відправити коментар", - "Sending comment…": "Відправка коментаря…", - "Comment posted.": "Коментар опублікований.", + "Post comment": "Надіслати коментар", + "Sending comment…": "Надсилання коментаря…", + "Comment posted.": "Коментар опубліковано.", "Could not refresh display: %s": "Не вдалося оновити екран: %s", "unknown status": "невідома причина", "server error or not responding": "помилка на сервері чи немає відповіді", "Could not post comment: %s": "Не вдалося опублікувати коментар: %s", - "Sending document…": "Відправка допису…", - "Your document is %s (Hit Ctrl+c to copy)": "Посилання на допис %s (Тисніть Ctrl+c, щоб скопіювати посилання)", - "Delete data": "Видалити допис", - "Could not create document: %s": "Не вдалося опублікувати допис: %s", - "Cannot decrypt document: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Неможливо розшифрувати запис: Ключ дешифрування відсутній в посиланні (Можливо, ви використовуєте скорочувач посилань, що видаляє частину посилання?)", - "B": "байт", - "kB": "кбайт", - "MB": "Мбайт", - "GB": "Гбайт", - "TB": "Тбайт", - "PB": "Пбайт", - "EB": "Ебайт", - "ZB": "Збайт", - "YB": "Йбайт", + "Sending document…": "Надсилання документа…", + "Your document is %s (Hit Ctrl+c to copy)": "Документ: %s (копіювати: Ctrl+c)", + "Delete data": "Видалити дані", + "Could not create document: %s": "Не вдалося опублікувати документ: %s", + "Cannot decrypt document: Decryption key missing in URL (Did you use a redirector or an URL shortener which strips part of the URL?)": "Не вдається розшифрувати документ: ключ дешифрування відсутній у посиланні (можливо, ваш сервіс переспрямування чи скорочення посилань видаляє частину посилання?)", + "B": "Б", + "kB": "кБ", + "MB": "МБ", + "GB": "ГБ", + "TB": "ТБ", + "PB": "ПБ", + "EB": "ЕБ", + "ZB": "ЗБ", + "YB": "ЙБ", "Format": "Формат", "Plain Text": "Звичайний текст", "Source Code": "Вихідний код", "Markdown": "Мова розмітки", - "Download attachment": "Звантажити прикріплений файл", - "Cloned: '%s'": "Дубльовано: '%s'", - "The cloned file '%s' was attached to this document.": "Дублікат файлу '%s' був прикріплений до цього запису.", - "Attach a file": "Прикріпити файл", - "alternatively drag & drop a file or paste an image from the clipboard": "також можна перенести файл у вікно переглядача чи вставити зображення з буфера", - "File too large, to display a preview. Please download the attachment.": "Файл завеликий для відображення передогляду. Будь ласка, звантажте прикріплений файл.", + "Download attachment": "Завантажити вкладення", + "Cloned: '%s'": "Дубльовано: «%s»", + "The cloned file '%s' was attached to this document.": "Дублікат файлу «%s» вкладено до цього документа.", + "Attach a file": "Вкласти файл", + "alternatively drag & drop a file or paste an image from the clipboard": "також можна перенести файл у вікно браузера чи вставити зображення з буфера", + "File too large, to display a preview. Please download the attachment.": "Файл завеликий для передогляду. Будь ласка, завантажте вкладення.", "Remove attachment": "Видалити вкладення", - "Your browser does not support uploading encrypted files. Please use a newer browser.": "Ваш переглядач не підтримує відправлення зашифрованих файлів. Використовуйте сучасніший переглядач.", - "Invalid attachment.": "Невідоме вкладення.", + "Your browser does not support uploading encrypted files. Please use a newer browser.": "Ваш браузер не підтримує надсилання зашифрованих файлів. Використовуйте сучасніший браузер.", + "Invalid attachment.": "Непідтримуване вкладення.", "Options": "Опції", "Shorten URL": "Коротке посилання", "Editor": "Редактор", "Preview": "Передогляд", - "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "Змінна PATH необхідна %s в конці \"%s\". Будь ласка, оновіть змінну PATH у вашому index.php.", + "%s requires the PATH to end in a \"%s\". Please update the PATH in your index.php.": "Для роботи %s у кінці змінної PATH має бути «%s». Будь ласка, оновіть змінну PATH у вашому index.php.", "Decrypt": "Розшифрувати", "Enter password": "Введіть пароль", "Loading…": "Завантаження…", - "Decrypting document…": "Розшифровування допису…", - "Preparing new document…": "Приготування нового допису…", + "Decrypting document…": "Розшифрування документа…", + "Preparing new document…": "Підготовка нового документа…", "In case this message never disappears please have a look at this FAQ for information to troubleshoot.": "Якщо це повідомлення не зникатиме тривалий час, подивіться цей FAQ з інформацією про можливе вирішення проблеми.", - "+++ no document text +++": "+++ у дописі немає тексту +++", - "Could not get document data: %s": "Не вдалося отримати дані допису: %s", + "+++ no document text +++": "+++ документ без тексту +++", + "Could not get document data: %s": "Не вдалося отримати дані документа: %s", "QR code": "QR код", - "This website is using an insecure HTTP connection! Please use it only for testing.": "Цей сайт використовує незахищене HTTP підключення! Будь ласка, використовуйте його лише для тестування.", - "For more information see this FAQ entry.": "Для подробиць дивіться інформацію в FAQ.", - "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": "Ваш переглядач вимагає підключення HTTPS для підтримки WebCrypto API. Спробуйте перемкнутися на HTTPS.", - "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Ваш переглядач не підтримує WebAssembly, що використовується для стиснення zlib. Ви можете створювати нестиснені документи, але не зможете читати стиснені.", - "waiting on user to provide a password": "очікування користувача для вводу паролю", + "This website is using an insecure HTTP connection! Please use it only for testing.": "Сайт використовує незахищене HTTP-з'єднання! Користуйтесь лише для тестування.", + "For more information see this FAQ entry.": "Докладніше — в FAQ.", + "Your browser may require an HTTPS connection to support the WebCrypto API. Try switching to HTTPS.": "WebCrypto API може не працювати у вашому браузері без HTTPS-з'єднання. Спробуйте перемкнутися на HTTPS.", + "Your browser doesn't support WebAssembly, used for zlib compression. You can create uncompressed documents, but can't read compressed ones.": "Ваш браузер не підтримує WebAssembly, без якого zlib-стиснення не працює. Ви можете створювати нестиснені документи, але не можете читати стиснені.", + "waiting on user to provide a password": "очікування користувача для вводу пароля", "Could not decrypt data. Did you enter a wrong password? Retry with the button at the top.": "Не вдалося розшифрувати дані. Може, ви ввели неправильний пароль? Спробуйте знову за допомогою верхньої кнопки.", "Retry": "Спробуйте ще раз", - "Showing raw text…": "Відображається неформатований текст…", + "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.": "Дане посилання доступна тільки один раз, не натискайте кнопку назад та не обновляйте сторінку браузера.", + "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": "Закрити", - "Encrypted note on %s": "Зашифрована нотатка на %s", + "Encrypted note on %s": "Зашифрована нотатка в %s", "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Відвідайте посилання, щоб переглянути нотатку. Передача посилання будь-кому дозволить їм переглянути нотатку.", "URL shortener may expose your decrypt key in URL.": "Сервіс скорочення посилань може викрити ваш ключ дешифрування з URL.", - "URL shortener is enabled by default.": "URL shortener is enabled by default.", - "Save document": "Зберегти вставку", - "Your IP is not authorized to create documents.": "Вашому IP не дозволено створювати вставки.", - "Trying to shorten a URL that isn't pointing at our instance.": "Спроба скоротити URL, який не вказує на наш екземпляр.", - "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Помилка виклику YOURLS. Ймовірно проблема з налаштуванням, наприклад \"apiurl\" чи \"signature\".", - "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Помилка розбору відповіді YOURLS.", - "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.", + "URL shortener is enabled by default.": "Скорочення посилань типово увімкнено.", + "Save document": "Зберегти документ", + "Your IP is not authorized to create documents.": "Вашій IP-адресі не дозволено створювати документи.", + "Trying to shorten a URL that isn't pointing at our instance.": "Спроба скоротити URL, який не вказує на наш сервер.", + "Proxy error: Proxy URL is empty. This can be a configuration issue, like wrong or missing config keys.": "Помилка проксі: не вказано URL-адреси проксі. Ймовірно, проблема в налаштуваннях: ключі конфігурації можуть бути відсутні або містити одрук.", + "Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.": "Помилка проксі: не вдається розпізнати відповідь проксі. Ймовірно, проблема в налаштуваннях: ключі конфігурації можуть бути відсутні або містити одрук", + "Proxy error: Bad response. This can be a configuration issue, like wrong or missing config keys or a temporary outage.": "Помилка проксі: хибна відповідь. Ймовірно, проблема в налаштуваннях: ключі конфігурації можуть бути відсутні або містити одрук.", "This secret message can only be displayed once. Would you like to see it now?": "Це таємне повідомлення можна надіслати лише один раз. Хочете переглянути його зараз?", "Yes, see it": "Так, побачити", "Dark Mode": "Темний режим", - "Error compressing document, due to missing WebAssembly support.": "Помилка при стисканні допису, через відсутність підтримки WebAssembly сервера.", - "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.": "Помилка при розпакуванні допису, бо ваш браузер не підтримує WebAssembly. Будь ласка, відкрийте в іншому браузері для перегляду цього допису.", + "Error compressing document, due to missing WebAssembly support.": "Помилка стиснення документа: сервер не підтримує WebAssembly.", + "Error decompressing document, your browser does not support WebAssembly. Please use another browser to view this document.": "Помилка розпакування документа: браузер не підтримує WebAssembly. Спробуйте відкрити документ в іншому браузері.", "Start over": "Почати знову", - "Document copied to clipboard": "Document copied to clipboard", - "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c": "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c", - "Copy link": "Copy link", - "Link copied to clipboard": "Link copied to clipboard", - "Document text": "Document text", - "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)", - "Show password": "Show password", - "Hide password": "Hide password", - "Theme": "Theme" + "Document copied to clipboard": "Документ скопійовано до буфера", + "To copy document press on the copy button or use the clipboard shortcut Ctrl+c/Cmd+c": "Щоб скопіювати документ, натисніть кнопку «Копіювати» чи клавіші Ctrl+c/Cmd+c", + "Copy link": "Копіювати посилання", + "Link copied to clipboard": "Посилання скопійовано до буфера", + "Document text": "Текст документа", + "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Клавіша Tab вводить символ табуляції (перемикається клавішами Ctrl+m чи Esc)", + "Show password": "Показати пароль", + "Hide password": "Сховати пароль", + "Theme": "Тема" } diff --git a/i18n/zh.json b/i18n/zh.json index dc4ea8f0..7406fdb0 100644 --- a/i18n/zh.json +++ b/i18n/zh.json @@ -229,7 +229,7 @@ "Link copied to clipboard": "链接已复制到剪贴板", "Document text": "粘贴文本", "Tabulator key serves as character (Hit Ctrl+m or Esc to toggle)": "Tab 键可作为字符(按 Ctrl+mEsc 切换开关)", - "Show password": "Show password", - "Hide password": "Hide password", + "Show password": "显示密码", + "Hide password": "隐藏密码", "Theme": "主题" } diff --git a/js/common.js b/js/common.js index 8aa1bf72..fb3129ca 100644 --- a/js/common.js +++ b/js/common.js @@ -15,7 +15,7 @@ require('./prettify'); global.prettyPrint = window.PR.prettyPrint; global.prettyPrintOne = window.PR.prettyPrintOne; global.showdown = require('./showdown-2.1.0'); -global.DOMPurify = require('./purify-3.2.7'); +global.DOMPurify = require('./purify-3.3.0'); global.baseX = require('./base-x-5.0.1').baseX; global.Legacy = require('./legacy').Legacy; require('./privatebin'); diff --git a/js/package-lock.json b/js/package-lock.json index 80b713d3..c06d5bf8 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -1,12 +1,12 @@ { "name": "privatebin", - "version": "2.0.0", + "version": "2.0.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "privatebin", - "version": "2.0.0", + "version": "2.0.3", "license": "zlib-acknowledgement", "devDependencies": { "@peculiar/webcrypto": "^1.5.0", @@ -1275,11 +1275,10 @@ "license": "ISC" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -2785,9 +2784,9 @@ "dev": true }, "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "requires": { "argparse": "^2.0.1" diff --git a/js/package.json b/js/package.json index 4f11bf2e..8e1bb434 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "privatebin", - "version": "2.0.1", + "version": "2.0.3", "description": "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of stored data. Data is encrypted/decrypted in the browser using 256 bit AES in Galois Counter mode (GCM).", "main": "privatebin.js", "directories": { diff --git a/js/privatebin.js b/js/privatebin.js index 742abb42..a6cdfd6e 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -671,7 +671,7 @@ jQuery.PrivateBin = (function($) { * @prop {string[]} * @readonly */ - const supportedLanguages = ['ar', 'bg', 'ca', 'co', 'cs', 'de', 'el', 'es', 'et', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ro', 'ru', 'sk', 'sl', 'th', 'tr', 'uk', 'zh']; + const supportedLanguages = ['ar', 'bg', 'ca', 'co', 'cs', 'de', 'el', 'es', 'et', 'fi', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'jbo', 'lt', 'no', 'nl', 'pl', 'pt', 'oc', 'ro', 'ru', 'sk', 'sl', 'sv', 'th', 'tr', 'uk', 'zh']; /** * built in language @@ -887,7 +887,7 @@ jQuery.PrivateBin = (function($) { return n % 10 === 1 && n % 100 !== 11 ? 0 : (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20) ? 1 : 2); case 'sl': return n % 100 === 1 ? 1 : (n % 100 === 2 ? 2 : (n % 100 === 3 || n % 100 === 4 ? 3 : 0)); - // bg, ca, de, el, en, es, et, fi, hu, it, nl, no, pt + // bg, ca, de, el, en, es, et, fi, hu, it, nl, no, pt, sv default: return n !== 1 ? 1 : 0; } @@ -2992,7 +2992,8 @@ jQuery.PrivateBin = (function($) { attachmentLink.attr('download', fileName); const fileSize = Helper.formatBytes(decodedData.length); - template.append(`(${fileName}, ${fileSize})`); + const fileInfo = document.createTextNode(` (${fileName}, ${fileSize})`); + template[0].appendChild(fileInfo); } // sanitize SVG preview @@ -3085,10 +3086,15 @@ jQuery.PrivateBin = (function($) { * @name AttachmentViewer.printDragAndDropFileNames * @private * @function - * @param {array} fileNames + * @param {string[]} fileNames */ function printDragAndDropFileNames(fileNames) { - $dragAndDropFileNames.html(fileNames.join('
')); + $dragAndDropFileNames.empty(); + fileNames.forEach(fileName => { + const name = document.createTextNode(fileName); + $dragAndDropFileNames[0].appendChild(name); + $dragAndDropFileNames[0].appendChild(document.createElement('br')); + }); } /** @@ -3287,44 +3293,38 @@ jQuery.PrivateBin = (function($) { const alreadyIncludesCurrentAttachment = $targetElement.find(`[src='${blobUrl}']`).length > 0; if (blobUrl && !alreadyIncludesCurrentAttachment) { - if (mimeType.match(/^image\//i)) { - $targetElement.append( - $(document.createElement('img')) - .attr('src', blobUrl) - .attr('class', 'img-thumbnail') - ); - } else if (mimeType.match(/^video\//i)) { - $targetElement.append( - $(document.createElement('video')) - .attr('controls', 'true') - .attr('autoplay', 'true') - .attr('class', 'img-thumbnail') - - .append($(document.createElement('source')) - .attr('type', mimeType) - .attr('src', blobUrl)) - ); - } else if (mimeType.match(/^audio\//i)) { - $targetElement.append( - $(document.createElement('audio')) - .attr('controls', 'true') - .attr('autoplay', 'true') - - .append($(document.createElement('source')) - .attr('type', mimeType) - .attr('src', blobUrl)) - ); - } else if (mimeType.match(/\/pdf/i)) { + if (mimeType.toLowerCase().startsWith('image/')) { + const image = document.createElement('img'); + image.setAttribute('src', blobUrl); + image.setAttribute('class', 'img-thumbnail'); + $targetElement[0].appendChild(image); + } else if (mimeType.toLowerCase().startsWith('video/')) { + const video = document.createElement('video'); + video.setAttribute('controls', 'true'); + video.setAttribute('autoplay', 'true'); + video.setAttribute('class', 'img-thumbnail'); + const source = document.createElement('source'); + source.setAttribute('type', mimeType); + source.setAttribute('src', blobUrl); + video.appendChild(source); + $targetElement[0].appendChild(video); + } else if (mimeType.toLowerCase().startsWith('audio/')) { + const audio = document.createElement('audio'); + audio.setAttribute('controls', 'true'); + audio.setAttribute('autoplay', 'true'); + const source = document.createElement('source'); + source.setAttribute('type', mimeType); + source.setAttribute('src', blobUrl); + audio.appendChild(source); + $targetElement[0].appendChild(audio); + } else if (mimeType.toLowerCase().endsWith('/pdf')) { + const embed = document.createElement('embed'); + embed.setAttribute('src', blobUrl); + embed.setAttribute('type', 'application/pdf'); + embed.setAttribute('class', 'pdfPreview'); // Fallback for browsers, that don't support the vh unit - const clientHeight = $(window).height(); - - $targetElement.append( - $(document.createElement('embed')) - .attr('src', blobUrl) - .attr('type', 'application/pdf') - .attr('class', 'pdfPreview') - .css('height', clientHeight) - ); + embed.style.height = window.innerHeight + 'px'; + $targetElement[0].appendChild(embed); } } }; @@ -3399,19 +3399,21 @@ jQuery.PrivateBin = (function($) { * @function */ function addClipboardEventHandler() { - $('#message').on('paste', function (event) { + document.addEventListener('paste', (event) => { const items = (event.clipboardData || event.originalEvent.clipboardData).items; const files = [...items] .filter(item => item.kind === 'file') .map(item => item.getAsFile()); - if (TopNav.isAttachmentReadonly()) { + if (TopNav.isAttachmentReadonly() && files.length) { event.stopPropagation(); event.preventDefault(); return false; } - readFileData(files); + if (files.length) { + readFileData(files); + } }); } @@ -3603,8 +3605,10 @@ jQuery.PrivateBin = (function($) { if (nickname.length > 0) { $commentEntry.find('span.nickname').text(nickname); } else { - $commentEntry.find('span.nickname').html(''); - I18n._($commentEntry.find('span.nickname i'), 'Anonymous'); + const anonCommenter = document.createElement('em'); + anonCommenter.textContent = I18n._('Anonymous'); + $commentEntry.find('span.nickname')[0].innerHTML = ''; + $commentEntry.find('span.nickname')[0].appendChild(anonCommenter); } // set date @@ -3617,14 +3621,10 @@ jQuery.PrivateBin = (function($) { // if an avatar is available, display it const icon = comment.getIcon(); if (icon) { - $commentEntry.find('span.nickname') - .before( - ' ' - ); - $(document).on('languageLoaded', function () { - $commentEntry.find('img.vizhash') - .prop('title', I18n._('Avatar generated from IP address')); - }); + const image = document.createElement('img'); + image.setAttribute('src', icon); + image.setAttribute('class', 'vizhash'); + $commentEntry.find('span.nickname').prepend(' ').prepend(image); } // starting point (default value/fallback) @@ -4632,7 +4632,11 @@ jQuery.PrivateBin = (function($) { */ me.setFormat = function(format) { - $formatter.parent().find(`a[data-format="${format}"]`).click(); + if (Helper.isBootstrap5()) { + $formatter.find('select').val(format); + } else { + $formatter.parent().find(`a[data-format="${format}"]`).click(); + } } /** @@ -5221,22 +5225,23 @@ jQuery.PrivateBin = (function($) { cipherMessage['attachment'] = attachments.map(attachment => attachment[0]); cipherMessage['attachment_name'] = attachments.map(attachment => attachment[1]); - cipherMessage['attachment'] = await Promise.all(cipherMessage['attachment'].map(async (attachment) => { + cipherMessage['attachment'] = await Promise.all(cipherMessage['attachment'].map(async (attachment, i) => { // we need to retrieve data from blob if browser already parsed it in memory if (typeof attachment === 'string' && attachment.startsWith('blob:')) { Alert.showStatus( [ 'Retrieving cloned file \'%s\' from memory...', - attachment[1] + cipherMessage['attachment_name'][i] ], 'copy' ); try { const blobData = await $.ajax({ type: 'GET', - url: `${attachment}`, + url: attachment, processData: false, timeout: 10000, + dataType: 'binary', xhrFields: { withCredentials: false, responseType: 'blob' @@ -5418,6 +5423,10 @@ jQuery.PrivateBin = (function($) { plaintexts[i][1] ); } + $(document).on('languageLoaded', function () { + $('#commentcontainer').find('img.vizhash') + .prop('title', I18n._('Avatar generated from IP address')); + }); }); } @@ -5787,6 +5796,10 @@ jQuery.PrivateBin = (function($) { AttachmentViewer.removeAttachment(); TopNav.resetInput(); + // reset format + PasteViewer.setFormat('plaintext'); + TopNav.setFormat('plaintext'); + TopNav.showCreateButtons(); // newPaste could be called when user is on document clone editing view diff --git a/js/purify-3.2.7.js b/js/purify-3.2.7.js deleted file mode 100644 index 169dee48..00000000 --- a/js/purify-3.2.7.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! @license DOMPurify 3.2.7 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.2.7/LICENSE */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).DOMPurify=t()}(this,(function(){"use strict";const{entries:e,setPrototypeOf:t,isFrozen:n,getPrototypeOf:o,getOwnPropertyDescriptor:r}=Object;let{freeze:i,seal:a,create:l}=Object,{apply:c,construct:s}="undefined"!=typeof Reflect&&Reflect;i||(i=function(e){return e}),a||(a=function(e){return e}),c||(c=function(e,t){for(var n=arguments.length,o=new Array(n>2?n-2:0),r=2;r1?t-1:0),o=1;o1?n-1:0),r=1;r2&&void 0!==arguments[2]?arguments[2]:h;t&&t(e,null);let i=o.length;for(;i--;){let t=o[i];if("string"==typeof t){const e=r(t);e!==t&&(n(o)||(o[i]=e),t=e)}e[t]=!0}return e}function O(e){for(let t=0;t/gm),G=a(/\$\{[\w\W]*/gm),Y=a(/^data-[\-\w.\u00B7-\uFFFF]+$/),j=a(/^aria-[\-\w]+$/),X=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),q=a(/^(?:\w+script|data):/i),$=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),K=a(/^html$/i),V=a(/^[a-z][.\w]*(-[.\w]+)+$/i);var Z=Object.freeze({__proto__:null,ARIA_ATTR:j,ATTR_WHITESPACE:$,CUSTOM_ELEMENT:V,DATA_ATTR:Y,DOCTYPE_NAME:K,ERB_EXPR:W,IS_ALLOWED_URI:X,IS_SCRIPT_OR_DATA:q,MUSTACHE_EXPR:B,TMPLIT_EXPR:G});const J=1,Q=3,ee=7,te=8,ne=9,oe=function(){return"undefined"==typeof window?null:window};var re=function t(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:oe();const o=e=>t(e);if(o.version="3.2.7",o.removed=[],!n||!n.document||n.document.nodeType!==ne||!n.Element)return o.isSupported=!1,o;let{document:r}=n;const a=r,c=a.currentScript,{DocumentFragment:s,HTMLTemplateElement:N,Node:w,Element:O,NodeFilter:B,NamedNodeMap:W=n.NamedNodeMap||n.MozNamedAttrMap,HTMLFormElement:G,DOMParser:Y,trustedTypes:j}=n,q=O.prototype,$=D(q,"cloneNode"),V=D(q,"remove"),re=D(q,"nextSibling"),ie=D(q,"childNodes"),ae=D(q,"parentNode");if("function"==typeof N){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let le,ce="";const{implementation:se,createNodeIterator:ue,createDocumentFragment:me,getElementsByTagName:pe}=r,{importNode:fe}=a;let de={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]};o.isSupported="function"==typeof e&&"function"==typeof ae&&se&&void 0!==se.createHTMLDocument;const{MUSTACHE_EXPR:he,ERB_EXPR:ge,TMPLIT_EXPR:Te,DATA_ATTR:ye,ARIA_ATTR:Ee,IS_SCRIPT_OR_DATA:Ae,ATTR_WHITESPACE:_e,CUSTOM_ELEMENT:Se}=Z;let{IS_ALLOWED_URI:be}=Z,Ne=null;const we=R({},[...x,...L,...C,...I,...U]);let Re=null;const Oe=R({},[...z,...P,...H,...F]);let ve=Object.seal(l(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),De=null,xe=null,Le=!0,Ce=!0,ke=!1,Ie=!0,Me=!1,Ue=!0,ze=!1,Pe=!1,He=!1,Fe=!1,Be=!1,We=!1,Ge=!0,Ye=!1,je=!0,Xe=!1,qe={},$e=null;const Ke=R({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let Ve=null;const Ze=R({},["audio","video","img","source","image","track"]);let Je=null;const Qe=R({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),et="http://www.w3.org/1998/Math/MathML",tt="http://www.w3.org/2000/svg",nt="http://www.w3.org/1999/xhtml";let ot=nt,rt=!1,it=null;const at=R({},[et,tt,nt],g);let lt=R({},["mi","mo","mn","ms","mtext"]),ct=R({},["annotation-xml"]);const st=R({},["title","style","font","a","script"]);let ut=null;const mt=["application/xhtml+xml","text/html"];let pt=null,ft=null;const dt=r.createElement("form"),ht=function(e){return e instanceof RegExp||e instanceof Function},gt=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!ft||ft!==e){if(e&&"object"==typeof e||(e={}),e=v(e),ut=-1===mt.indexOf(e.PARSER_MEDIA_TYPE)?"text/html":e.PARSER_MEDIA_TYPE,pt="application/xhtml+xml"===ut?g:h,Ne=_(e,"ALLOWED_TAGS")?R({},e.ALLOWED_TAGS,pt):we,Re=_(e,"ALLOWED_ATTR")?R({},e.ALLOWED_ATTR,pt):Oe,it=_(e,"ALLOWED_NAMESPACES")?R({},e.ALLOWED_NAMESPACES,g):at,Je=_(e,"ADD_URI_SAFE_ATTR")?R(v(Qe),e.ADD_URI_SAFE_ATTR,pt):Qe,Ve=_(e,"ADD_DATA_URI_TAGS")?R(v(Ze),e.ADD_DATA_URI_TAGS,pt):Ze,$e=_(e,"FORBID_CONTENTS")?R({},e.FORBID_CONTENTS,pt):Ke,De=_(e,"FORBID_TAGS")?R({},e.FORBID_TAGS,pt):v({}),xe=_(e,"FORBID_ATTR")?R({},e.FORBID_ATTR,pt):v({}),qe=!!_(e,"USE_PROFILES")&&e.USE_PROFILES,Le=!1!==e.ALLOW_ARIA_ATTR,Ce=!1!==e.ALLOW_DATA_ATTR,ke=e.ALLOW_UNKNOWN_PROTOCOLS||!1,Ie=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,Me=e.SAFE_FOR_TEMPLATES||!1,Ue=!1!==e.SAFE_FOR_XML,ze=e.WHOLE_DOCUMENT||!1,Fe=e.RETURN_DOM||!1,Be=e.RETURN_DOM_FRAGMENT||!1,We=e.RETURN_TRUSTED_TYPE||!1,He=e.FORCE_BODY||!1,Ge=!1!==e.SANITIZE_DOM,Ye=e.SANITIZE_NAMED_PROPS||!1,je=!1!==e.KEEP_CONTENT,Xe=e.IN_PLACE||!1,be=e.ALLOWED_URI_REGEXP||X,ot=e.NAMESPACE||nt,lt=e.MATHML_TEXT_INTEGRATION_POINTS||lt,ct=e.HTML_INTEGRATION_POINTS||ct,ve=e.CUSTOM_ELEMENT_HANDLING||{},e.CUSTOM_ELEMENT_HANDLING&&ht(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(ve.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&&ht(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(ve.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(ve.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),Me&&(Ce=!1),Be&&(Fe=!0),qe&&(Ne=R({},U),Re=[],!0===qe.html&&(R(Ne,x),R(Re,z)),!0===qe.svg&&(R(Ne,L),R(Re,P),R(Re,F)),!0===qe.svgFilters&&(R(Ne,C),R(Re,P),R(Re,F)),!0===qe.mathMl&&(R(Ne,I),R(Re,H),R(Re,F))),e.ADD_TAGS&&(Ne===we&&(Ne=v(Ne)),R(Ne,e.ADD_TAGS,pt)),e.ADD_ATTR&&(Re===Oe&&(Re=v(Re)),R(Re,e.ADD_ATTR,pt)),e.ADD_URI_SAFE_ATTR&&R(Je,e.ADD_URI_SAFE_ATTR,pt),e.FORBID_CONTENTS&&($e===Ke&&($e=v($e)),R($e,e.FORBID_CONTENTS,pt)),je&&(Ne["#text"]=!0),ze&&R(Ne,["html","head","body"]),Ne.table&&(R(Ne,["tbody"]),delete De.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw b('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw b('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');le=e.TRUSTED_TYPES_POLICY,ce=le.createHTML("")}else void 0===le&&(le=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const o="data-tt-policy-suffix";t&&t.hasAttribute(o)&&(n=t.getAttribute(o));const r="dompurify"+(n?"#"+n:"");try{return e.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+r+" could not be created."),null}}(j,c)),null!==le&&"string"==typeof ce&&(ce=le.createHTML(""));i&&i(e),ft=e}},Tt=R({},[...L,...C,...k]),yt=R({},[...I,...M]),Et=function(e){f(o.removed,{element:e});try{ae(e).removeChild(e)}catch(t){V(e)}},At=function(e,t){try{f(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){f(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e)if(Fe||Be)try{Et(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},_t=function(e){let t=null,n=null;if(He)e=""+e;else{const t=T(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===ut&&ot===nt&&(e=''+e+"");const o=le?le.createHTML(e):e;if(ot===nt)try{t=(new Y).parseFromString(o,ut)}catch(e){}if(!t||!t.documentElement){t=se.createDocument(ot,"template",null);try{t.documentElement.innerHTML=rt?ce:o}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(r.createTextNode(n),i.childNodes[0]||null),ot===nt?pe.call(t,ze?"html":"body")[0]:ze?t.documentElement:i},St=function(e){return ue.call(e.ownerDocument||e,e,B.SHOW_ELEMENT|B.SHOW_COMMENT|B.SHOW_TEXT|B.SHOW_PROCESSING_INSTRUCTION|B.SHOW_CDATA_SECTION,null)},bt=function(e){return e instanceof G&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof W)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},Nt=function(e){return"function"==typeof w&&e instanceof w};function wt(e,t,n){u(e,(e=>{e.call(o,t,n,ft)}))}const Rt=function(e){let t=null;if(wt(de.beforeSanitizeElements,e,null),bt(e))return Et(e),!0;const n=pt(e.nodeName);if(wt(de.uponSanitizeElement,e,{tagName:n,allowedTags:Ne}),Ue&&e.hasChildNodes()&&!Nt(e.firstElementChild)&&S(/<[/\w!]/g,e.innerHTML)&&S(/<[/\w!]/g,e.textContent))return Et(e),!0;if(e.nodeType===ee)return Et(e),!0;if(Ue&&e.nodeType===te&&S(/<[/\w]/g,e.data))return Et(e),!0;if(!Ne[n]||De[n]){if(!De[n]&&vt(n)){if(ve.tagNameCheck instanceof RegExp&&S(ve.tagNameCheck,n))return!1;if(ve.tagNameCheck instanceof Function&&ve.tagNameCheck(n))return!1}if(je&&!$e[n]){const t=ae(e)||e.parentNode,n=ie(e)||e.childNodes;if(n&&t){for(let o=n.length-1;o>=0;--o){const r=$(n[o],!0);r.__removalCount=(e.__removalCount||0)+1,t.insertBefore(r,re(e))}}}return Et(e),!0}return e instanceof O&&!function(e){let t=ae(e);t&&t.tagName||(t={namespaceURI:ot,tagName:"template"});const n=h(e.tagName),o=h(t.tagName);return!!it[e.namespaceURI]&&(e.namespaceURI===tt?t.namespaceURI===nt?"svg"===n:t.namespaceURI===et?"svg"===n&&("annotation-xml"===o||lt[o]):Boolean(Tt[n]):e.namespaceURI===et?t.namespaceURI===nt?"math"===n:t.namespaceURI===tt?"math"===n&&ct[o]:Boolean(yt[n]):e.namespaceURI===nt?!(t.namespaceURI===tt&&!ct[o])&&!(t.namespaceURI===et&&!lt[o])&&!yt[n]&&(st[n]||!Tt[n]):!("application/xhtml+xml"!==ut||!it[e.namespaceURI]))}(e)?(Et(e),!0):"noscript"!==n&&"noembed"!==n&&"noframes"!==n||!S(/<\/no(script|embed|frames)/i,e.innerHTML)?(Me&&e.nodeType===Q&&(t=e.textContent,u([he,ge,Te],(e=>{t=y(t,e," ")})),e.textContent!==t&&(f(o.removed,{element:e.cloneNode()}),e.textContent=t)),wt(de.afterSanitizeElements,e,null),!1):(Et(e),!0)},Ot=function(e,t,n){if(Ge&&("id"===t||"name"===t)&&(n in r||n in dt))return!1;if(Ce&&!xe[t]&&S(ye,t));else if(Le&&S(Ee,t));else if(!Re[t]||xe[t]){if(!(vt(e)&&(ve.tagNameCheck instanceof RegExp&&S(ve.tagNameCheck,e)||ve.tagNameCheck instanceof Function&&ve.tagNameCheck(e))&&(ve.attributeNameCheck instanceof RegExp&&S(ve.attributeNameCheck,t)||ve.attributeNameCheck instanceof Function&&ve.attributeNameCheck(t,e))||"is"===t&&ve.allowCustomizedBuiltInElements&&(ve.tagNameCheck instanceof RegExp&&S(ve.tagNameCheck,n)||ve.tagNameCheck instanceof Function&&ve.tagNameCheck(n))))return!1}else if(Je[t]);else if(S(be,y(n,_e,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==E(n,"data:")||!Ve[e]){if(ke&&!S(Ae,y(n,_e,"")));else if(n)return!1}else;return!0},vt=function(e){return"annotation-xml"!==e&&T(e,Se)},Dt=function(e){wt(de.beforeSanitizeAttributes,e,null);const{attributes:t}=e;if(!t||bt(e))return;const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:Re,forceKeepAttr:void 0};let r=t.length;for(;r--;){const i=t[r],{name:a,namespaceURI:l,value:c}=i,s=pt(a),m=c;let f="value"===a?m:A(m);if(n.attrName=s,n.attrValue=f,n.keepAttr=!0,n.forceKeepAttr=void 0,wt(de.uponSanitizeAttribute,e,n),f=n.attrValue,!Ye||"id"!==s&&"name"!==s||(At(a,e),f="user-content-"+f),Ue&&S(/((--!?|])>)|<\/(style|title|textarea)/i,f)){At(a,e);continue}if("attributename"===s&&T(f,"href")){At(a,e);continue}if(n.forceKeepAttr)continue;if(!n.keepAttr){At(a,e);continue}if(!Ie&&S(/\/>/i,f)){At(a,e);continue}Me&&u([he,ge,Te],(e=>{f=y(f,e," ")}));const d=pt(e.nodeName);if(Ot(d,s,f)){if(le&&"object"==typeof j&&"function"==typeof j.getAttributeType)if(l);else switch(j.getAttributeType(d,s)){case"TrustedHTML":f=le.createHTML(f);break;case"TrustedScriptURL":f=le.createScriptURL(f)}if(f!==m)try{l?e.setAttributeNS(l,a,f):e.setAttribute(a,f),bt(e)?Et(e):p(o.removed)}catch(t){At(a,e)}}else At(a,e)}wt(de.afterSanitizeAttributes,e,null)},xt=function e(t){let n=null;const o=St(t);for(wt(de.beforeSanitizeShadowDOM,t,null);n=o.nextNode();)wt(de.uponSanitizeShadowNode,n,null),Rt(n),Dt(n),n.content instanceof s&&e(n.content);wt(de.afterSanitizeShadowDOM,t,null)};return o.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=null,r=null,i=null,l=null;if(rt=!e,rt&&(e="\x3c!--\x3e"),"string"!=typeof e&&!Nt(e)){if("function"!=typeof e.toString)throw b("toString is not a function");if("string"!=typeof(e=e.toString()))throw b("dirty is not a string, aborting")}if(!o.isSupported)return e;if(Pe||gt(t),o.removed=[],"string"==typeof e&&(Xe=!1),Xe){if(e.nodeName){const t=pt(e.nodeName);if(!Ne[t]||De[t])throw b("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof w)n=_t("\x3c!----\x3e"),r=n.ownerDocument.importNode(e,!0),r.nodeType===J&&"BODY"===r.nodeName||"HTML"===r.nodeName?n=r:n.appendChild(r);else{if(!Fe&&!Me&&!ze&&-1===e.indexOf("<"))return le&&We?le.createHTML(e):e;if(n=_t(e),!n)return Fe?null:We?ce:""}n&&He&&Et(n.firstChild);const c=St(Xe?e:n);for(;i=c.nextNode();)Rt(i),Dt(i),i.content instanceof s&&xt(i.content);if(Xe)return e;if(Fe){if(Be)for(l=me.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return(Re.shadowroot||Re.shadowrootmode)&&(l=fe.call(a,l,!0)),l}let m=ze?n.outerHTML:n.innerHTML;return ze&&Ne["!doctype"]&&n.ownerDocument&&n.ownerDocument.doctype&&n.ownerDocument.doctype.name&&S(K,n.ownerDocument.doctype.name)&&(m="\n"+m),Me&&u([he,ge,Te],(e=>{m=y(m,e," ")})),le&&We?le.createHTML(m):m},o.setConfig=function(){gt(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}),Pe=!0},o.clearConfig=function(){ft=null,Pe=!1},o.isValidAttribute=function(e,t,n){ft||gt({});const o=pt(e),r=pt(t);return Ot(o,r,n)},o.addHook=function(e,t){"function"==typeof t&&f(de[e],t)},o.removeHook=function(e,t){if(void 0!==t){const n=m(de[e],t);return-1===n?void 0:d(de[e],n,1)[0]}return p(de[e])},o.removeHooks=function(e){de[e]=[]},o.removeAllHooks=function(){de={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}},o}();return re})); diff --git a/js/purify-3.3.0.js b/js/purify-3.3.0.js new file mode 100644 index 00000000..07483124 --- /dev/null +++ b/js/purify-3.3.0.js @@ -0,0 +1,2 @@ +/*! @license DOMPurify 3.3.0 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.3.0/LICENSE */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).DOMPurify=t()}(this,(function(){"use strict";const{entries:e,setPrototypeOf:t,isFrozen:n,getPrototypeOf:o,getOwnPropertyDescriptor:r}=Object;let{freeze:i,seal:a,create:l}=Object,{apply:c,construct:s}="undefined"!=typeof Reflect&&Reflect;i||(i=function(e){return e}),a||(a=function(e){return e}),c||(c=function(e,t){for(var n=arguments.length,o=new Array(n>2?n-2:0),r=2;r1?t-1:0),o=1;o1?n-1:0),r=1;r2&&void 0!==arguments[2]?arguments[2]:h;t&&t(e,null);let i=o.length;for(;i--;){let t=o[i];if("string"==typeof t){const e=r(t);e!==t&&(n(o)||(o[i]=e),t=e)}e[t]=!0}return e}function D(e){for(let t=0;t/gm),W=a(/\$\{[\w\W]*/gm),Y=a(/^data-[\-\w.\u00B7-\uFFFF]+$/),j=a(/^aria-[\-\w]+$/),X=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),q=a(/^(?:\w+script|data):/i),$=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),K=a(/^html$/i),V=a(/^[a-z][.\w]*(-[.\w]+)+$/i);var Z=Object.freeze({__proto__:null,ARIA_ATTR:j,ATTR_WHITESPACE:$,CUSTOM_ELEMENT:V,DATA_ATTR:Y,DOCTYPE_NAME:K,ERB_EXPR:G,IS_ALLOWED_URI:X,IS_SCRIPT_OR_DATA:q,MUSTACHE_EXPR:B,TMPLIT_EXPR:W});const J=1,Q=3,ee=7,te=8,ne=9,oe=function(){return"undefined"==typeof window?null:window};var re=function t(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:oe();const o=e=>t(e);if(o.version="3.3.0",o.removed=[],!n||!n.document||n.document.nodeType!==ne||!n.Element)return o.isSupported=!1,o;let{document:r}=n;const a=r,c=a.currentScript,{DocumentFragment:s,HTMLTemplateElement:N,Node:w,Element:D,NodeFilter:B,NamedNodeMap:G=n.NamedNodeMap||n.MozNamedAttrMap,HTMLFormElement:W,DOMParser:Y,trustedTypes:j}=n,q=D.prototype,$=v(q,"cloneNode"),V=v(q,"remove"),re=v(q,"nextSibling"),ie=v(q,"childNodes"),ae=v(q,"parentNode");if("function"==typeof N){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let le,ce="";const{implementation:se,createNodeIterator:ue,createDocumentFragment:me,getElementsByTagName:pe}=r,{importNode:fe}=a;let de={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]};o.isSupported="function"==typeof e&&"function"==typeof ae&&se&&void 0!==se.createHTMLDocument;const{MUSTACHE_EXPR:he,ERB_EXPR:ge,TMPLIT_EXPR:Te,DATA_ATTR:ye,ARIA_ATTR:Ee,IS_SCRIPT_OR_DATA:Ae,ATTR_WHITESPACE:_e,CUSTOM_ELEMENT:be}=Z;let{IS_ALLOWED_URI:Se}=Z,Ne=null;const we=R({},[...O,...x,...L,...I,...U]);let Re=null;const De=R({},[...z,...P,...F,...H]);let Ce=Object.seal(l(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),ve=null,Oe=null;const xe=Object.seal(l(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let Le=!0,ke=!0,Ie=!1,Me=!0,Ue=!1,ze=!0,Pe=!1,Fe=!1,He=!1,Be=!1,Ge=!1,We=!1,Ye=!0,je=!1,Xe=!0,qe=!1,$e={},Ke=null;const Ve=R({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let Ze=null;const Je=R({},["audio","video","img","source","image","track"]);let Qe=null;const et=R({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),tt="http://www.w3.org/1998/Math/MathML",nt="http://www.w3.org/2000/svg",ot="http://www.w3.org/1999/xhtml";let rt=ot,it=!1,at=null;const lt=R({},[tt,nt,ot],g);let ct=R({},["mi","mo","mn","ms","mtext"]),st=R({},["annotation-xml"]);const ut=R({},["title","style","font","a","script"]);let mt=null;const pt=["application/xhtml+xml","text/html"];let ft=null,dt=null;const ht=r.createElement("form"),gt=function(e){return e instanceof RegExp||e instanceof Function},Tt=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(!dt||dt!==e){if(e&&"object"==typeof e||(e={}),e=C(e),mt=-1===pt.indexOf(e.PARSER_MEDIA_TYPE)?"text/html":e.PARSER_MEDIA_TYPE,ft="application/xhtml+xml"===mt?g:h,Ne=_(e,"ALLOWED_TAGS")?R({},e.ALLOWED_TAGS,ft):we,Re=_(e,"ALLOWED_ATTR")?R({},e.ALLOWED_ATTR,ft):De,at=_(e,"ALLOWED_NAMESPACES")?R({},e.ALLOWED_NAMESPACES,g):lt,Qe=_(e,"ADD_URI_SAFE_ATTR")?R(C(et),e.ADD_URI_SAFE_ATTR,ft):et,Ze=_(e,"ADD_DATA_URI_TAGS")?R(C(Je),e.ADD_DATA_URI_TAGS,ft):Je,Ke=_(e,"FORBID_CONTENTS")?R({},e.FORBID_CONTENTS,ft):Ve,ve=_(e,"FORBID_TAGS")?R({},e.FORBID_TAGS,ft):C({}),Oe=_(e,"FORBID_ATTR")?R({},e.FORBID_ATTR,ft):C({}),$e=!!_(e,"USE_PROFILES")&&e.USE_PROFILES,Le=!1!==e.ALLOW_ARIA_ATTR,ke=!1!==e.ALLOW_DATA_ATTR,Ie=e.ALLOW_UNKNOWN_PROTOCOLS||!1,Me=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,Ue=e.SAFE_FOR_TEMPLATES||!1,ze=!1!==e.SAFE_FOR_XML,Pe=e.WHOLE_DOCUMENT||!1,Be=e.RETURN_DOM||!1,Ge=e.RETURN_DOM_FRAGMENT||!1,We=e.RETURN_TRUSTED_TYPE||!1,He=e.FORCE_BODY||!1,Ye=!1!==e.SANITIZE_DOM,je=e.SANITIZE_NAMED_PROPS||!1,Xe=!1!==e.KEEP_CONTENT,qe=e.IN_PLACE||!1,Se=e.ALLOWED_URI_REGEXP||X,rt=e.NAMESPACE||ot,ct=e.MATHML_TEXT_INTEGRATION_POINTS||ct,st=e.HTML_INTEGRATION_POINTS||st,Ce=e.CUSTOM_ELEMENT_HANDLING||{},e.CUSTOM_ELEMENT_HANDLING&>(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(Ce.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&>(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(Ce.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(Ce.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),Ue&&(ke=!1),Ge&&(Be=!0),$e&&(Ne=R({},U),Re=[],!0===$e.html&&(R(Ne,O),R(Re,z)),!0===$e.svg&&(R(Ne,x),R(Re,P),R(Re,H)),!0===$e.svgFilters&&(R(Ne,L),R(Re,P),R(Re,H)),!0===$e.mathMl&&(R(Ne,I),R(Re,F),R(Re,H))),e.ADD_TAGS&&("function"==typeof e.ADD_TAGS?xe.tagCheck=e.ADD_TAGS:(Ne===we&&(Ne=C(Ne)),R(Ne,e.ADD_TAGS,ft))),e.ADD_ATTR&&("function"==typeof e.ADD_ATTR?xe.attributeCheck=e.ADD_ATTR:(Re===De&&(Re=C(Re)),R(Re,e.ADD_ATTR,ft))),e.ADD_URI_SAFE_ATTR&&R(Qe,e.ADD_URI_SAFE_ATTR,ft),e.FORBID_CONTENTS&&(Ke===Ve&&(Ke=C(Ke)),R(Ke,e.FORBID_CONTENTS,ft)),Xe&&(Ne["#text"]=!0),Pe&&R(Ne,["html","head","body"]),Ne.table&&(R(Ne,["tbody"]),delete ve.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw S('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw S('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');le=e.TRUSTED_TYPES_POLICY,ce=le.createHTML("")}else void 0===le&&(le=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const o="data-tt-policy-suffix";t&&t.hasAttribute(o)&&(n=t.getAttribute(o));const r="dompurify"+(n?"#"+n:"");try{return e.createPolicy(r,{createHTML:e=>e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+r+" could not be created."),null}}(j,c)),null!==le&&"string"==typeof ce&&(ce=le.createHTML(""));i&&i(e),dt=e}},yt=R({},[...x,...L,...k]),Et=R({},[...I,...M]),At=function(e){f(o.removed,{element:e});try{ae(e).removeChild(e)}catch(t){V(e)}},_t=function(e,t){try{f(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){f(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e)if(Be||Ge)try{At(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},bt=function(e){let t=null,n=null;if(He)e=""+e;else{const t=T(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===mt&&rt===ot&&(e=''+e+"");const o=le?le.createHTML(e):e;if(rt===ot)try{t=(new Y).parseFromString(o,mt)}catch(e){}if(!t||!t.documentElement){t=se.createDocument(rt,"template",null);try{t.documentElement.innerHTML=it?ce:o}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(r.createTextNode(n),i.childNodes[0]||null),rt===ot?pe.call(t,Pe?"html":"body")[0]:Pe?t.documentElement:i},St=function(e){return ue.call(e.ownerDocument||e,e,B.SHOW_ELEMENT|B.SHOW_COMMENT|B.SHOW_TEXT|B.SHOW_PROCESSING_INSTRUCTION|B.SHOW_CDATA_SECTION,null)},Nt=function(e){return e instanceof W&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof G)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},wt=function(e){return"function"==typeof w&&e instanceof w};function Rt(e,t,n){u(e,(e=>{e.call(o,t,n,dt)}))}const Dt=function(e){let t=null;if(Rt(de.beforeSanitizeElements,e,null),Nt(e))return At(e),!0;const n=ft(e.nodeName);if(Rt(de.uponSanitizeElement,e,{tagName:n,allowedTags:Ne}),ze&&e.hasChildNodes()&&!wt(e.firstElementChild)&&b(/<[/\w!]/g,e.innerHTML)&&b(/<[/\w!]/g,e.textContent))return At(e),!0;if(e.nodeType===ee)return At(e),!0;if(ze&&e.nodeType===te&&b(/<[/\w]/g,e.data))return At(e),!0;if(!(xe.tagCheck instanceof Function&&xe.tagCheck(n))&&(!Ne[n]||ve[n])){if(!ve[n]&&vt(n)){if(Ce.tagNameCheck instanceof RegExp&&b(Ce.tagNameCheck,n))return!1;if(Ce.tagNameCheck instanceof Function&&Ce.tagNameCheck(n))return!1}if(Xe&&!Ke[n]){const t=ae(e)||e.parentNode,n=ie(e)||e.childNodes;if(n&&t){for(let o=n.length-1;o>=0;--o){const r=$(n[o],!0);r.__removalCount=(e.__removalCount||0)+1,t.insertBefore(r,re(e))}}}return At(e),!0}return e instanceof D&&!function(e){let t=ae(e);t&&t.tagName||(t={namespaceURI:rt,tagName:"template"});const n=h(e.tagName),o=h(t.tagName);return!!at[e.namespaceURI]&&(e.namespaceURI===nt?t.namespaceURI===ot?"svg"===n:t.namespaceURI===tt?"svg"===n&&("annotation-xml"===o||ct[o]):Boolean(yt[n]):e.namespaceURI===tt?t.namespaceURI===ot?"math"===n:t.namespaceURI===nt?"math"===n&&st[o]:Boolean(Et[n]):e.namespaceURI===ot?!(t.namespaceURI===nt&&!st[o])&&!(t.namespaceURI===tt&&!ct[o])&&!Et[n]&&(ut[n]||!yt[n]):!("application/xhtml+xml"!==mt||!at[e.namespaceURI]))}(e)?(At(e),!0):"noscript"!==n&&"noembed"!==n&&"noframes"!==n||!b(/<\/no(script|embed|frames)/i,e.innerHTML)?(Ue&&e.nodeType===Q&&(t=e.textContent,u([he,ge,Te],(e=>{t=y(t,e," ")})),e.textContent!==t&&(f(o.removed,{element:e.cloneNode()}),e.textContent=t)),Rt(de.afterSanitizeElements,e,null),!1):(At(e),!0)},Ct=function(e,t,n){if(Ye&&("id"===t||"name"===t)&&(n in r||n in ht))return!1;if(ke&&!Oe[t]&&b(ye,t));else if(Le&&b(Ee,t));else if(xe.attributeCheck instanceof Function&&xe.attributeCheck(t,e));else if(!Re[t]||Oe[t]){if(!(vt(e)&&(Ce.tagNameCheck instanceof RegExp&&b(Ce.tagNameCheck,e)||Ce.tagNameCheck instanceof Function&&Ce.tagNameCheck(e))&&(Ce.attributeNameCheck instanceof RegExp&&b(Ce.attributeNameCheck,t)||Ce.attributeNameCheck instanceof Function&&Ce.attributeNameCheck(t,e))||"is"===t&&Ce.allowCustomizedBuiltInElements&&(Ce.tagNameCheck instanceof RegExp&&b(Ce.tagNameCheck,n)||Ce.tagNameCheck instanceof Function&&Ce.tagNameCheck(n))))return!1}else if(Qe[t]);else if(b(Se,y(n,_e,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==E(n,"data:")||!Ze[e]){if(Ie&&!b(Ae,y(n,_e,"")));else if(n)return!1}else;return!0},vt=function(e){return"annotation-xml"!==e&&T(e,be)},Ot=function(e){Rt(de.beforeSanitizeAttributes,e,null);const{attributes:t}=e;if(!t||Nt(e))return;const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:Re,forceKeepAttr:void 0};let r=t.length;for(;r--;){const i=t[r],{name:a,namespaceURI:l,value:c}=i,s=ft(a),m=c;let f="value"===a?m:A(m);if(n.attrName=s,n.attrValue=f,n.keepAttr=!0,n.forceKeepAttr=void 0,Rt(de.uponSanitizeAttribute,e,n),f=n.attrValue,!je||"id"!==s&&"name"!==s||(_t(a,e),f="user-content-"+f),ze&&b(/((--!?|])>)|<\/(style|title|textarea)/i,f)){_t(a,e);continue}if("attributename"===s&&T(f,"href")){_t(a,e);continue}if(n.forceKeepAttr)continue;if(!n.keepAttr){_t(a,e);continue}if(!Me&&b(/\/>/i,f)){_t(a,e);continue}Ue&&u([he,ge,Te],(e=>{f=y(f,e," ")}));const d=ft(e.nodeName);if(Ct(d,s,f)){if(le&&"object"==typeof j&&"function"==typeof j.getAttributeType)if(l);else switch(j.getAttributeType(d,s)){case"TrustedHTML":f=le.createHTML(f);break;case"TrustedScriptURL":f=le.createScriptURL(f)}if(f!==m)try{l?e.setAttributeNS(l,a,f):e.setAttribute(a,f),Nt(e)?At(e):p(o.removed)}catch(t){_t(a,e)}}else _t(a,e)}Rt(de.afterSanitizeAttributes,e,null)},xt=function e(t){let n=null;const o=St(t);for(Rt(de.beforeSanitizeShadowDOM,t,null);n=o.nextNode();)Rt(de.uponSanitizeShadowNode,n,null),Dt(n),Ot(n),n.content instanceof s&&e(n.content);Rt(de.afterSanitizeShadowDOM,t,null)};return o.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=null,r=null,i=null,l=null;if(it=!e,it&&(e="\x3c!--\x3e"),"string"!=typeof e&&!wt(e)){if("function"!=typeof e.toString)throw S("toString is not a function");if("string"!=typeof(e=e.toString()))throw S("dirty is not a string, aborting")}if(!o.isSupported)return e;if(Fe||Tt(t),o.removed=[],"string"==typeof e&&(qe=!1),qe){if(e.nodeName){const t=ft(e.nodeName);if(!Ne[t]||ve[t])throw S("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof w)n=bt("\x3c!----\x3e"),r=n.ownerDocument.importNode(e,!0),r.nodeType===J&&"BODY"===r.nodeName||"HTML"===r.nodeName?n=r:n.appendChild(r);else{if(!Be&&!Ue&&!Pe&&-1===e.indexOf("<"))return le&&We?le.createHTML(e):e;if(n=bt(e),!n)return Be?null:We?ce:""}n&&He&&At(n.firstChild);const c=St(qe?e:n);for(;i=c.nextNode();)Dt(i),Ot(i),i.content instanceof s&&xt(i.content);if(qe)return e;if(Be){if(Ge)for(l=me.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return(Re.shadowroot||Re.shadowrootmode)&&(l=fe.call(a,l,!0)),l}let m=Pe?n.outerHTML:n.innerHTML;return Pe&&Ne["!doctype"]&&n.ownerDocument&&n.ownerDocument.doctype&&n.ownerDocument.doctype.name&&b(K,n.ownerDocument.doctype.name)&&(m="\n"+m),Ue&&u([he,ge,Te],(e=>{m=y(m,e," ")})),le&&We?le.createHTML(m):m},o.setConfig=function(){Tt(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}),Fe=!0},o.clearConfig=function(){dt=null,Fe=!1},o.isValidAttribute=function(e,t,n){dt||Tt({});const o=ft(e),r=ft(t);return Ct(o,r,n)},o.addHook=function(e,t){"function"==typeof t&&f(de[e],t)},o.removeHook=function(e,t){if(void 0!==t){const n=m(de[e],t);return-1===n?void 0:d(de[e],n,1)[0]}return p(de[e])},o.removeHooks=function(e){de[e]=[]},o.removeAllHooks=function(){de={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}},o}();return re})); diff --git a/js/test/AttachmentViewer.js b/js/test/AttachmentViewer.js index 2a03ef54..04f9636e 100644 --- a/js/test/AttachmentViewer.js +++ b/js/test/AttachmentViewer.js @@ -129,5 +129,46 @@ describe('AttachmentViewer', function () { return results.every(element => element); } ); + + it( + 'sanitizes file names in attachments', + function() { + const clean = jsdom(); + $('body').html( + '' + + '' + + '
' + + '' + + '
' + ); + // mock createObjectURL for jsDOM + if (typeof window.URL.createObjectURL === 'undefined') { + Object.defineProperty( + window.URL, + 'createObjectURL', + {value: function(blob) { + return 'blob:' + location.origin + '/1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed'; + }} + ) + } + $.PrivateBin.AttachmentViewer.init(); + $.PrivateBin.Model.init(); + global.atob = common.atob; + + const maliciousFileNames = [ + '