From 3ede063e0a50a3f2436b9d3dbcd891203f0a686a Mon Sep 17 00:00:00 2001 From: rugk Date: Wed, 12 Nov 2025 20:42:05 +0100 Subject: [PATCH 01/89] Syncronize changelog with GitHub release doc (for CVE IDs) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aka adding the CVE ID's. BTW GitHub will make the CVEs clickable automatically when published. As for the GitHub's own ID well yeah… I just kept it synchronous now. (Maybe it's not _that_ relevant to mention all that IDs.) --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e95aec9..bf6852cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,14 +3,14 @@ ## 2.0.4 (not yet released) ## 2.0.3 (2025-11-12) -* FIXED: Prevent arbitrary PHP file inclusion when enabling template switching -* FIXED: Malicious filename can be used for self-XSS / HTML injection locally for users +* FIXED: Prevent arbitrary PHP file inclusion when enabling template switching (CVE-2025-64714) +* FIXED: Malicious filename can be used for self-XSS / HTML injection locally for users (CVE-2025-64711) * 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 +* FIXED: Sanitize file name in attachment size hint (CVE-2025-62796 / https://github.com/PrivateBin/PrivateBin/security/advisories/GHSA-867c-p784-5q6g) * FIXED: PHP OPcache module is optional again (#1679) * FIXED: bootstrap template password peek input group display From 4cdc6871e726faec4ae37fe48a84568ef2d5ad6d Mon Sep 17 00:00:00 2001 From: rugk Date: Thu, 13 Nov 2025 10:35:49 +0100 Subject: [PATCH 02/89] docs: use CVE website link Co-authored-by: El RIDO --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf6852cf..3dfaebad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ ## 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://github.com/PrivateBin/PrivateBin/security/advisories/GHSA-867c-p784-5q6g) +* 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 From 2c4dd2594cc47ca0b5cd719b963304ec48aecba2 Mon Sep 17 00:00:00 2001 From: rugk Date: Thu, 13 Nov 2025 10:52:08 +0000 Subject: [PATCH 03/89] fix: do not encode source JSON translation string resulting in wrong display of special characters like ' Fixes #1712 Disclosure: Coded with help of Copiot. (description wrtten by me) So this does indeed loosen the encoding a bit. However, IMHO, it was neither better before though. You could always bypass the encoding for `args{0]` when you just include `')) { - continue; - } - } elseif (is_int($args[$i])) { + for ($i = 1; $i < $argsCount; ++$i) { + if (is_int($args[$i])) { continue; } $args[$i] = self::encode($args[$i]); diff --git a/tst/I18nTest.php b/tst/I18nTest.php index 9e196103..118fe23b 100644 --- a/tst/I18nTest.php +++ b/tst/I18nTest.php @@ -182,7 +182,20 @@ class I18nTest extends TestCase $result = htmlspecialchars($input, ENT_QUOTES | ENT_HTML5 | ENT_DISALLOWED, 'UTF-8', false); $this->assertEquals($result, I18n::encode($input), 'encodes HTML entities'); $this->assertEquals('some ' . $result . ' + 1', I18n::_('some %s + %d', $input, 1), 'encodes parameters in translations'); - $this->assertEquals($result . $result, I18n::_($input . '%s', $input), 'encodes message ID as well, when no link'); + // Message ID should NOT be encoded (it comes from trusted source), only the parameter should be + $this->assertEquals($input . $result, I18n::_($input . '%s', $input), 'encodes only parameters, not message ID'); + } + + public function testFrenchApostropheInMessage() + { + $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'fr'; + I18n::loadTranslations(); + // The French translation should not have the apostrophe encoded + // Original: "Le document n'existe pas, a expiré, ou a été supprimé." + // Should NOT become: "Le document n'existe pas, a expiré, ou a été supprimé." + $message = I18n::_('Document does not exist, has expired or has been deleted.'); + $this->assertFalse(strpos($message, ''') !== false, 'French apostrophe should not be encoded in translation message'); + $this->assertTrue(strpos($message, "n'existe") !== false, 'French apostrophe should be present as literal character'); } public function testFallbackAlwaysPresent() From 38a722d2f5516085783583dee8f9c99e2ff2695a Mon Sep 17 00:00:00 2001 From: rugk Date: Thu, 13 Nov 2025 12:19:49 +0000 Subject: [PATCH 04/89] test: make sure to unset HTTP_ACCEPT_LANGUAGE at test teardown --- tst/I18nTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tst/I18nTest.php b/tst/I18nTest.php index 118fe23b..0374c897 100644 --- a/tst/I18nTest.php +++ b/tst/I18nTest.php @@ -37,6 +37,7 @@ class I18nTest extends TestCase public function tearDown(): void { unset($_COOKIE['lang'], $_SERVER['HTTP_ACCEPT_LANGUAGE']); + unset($_SERVER['HTTP_ACCEPT_LANGUAGE']); } public function testTranslationFallback() @@ -186,7 +187,7 @@ class I18nTest extends TestCase $this->assertEquals($input . $result, I18n::_($input . '%s', $input), 'encodes only parameters, not message ID'); } - public function testFrenchApostropheInMessage() + public function testApostropheEncodngInMessage() { $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'fr'; I18n::loadTranslations(); From e6762646168c69f32ea4ec1de5ec96d88f221935 Mon Sep 17 00:00:00 2001 From: rugk Date: Thu, 13 Nov 2025 12:28:03 +0000 Subject: [PATCH 05/89] test: make I18nTest actually reload English translations again --- tst/I18nTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/I18nTest.php b/tst/I18nTest.php index 0374c897..02d8cd3f 100644 --- a/tst/I18nTest.php +++ b/tst/I18nTest.php @@ -37,7 +37,7 @@ class I18nTest extends TestCase public function tearDown(): void { unset($_COOKIE['lang'], $_SERVER['HTTP_ACCEPT_LANGUAGE']); - unset($_SERVER['HTTP_ACCEPT_LANGUAGE']); + I18n::loadTranslations(); } public function testTranslationFallback() From 72d4c7aa2b91b64ae1c2af923535e5a6a02f5642 Mon Sep 17 00:00:00 2001 From: rugk Date: Thu, 13 Nov 2025 12:33:31 +0000 Subject: [PATCH 06/89] style: clarify comments --- tst/I18nTest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tst/I18nTest.php b/tst/I18nTest.php index 02d8cd3f..d707d724 100644 --- a/tst/I18nTest.php +++ b/tst/I18nTest.php @@ -191,9 +191,8 @@ class I18nTest extends TestCase { $_SERVER['HTTP_ACCEPT_LANGUAGE'] = 'fr'; I18n::loadTranslations(); - // The French translation should not have the apostrophe encoded - // Original: "Le document n'existe pas, a expiré, ou a été supprimé." - // Should NOT become: "Le document n'existe pas, a expiré, ou a été supprimé." + // For example, the French translation should not have the apostrophe encoded + // See https://github.com/PrivateBin/PrivateBin/issues/1712 $message = I18n::_('Document does not exist, has expired or has been deleted.'); $this->assertFalse(strpos($message, ''') !== false, 'French apostrophe should not be encoded in translation message'); $this->assertTrue(strpos($message, "n'existe") !== false, 'French apostrophe should be present as literal character'); From 318a37d352a36ebaa7d145e3bc6d5064862fd47e Mon Sep 17 00:00:00 2001 From: El RIDO Date: Thu, 13 Nov 2025 15:38:22 +0100 Subject: [PATCH 07/89] document changes - forward ported 1.7.9 release changes - linked all vulnerability reports - unified heading formats (dropped colons) --- CHANGELOG.md | 58 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dfaebad..6b7ad93c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,19 @@ ## 2.0.4 (not yet released) +## 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) -* FIXED: Malicious filename can be used for self-XSS / HTML injection locally for users (CVE-2025-64711) +* 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) @@ -87,7 +97,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 @@ -167,7 +177,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) @@ -219,12 +229,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) @@ -268,7 +278,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) @@ -287,7 +297,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 @@ -328,7 +338,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 @@ -344,13 +354,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 @@ -368,7 +378,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 @@ -379,11 +389,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. @@ -397,32 +407,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. @@ -430,17 +440,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.) From d78c33438d3daabecd48e9509c03b28ad8211e9a Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 14 Nov 2025 10:04:41 +0100 Subject: [PATCH 08/89] refactor JSON response processing - avoid translating JSON error messages twice - separation of concerns, JSON response preparation should not mix handling errors and results, provide two functions instead of one - callers of JSON error method are responsible for translation of errors --- CHANGELOG.md | 1 + lib/Controller.php | 84 +++++++++++++++++++++++++++------------------- 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dfaebad..ee0b59dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # PrivateBin version history ## 2.0.4 (not yet released) +* CHANGED: Deduplicate JSON error message translations. ## 2.0.3 (2025-11-12) * FIXED: Prevent arbitrary PHP file inclusion when enabling template switching (CVE-2025-64714) diff --git a/lib/Controller.php b/lib/Controller.php index b4a12706..def5fae2 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -271,7 +271,8 @@ class Controller try { TrafficLimiter::canPass(); } catch (Exception $e) { - $this->_return_message(1, $e->getMessage()); + // traffic limiter exceptions come translated + $this->_json_error($e->getMessage()); return; } @@ -281,14 +282,13 @@ class Controller array_key_exists('parentid', $data) && !empty($data['parentid']); if (!FormatV2::isValid($data, $isComment)) { - $this->_return_message(1, I18n::_('Invalid data.')); + $this->_json_error(I18n::_('Invalid data.')); return; } $sizelimit = $this->_conf->getKey('sizelimit'); // Ensure content is not too big. if (strlen($data['ct']) > $sizelimit) { - $this->_return_message( - 1, + $this->_json_error( I18n::_( 'Document is limited to %s of encrypted data.', Filter::formatHumanReadableSize($sizelimit) @@ -306,12 +306,13 @@ class Controller $comment->setData($data); $comment->store(); } catch (Exception $e) { - $this->_return_message(1, $e->getMessage()); + // comment exceptions need translation + $this->_json_error(I18n::_($e->getMessage())); return; } - $this->_return_message(0, $comment->getId()); + $this->_json_result($comment->getId()); } else { - $this->_return_message(1, I18n::_('Invalid data.')); + $this->_json_error(I18n::_('Invalid data.')); } } // The user posts a standard paste. @@ -329,10 +330,11 @@ class Controller $paste->setData($data); $paste->store(); } catch (Exception $e) { - $this->_return_message(1, $e->getMessage()); + // paste exceptions need translation + $this->_json_error(I18n::_($e->getMessage())); return; } - $this->_return_message(0, $paste->getId(), array('deletetoken' => $paste->getDeleteToken())); + $this->_json_result($paste->getId(), array('deletetoken' => $paste->getDeleteToken())); } } @@ -367,9 +369,9 @@ class Controller } if ($this->_request->isJsonApiCall()) { if (empty($this->_error)) { - $this->_return_message(0, $dataid); + $this->_json_result($dataid); } else { - $this->_return_message(1, $this->_error); + $this->_json_error(I18n::_($this->_error)); } } } @@ -393,12 +395,13 @@ class Controller if (array_key_exists('salt', $data['meta'])) { unset($data['meta']['salt']); } - $this->_return_message(0, $dataid, (array) $data); + $this->_json_result($dataid, (array) $data); } else { - $this->_return_message(1, self::GENERIC_ERROR); + $this->_json_error(I18n::_(self::GENERIC_ERROR)); } } catch (Exception $e) { - $this->_return_message(1, $e->getMessage()); + // paste exceptions need translation + $this->_json_error(I18n::_($e->getMessage())); } } @@ -537,6 +540,38 @@ class Controller echo $content; } + /** + * prepares JSON encoded error message + * + * @access private + * @param string $error + */ + private function _json_error($error) + { + $result = array( + 'status' => 1, + 'message' => $error + ); + $this->_json = Json::encode($result); + } + + /** + * prepares JSON encoded result message + * + * @access private + * @param string $dataid + * @param array $other + */ + private function _json_result($dataid, $other = array()) + { + $result = array( + 'status' => 0, + 'id' => $dataid, + 'url' => $this->_urlBase . '?' . $dataid + ) + $other; + $this->_json = Json::encode($result); + } + /** * Proxies a link using the specified proxy class, and updates the status or error with the response. * @@ -551,25 +586,4 @@ class Controller $this->_status = $proxy->getUrl(); } } - - /** - * prepares JSON encoded status message - * - * @access private - * @param int $status - * @param string $message - * @param array $other - */ - private function _return_message($status, $message, $other = array()) - { - $result = array('status' => $status); - if ($status) { - $result['message'] = I18n::_($message); - } else { - $result['id'] = $message; - $result['url'] = $this->_urlBase . '?' . $message; - } - $result += $other; - $this->_json = Json::encode($result); - } } From e26bcfa733c3ed7d1efc183bc1699137f8165a6b Mon Sep 17 00:00:00 2001 From: El RIDO Date: Fri, 14 Nov 2025 10:21:57 +0100 Subject: [PATCH 09/89] apply StyleCI recommendation --- lib/Controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Controller.php b/lib/Controller.php index def5fae2..bb161907 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -550,7 +550,7 @@ class Controller { $result = array( 'status' => 1, - 'message' => $error + 'message' => $error, ); $this->_json = Json::encode($result); } @@ -567,7 +567,7 @@ class Controller $result = array( 'status' => 0, 'id' => $dataid, - 'url' => $this->_urlBase . '?' . $dataid + 'url' => $this->_urlBase . '?' . $dataid, ) + $other; $this->_json = Json::encode($result); } From f4f655966cd2b82fd945fb27a4360b4b74316067 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Nov 2025 10:37:12 +0000 Subject: [PATCH 10/89] Bump js-yaml from 4.1.0 to 4.1.1 in /js Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.1.0 to 4.1.1. - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](https://github.com/nodeca/js-yaml/compare/4.1.0...4.1.1) --- updated-dependencies: - dependency-name: js-yaml dependency-version: 4.1.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- js/package-lock.json | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) 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" From 3e6f1733f95c423fd51bb9113b31de1e01050f60 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Mon, 17 Nov 2025 17:28:13 +0100 Subject: [PATCH 11/89] refactored exceptions in controller - added missing exception doc blocks - introduced exception type that translates message during construction - catch explicit exception types where possible --- lib/Configuration.php | 11 +++++--- lib/Controller.php | 25 ++++++++++--------- lib/Model/AbstractModel.php | 26 ++++++++++++++----- lib/Model/Comment.php | 17 ++++++------- lib/Model/Paste.php | 28 ++++++++++----------- lib/Persistence/TrafficLimiter.php | 9 +++---- lib/TranslatedException.php | 36 +++++++++++++++++++++++++++ vendor/composer/autoload_classmap.php | 1 + vendor/composer/autoload_static.php | 1 + vendor/composer/installed.php | 4 +-- 10 files changed, 105 insertions(+), 53 deletions(-) create mode 100644 lib/TranslatedException.php diff --git a/lib/Configuration.php b/lib/Configuration.php index 2cccc342..c4651f8b 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -12,6 +12,7 @@ namespace PrivateBin; use Exception; +use PrivateBin\TranslatedException; /** * Configuration @@ -131,7 +132,7 @@ class Configuration /** * parse configuration file and ensure default configuration values are present * - * @throws Exception + * @throws TranslatedException */ public function __construct() { @@ -148,7 +149,9 @@ class Configuration $config = parse_ini_file($configFile, true); foreach (array('main', 'model', 'model_options') as $section) { if (!array_key_exists($section, $config)) { - throw new Exception(I18n::_('PrivateBin requires configuration section [%s] to be present in configuration file.', $section), 2); + $name = array_key_exists('main', $config) && array_key_exists('name', $config['main']) ? + $config['main']['name'] : self::getDefaults()['main']['name']; + throw new TranslatedException(array('%s requires configuration section [%s] to be present in configuration file.', I18n::_($name), $section), 2); } } break; @@ -304,13 +307,13 @@ class Configuration * get a section from the configuration, must exist * * @param string $section - * @throws Exception + * @throws TranslatedException * @return mixed */ public function getSection($section) { if (!array_key_exists($section, $this->_configuration)) { - throw new Exception(I18n::_('%s requires configuration section [%s] to be present in configuration file.', I18n::_($this->getKey('name')), $section), 3); + throw new TranslatedException(array('%s requires configuration section [%s] to be present in configuration file.', I18n::_($this->getKey('name')), $section), 3); } return $this->_configuration[$section]; } diff --git a/lib/Controller.php b/lib/Controller.php index bb161907..2e056db6 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -17,6 +17,7 @@ use PrivateBin\Persistence\TrafficLimiter; use PrivateBin\Proxy\AbstractProxy; use PrivateBin\Proxy\ShlinkProxy; use PrivateBin\Proxy\YourlsProxy; +use PrivateBin\TranslatedException; /** * Controller @@ -195,6 +196,7 @@ class Controller * Set default language * * @access private + * @throws Exception */ private function _setDefaultLanguage() { @@ -211,6 +213,7 @@ class Controller * Set default template * * @access private + * @throws Exception */ private function _setDefaultTemplate() { @@ -260,6 +263,7 @@ class Controller * pasteid (optional) = in discussions, which paste this comment belongs to. * * @access private + * @throws Exception * @return string */ private function _create() @@ -270,8 +274,7 @@ class Controller TrafficLimiter::setStore($this->_model->getStore()); try { TrafficLimiter::canPass(); - } catch (Exception $e) { - // traffic limiter exceptions come translated + } catch (TranslatedException $e) { $this->_json_error($e->getMessage()); return; } @@ -305,9 +308,8 @@ class Controller $comment = $paste->getComment($data['parentid']); $comment->setData($data); $comment->store(); - } catch (Exception $e) { - // comment exceptions need translation - $this->_json_error(I18n::_($e->getMessage())); + } catch (TranslatedException $e) { + $this->_json_error($e->getMessage()); return; } $this->_json_result($comment->getId()); @@ -319,7 +321,7 @@ class Controller else { try { $this->_model->purge(); - } catch (Exception $e) { + } catch (Exception $e) { // JSON error!!! error_log('Error purging documents: ' . $e->getMessage() . PHP_EOL . 'Use the administration scripts statistics to find ' . 'damaged paste IDs and either delete them or restore them ' . @@ -329,9 +331,8 @@ class Controller try { $paste->setData($data); $paste->store(); - } catch (Exception $e) { - // paste exceptions need translation - $this->_json_error(I18n::_($e->getMessage())); + } catch (TranslatedException $e) { + $this->_json_error($e->getMessage()); return; } $this->_json_result($paste->getId(), array('deletetoken' => $paste->getDeleteToken())); @@ -399,9 +400,8 @@ class Controller } else { $this->_json_error(I18n::_(self::GENERIC_ERROR)); } - } catch (Exception $e) { - // paste exceptions need translation - $this->_json_error(I18n::_($e->getMessage())); + } catch (TranslatedException $e) { + $this->_json_error($e->getMessage()); } } @@ -409,6 +409,7 @@ class Controller * Display frontend. * * @access private + * @throws Exception */ private function _view() { diff --git a/lib/Model/AbstractModel.php b/lib/Model/AbstractModel.php index d7b5102f..5ff4ab1b 100644 --- a/lib/Model/AbstractModel.php +++ b/lib/Model/AbstractModel.php @@ -11,9 +11,9 @@ namespace PrivateBin\Model; -use Exception; use PrivateBin\Configuration; use PrivateBin\Data\AbstractData; +use PrivateBin\TranslatedException; /** * AbstractModel @@ -22,6 +22,20 @@ use PrivateBin\Data\AbstractData; */ abstract class AbstractModel { + /** + * show the same error message if the data is invalid + * + * @const string + */ + const INVALID_DATA_ERROR = 'Invalid data.'; + + /** + * show the same error message if the document ID already exists + * + * @const string + */ + const COLLISION_ERROR = 'You are unlucky. Try again.'; + /** * Instance ID. * @@ -83,12 +97,12 @@ abstract class AbstractModel * * @access public * @param string $id - * @throws Exception + * @throws TranslatedException */ public function setId($id) { if (!self::isValidId($id)) { - throw new Exception('Invalid document ID.', 60); + throw new TranslatedException('Invalid document ID.', 60); } $this->_id = $id; } @@ -98,7 +112,7 @@ abstract class AbstractModel * * @access public * @param array $data - * @throws Exception + * @throws TranslatedException */ public function setData(array &$data) { @@ -125,7 +139,7 @@ abstract class AbstractModel * Store the instance's data. * * @access public - * @throws Exception + * @throws TranslatedException */ abstract public function store(); @@ -163,7 +177,7 @@ abstract class AbstractModel * * @access protected * @param array $data - * @throws Exception + * @throws TranslatedException */ protected function _validate(array &$data) { diff --git a/lib/Model/Comment.php b/lib/Model/Comment.php index 3c0b2b20..3a9d6336 100644 --- a/lib/Model/Comment.php +++ b/lib/Model/Comment.php @@ -11,10 +11,10 @@ namespace PrivateBin\Model; -use Exception; use Identicon\Identicon; use Jdenticon\Identicon as Jdenticon; use PrivateBin\Persistence\TrafficLimiter; +use PrivateBin\TranslatedException; use PrivateBin\Vizhash16x16; /** @@ -36,24 +36,24 @@ class Comment extends AbstractModel * Store the comment's data. * * @access public - * @throws Exception + * @throws TranslatedException */ public function store() { // Make sure paste exists. $pasteid = $this->getPaste()->getId(); if (!$this->getPaste()->exists()) { - throw new Exception('Invalid data.', 67); + throw new TranslatedException(self::INVALID_DATA_ERROR, 67); } // Make sure the discussion is opened in this paste and allowed in the configuration. if (!$this->getPaste()->isOpendiscussion() || !$this->_conf->getKey('discussion')) { - throw new Exception('Invalid data.', 68); + throw new TranslatedException(self::INVALID_DATA_ERROR, 68); } // Check for improbable collision. if ($this->exists()) { - throw new Exception('You are unlucky. Try again.', 69); + throw new TranslatedException(self::COLLISION_ERROR, 69); } $this->_data['meta']['created'] = time(); @@ -67,7 +67,7 @@ class Comment extends AbstractModel $this->_data ) === false ) { - throw new Exception('Error saving comment. Sorry.', 70); + throw new TranslatedException('Error saving comment. Sorry.', 70); } } @@ -91,7 +91,6 @@ class Comment extends AbstractModel * * @access public * @param Paste $paste - * @throws Exception */ public function setPaste(Paste &$paste) { @@ -115,12 +114,12 @@ class Comment extends AbstractModel * * @access public * @param string $id - * @throws Exception + * @throws TranslatedException */ public function setParentId($id) { if (!self::isValidId($id)) { - throw new Exception('Invalid document ID.', 65); + throw new TranslatedException('Invalid document ID.', 65); } $this->_data['parentid'] = $id; } diff --git a/lib/Model/Paste.php b/lib/Model/Paste.php index a42ab35e..0d52b2a3 100644 --- a/lib/Model/Paste.php +++ b/lib/Model/Paste.php @@ -11,9 +11,9 @@ namespace PrivateBin\Model; -use Exception; use PrivateBin\Controller; use PrivateBin\Persistence\ServerSalt; +use PrivateBin\TranslatedException; /** * Paste @@ -47,14 +47,14 @@ class Paste extends AbstractModel * Get paste data. * * @access public - * @throws Exception + * @throws TranslatedException * @return array */ public function get() { $data = $this->_store->read($this->getId()); if ($data === false) { - throw new Exception(Controller::GENERIC_ERROR, 64); + throw new TranslatedException(Controller::GENERIC_ERROR, 64); } // check if paste has expired and delete it if necessary. @@ -62,7 +62,7 @@ class Paste extends AbstractModel $now = time(); if ($data['meta']['expire_date'] < $now) { $this->delete(); - throw new Exception(Controller::GENERIC_ERROR, 63); + throw new TranslatedException(Controller::GENERIC_ERROR, 63); } // We kindly provide the remaining time before expiration (in seconds) $data['meta']['time_to_live'] = $data['meta']['expire_date'] - $now; @@ -93,13 +93,13 @@ class Paste extends AbstractModel * Store the paste's data. * * @access public - * @throws Exception + * @throws TranslatedException */ public function store() { // Check for improbable collision. if ($this->exists()) { - throw new Exception('You are unlucky. Try again.', 75); + throw new TranslatedException(self::COLLISION_ERROR, 75); } $this->_data['meta']['salt'] = ServerSalt::generate(); @@ -111,7 +111,7 @@ class Paste extends AbstractModel $this->_data ) === false ) { - throw new Exception('Error saving document. Sorry.', 76); + throw new TranslatedException('Error saving document. Sorry.', 76); } } @@ -119,7 +119,6 @@ class Paste extends AbstractModel * Delete the paste. * * @access public - * @throws Exception */ public function delete() { @@ -143,13 +142,13 @@ class Paste extends AbstractModel * @access public * @param string $parentId * @param string $commentId - * @throws Exception + * @throws TranslatedException * @return Comment */ public function getComment($parentId, $commentId = '') { if (!$this->exists()) { - throw new Exception('Invalid data.', 62); + throw new TranslatedException(self::INVALID_DATA_ERROR, 62); } $comment = new Comment($this->_conf, $this->_store); $comment->setPaste($this); @@ -201,7 +200,6 @@ class Paste extends AbstractModel * Check if paste has discussions enabled. * * @access public - * @throws Exception * @return bool */ public function isOpendiscussion() @@ -240,13 +238,13 @@ class Paste extends AbstractModel * * @access protected * @param array $data - * @throws Exception + * @throws TranslatedException */ protected function _validate(array &$data) { // reject invalid or disabled formatters if (!array_key_exists($data['adata'][self::ADATA_FORMATTER], $this->_conf->getSection('formatter_options'))) { - throw new Exception('Invalid data.', 75); + throw new TranslatedException(self::INVALID_DATA_ERROR, 75); } // discussion requested, but disabled in config or burn after reading requested as well, or invalid integer @@ -257,7 +255,7 @@ class Paste extends AbstractModel )) || ($data['adata'][self::ADATA_OPEN_DISCUSSION] !== 0 && $data['adata'][self::ADATA_OPEN_DISCUSSION] !== 1) ) { - throw new Exception('Invalid data.', 74); + throw new TranslatedException(self::INVALID_DATA_ERROR, 74); } // reject invalid burn after reading @@ -265,7 +263,7 @@ class Paste extends AbstractModel $data['adata'][self::ADATA_BURN_AFTER_READING] !== 0 && $data['adata'][self::ADATA_BURN_AFTER_READING] !== 1 ) { - throw new Exception('Invalid data.', 73); + throw new TranslatedException(self::INVALID_DATA_ERROR, 73); } } } diff --git a/lib/Persistence/TrafficLimiter.php b/lib/Persistence/TrafficLimiter.php index 60977f5d..c94bfdb2 100644 --- a/lib/Persistence/TrafficLimiter.php +++ b/lib/Persistence/TrafficLimiter.php @@ -12,11 +12,10 @@ namespace PrivateBin\Persistence; -use Exception; use IPLib\Factory; use IPLib\ParseStringFlag; use PrivateBin\Configuration; -use PrivateBin\I18n; +use PrivateBin\TranslatedException; /** * TrafficLimiter @@ -167,7 +166,7 @@ class TrafficLimiter extends AbstractPersistence * * @access public * @static - * @throws Exception + * @throws TranslatedException * @return true */ public static function canPass() @@ -181,7 +180,7 @@ class TrafficLimiter extends AbstractPersistence return true; } } - throw new Exception(I18n::_('Your IP is not authorized to create documents.')); + throw new TranslatedException('Your IP is not authorized to create documents.'); } // disable limits if set to less then 1 @@ -210,7 +209,7 @@ class TrafficLimiter extends AbstractPersistence } return true; } - throw new Exception(I18n::_( + throw new TranslatedException(array( 'Please wait %d seconds between each post.', self::$_limit )); diff --git a/lib/TranslatedException.php b/lib/TranslatedException.php new file mode 100644 index 00000000..bbadec43 --- /dev/null +++ b/lib/TranslatedException.php @@ -0,0 +1,36 @@ + $baseDir . '/lib/Proxy/YourlsProxy.php', 'PrivateBin\\Request' => $baseDir . '/lib/Request.php', 'PrivateBin\\TemplateSwitcher' => $baseDir . '/lib/TemplateSwitcher.php', + 'PrivateBin\\TranslatedException' => $baseDir . '/lib/TranslatedException.php', 'PrivateBin\\View' => $baseDir . '/lib/View.php', 'PrivateBin\\Vizhash16x16' => $baseDir . '/lib/Vizhash16x16.php', 'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index e9194d01..aa7dd9e8 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -139,6 +139,7 @@ class ComposerStaticInitDontChange 'PrivateBin\\Proxy\\YourlsProxy' => __DIR__ . '/../..' . '/lib/Proxy/YourlsProxy.php', 'PrivateBin\\Request' => __DIR__ . '/../..' . '/lib/Request.php', 'PrivateBin\\TemplateSwitcher' => __DIR__ . '/../..' . '/lib/TemplateSwitcher.php', + 'PrivateBin\\TranslatedException' => __DIR__ . '/../..' . '/lib/TranslatedException.php', 'PrivateBin\\View' => __DIR__ . '/../..' . '/lib/View.php', 'PrivateBin\\Vizhash16x16' => __DIR__ . '/../..' . '/lib/Vizhash16x16.php', 'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php', diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 56dccd55..c9befe93 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'privatebin/privatebin', 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '06496a1b0e975b79c5a7abc0bd54b492ca264640', + 'reference' => 'a051c4bd6b71fd56f0d7fc235e815dafc8eb54ea', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -31,7 +31,7 @@ 'privatebin/privatebin' => array( 'pretty_version' => 'dev-master', 'version' => 'dev-master', - 'reference' => '06496a1b0e975b79c5a7abc0bd54b492ca264640', + 'reference' => 'a051c4bd6b71fd56f0d7fc235e815dafc8eb54ea', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), From 3a23117ebfcfaf6fddfda82f73789df89e2e6adf Mon Sep 17 00:00:00 2001 From: El RIDO Date: Wed, 19 Nov 2025 09:36:08 +0100 Subject: [PATCH 12/89] Refactored translation of exception messages --- CHANGELOG.md | 4 +- composer.lock | 24 ++++----- lib/Configuration.php | 2 +- lib/Controller.php | 28 +++++------ lib/Data/Database.php | 55 ++++++++++++++------- lib/Data/Filesystem.php | 20 ++++---- lib/Data/GoogleCloudStorage.php | 8 ++- lib/Data/S3Storage.php | 11 +++-- lib/Exception/JsonException.php | 37 ++++++++++++++ lib/{ => Exception}/TranslatedException.php | 2 +- lib/I18n.php | 2 + lib/Json.php | 21 +++----- lib/Model/AbstractModel.php | 2 +- lib/Model/Comment.php | 2 +- lib/Model/Paste.php | 2 +- lib/Persistence/TrafficLimiter.php | 2 +- lib/Proxy/AbstractProxy.php | 4 +- lib/Proxy/ShlinkProxy.php | 18 ++++--- lib/Request.php | 4 +- tpl/bootstrap.php | 19 ++++--- tpl/bootstrap5.php | 19 ++++--- vendor/composer/autoload_classmap.php | 3 +- vendor/composer/autoload_static.php | 3 +- vendor/composer/installed.php | 4 +- 24 files changed, 186 insertions(+), 110 deletions(-) create mode 100644 lib/Exception/JsonException.php rename lib/{ => Exception}/TranslatedException.php (96%) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7977c0d..8002155b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # PrivateBin version history ## 2.0.4 (not yet released) -* CHANGED: Deduplicate JSON error message translations. +* CHANGED: Deduplicate JSON error message translations +* CHANGED: Refactored translation of exception messages +* FIXED: Some exceptions not getting translated ## 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 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/lib/Configuration.php b/lib/Configuration.php index c4651f8b..7836c3ec 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -12,7 +12,7 @@ namespace PrivateBin; use Exception; -use PrivateBin\TranslatedException; +use PrivateBin\Exception\TranslatedException; /** * Configuration diff --git a/lib/Controller.php b/lib/Controller.php index 2e056db6..056647de 100644 --- a/lib/Controller.php +++ b/lib/Controller.php @@ -12,12 +12,13 @@ namespace PrivateBin; use Exception; +use PrivateBin\Exception\JsonException; +use PrivateBin\Exception\TranslatedException; use PrivateBin\Persistence\ServerSalt; use PrivateBin\Persistence\TrafficLimiter; use PrivateBin\Proxy\AbstractProxy; use PrivateBin\Proxy\ShlinkProxy; use PrivateBin\Proxy\YourlsProxy; -use PrivateBin\TranslatedException; /** * Controller @@ -308,11 +309,10 @@ class Controller $comment = $paste->getComment($data['parentid']); $comment->setData($data); $comment->store(); - } catch (TranslatedException $e) { + $this->_json_result($comment->getId()); + } catch (Exception $e) { $this->_json_error($e->getMessage()); - return; } - $this->_json_result($comment->getId()); } else { $this->_json_error(I18n::_('Invalid data.')); } @@ -321,21 +321,13 @@ class Controller else { try { $this->_model->purge(); - } catch (Exception $e) { // JSON error!!! - error_log('Error purging documents: ' . $e->getMessage() . PHP_EOL . - 'Use the administration scripts statistics to find ' . - 'damaged paste IDs and either delete them or restore them ' . - 'from backup.'); - } - $paste = $this->_model->getPaste(); - try { + $paste = $this->_model->getPaste(); $paste->setData($data); $paste->store(); - } catch (TranslatedException $e) { + $this->_json_result($paste->getId(), array('deletetoken' => $paste->getDeleteToken())); + } catch (Exception $e) { $this->_json_error($e->getMessage()); - return; } - $this->_json_result($paste->getId(), array('deletetoken' => $paste->getDeleteToken())); } } @@ -365,7 +357,7 @@ class Controller } else { $this->_error = self::GENERIC_ERROR; } - } catch (Exception $e) { + } catch (TranslatedException $e) { $this->_error = $e->getMessage(); } if ($this->_request->isJsonApiCall()) { @@ -470,7 +462,7 @@ class Controller } $page->assign('BASEPATH', I18n::_($this->_conf->getKey('basepath'))); $page->assign('STATUS', I18n::_($this->_status)); - $page->assign('ISDELETED', I18n::_(json_encode($this->_is_deleted))); + $page->assign('ISDELETED', $this->_is_deleted); $page->assign('VERSION', self::VERSION); $page->assign('DISCUSSION', $this->_conf->getKey('discussion')); $page->assign('OPENDISCUSSION', $this->_conf->getKey('opendiscussion')); @@ -546,6 +538,7 @@ class Controller * * @access private * @param string $error + * @throws JsonException */ private function _json_error($error) { @@ -562,6 +555,7 @@ class Controller * @access private * @param string $dataid * @param array $other + * @throws JsonException */ private function _json_result($dataid, $other = array()) { diff --git a/lib/Data/Database.php b/lib/Data/Database.php index 0fd42dcc..108278a5 100644 --- a/lib/Data/Database.php +++ b/lib/Data/Database.php @@ -15,6 +15,7 @@ use Exception; use PDO; use PDOException; use PrivateBin\Controller; +use PrivateBin\Exception\JsonException; use PrivateBin\Json; /** @@ -179,18 +180,24 @@ class Database extends AbstractData 'SELECT * FROM "' . $this->_sanitizeIdentifier('paste') . '" WHERE "dataid" = ?', array($pasteid), true ); - } catch (Exception $e) { + } catch (PDOException $e) { $row = false; } if ($row === false) { return false; } // create array - $paste = Json::decode($row['data']); + try { + $paste = Json::decode($row['data']); + } catch (JsonException $e) { + error_log('Error while reading a paste from the database: ' . $e->getMessage()); + $paste = array(); + } try { $paste['meta'] = Json::decode($row['meta']); - } catch (Exception $e) { + } catch (JsonException $e) { + error_log('Error while reading a paste from the database: ' . $e->getMessage()); $paste['meta'] = array(); } $expire_date = (int) $row['expiredate']; @@ -233,7 +240,7 @@ class Database extends AbstractData 'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('paste') . '" WHERE "dataid" = ?', array($pasteid), true ); - } catch (Exception $e) { + } catch (PDOException $e) { return false; } return (bool) $row; @@ -253,7 +260,7 @@ class Database extends AbstractData { try { $data = Json::encode($comment); - } catch (Exception $e) { + } catch (JsonException $e) { error_log('Error while attempting to insert a comment into the database: ' . $e->getMessage()); return false; } @@ -274,7 +281,7 @@ class Database extends AbstractData $meta['created'], ) ); - } catch (Exception $e) { + } catch (PDOException $e) { error_log('Error while attempting to insert a comment into the database: ' . $e->getMessage()); return false; } @@ -298,8 +305,14 @@ class Database extends AbstractData $comments = array(); if (count($rows)) { foreach ($rows as $row) { + try { + $data = Json::decode($row['data']); + } catch (JsonException $e) { + error_log('Error while reading a comment from the database: ' . $e->getMessage()); + $data = array(); + } $i = $this->getOpenSlot($comments, (int) $row['postdate']); - $comments[$i] = Json::decode($row['data']); + $comments[$i] = $data; $comments[$i]['id'] = $row['dataid']; $comments[$i]['parentid'] = $row['parentid']; $comments[$i]['meta'] = array('created' => (int) $row['postdate']); @@ -329,7 +342,7 @@ class Database extends AbstractData '" WHERE "pasteid" = ? AND "parentid" = ? AND "dataid" = ?', array($pasteid, $parentid, $commentid), true ); - } catch (Exception $e) { + } catch (PDOException $e) { return false; } } @@ -349,7 +362,8 @@ class Database extends AbstractData $this->_last_cache[$key] = $value; try { $value = Json::encode($this->_last_cache); - } catch (Exception $e) { + } catch (JsonException $e) { + error_log('Error encoding JSON for table "config", row "traffic_limiter": ' . $e->getMessage()); return false; } } @@ -393,7 +407,8 @@ class Database extends AbstractData if ($value && $namespace === 'traffic_limiter') { try { $this->_last_cache = Json::decode($value); - } catch (Exception $e) { + } catch (JsonException $e) { + error_log('Error decoding JSON from table "config", row "traffic_limiter": ' . $e->getMessage()); $this->_last_cache = array(); } if (array_key_exists($key, $this->_last_cache)) { @@ -412,13 +427,18 @@ class Database extends AbstractData */ protected function _getExpiredPastes($batchsize) { - $statement = $this->_db->prepare( - 'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('paste') . - '" WHERE "expiredate" < ? AND "expiredate" != ? ' . - ($this->_type === 'oci' ? 'FETCH NEXT ? ROWS ONLY' : 'LIMIT ?') - ); - $statement->execute(array(time(), 0, $batchsize)); - return $statement->fetchAll(PDO::FETCH_COLUMN, 0); + try { + $statement = $this->_db->prepare( + 'SELECT "dataid" FROM "' . $this->_sanitizeIdentifier('paste') . + '" WHERE "expiredate" < ? AND "expiredate" != ? ' . + ($this->_type === 'oci' ? 'FETCH NEXT ? ROWS ONLY' : 'LIMIT ?') + ); + $statement->execute(array(time(), 0, $batchsize)); + return $statement->fetchAll(PDO::FETCH_COLUMN, 0); + } catch (PDOException $e) { + error_log('Error while attempting to find expired pastes in the database: ' . $e->getMessage()); + return array(); + } } /** @@ -552,6 +572,7 @@ class Database extends AbstractData '" WHERE "id" = ?', array($key), true ); } catch (PDOException $e) { + error_log('Error while attempting to fetch configuration key "' . $key . '" in the database: ' . $e->getMessage()); return ''; } return $row ? $row['value'] : ''; diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index e4377a9f..751980ab 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -11,8 +11,8 @@ namespace PrivateBin\Data; -use Exception; use GlobIterator; +use PrivateBin\Exception\JsonException; use PrivateBin\Json; /** @@ -104,13 +104,10 @@ class Filesystem extends AbstractData */ public function read($pasteid) { - if ( - !$this->exists($pasteid) || - !$paste = $this->_get($this->_dataid2path($pasteid) . $pasteid . '.php') - ) { - return false; + if ($this->exists($pasteid)) { + return $this->_get($this->_dataid2path($pasteid) . $pasteid . '.php'); } - return $paste; + return false; } /** @@ -346,7 +343,12 @@ class Filesystem extends AbstractData file_get_contents($filename), strlen(self::PROTECTION_LINE . PHP_EOL) ); - return Json::decode($data); + try { + return Json::decode($data); + } catch (JsonException $e) { + error_log('Error decoding JSON from "' . $filename . '": ' . $e->getMessage()); + return false; + } } /** @@ -450,7 +452,7 @@ class Filesystem extends AbstractData $filename, self::PROTECTION_LINE . PHP_EOL . Json::encode($data) ); - } catch (Exception $e) { + } catch (JsonException $e) { error_log('Error while trying to store data to the filesystem at path "' . $filename . '": ' . $e->getMessage()); return false; } diff --git a/lib/Data/GoogleCloudStorage.php b/lib/Data/GoogleCloudStorage.php index 9f64abe5..2ebfb7fe 100644 --- a/lib/Data/GoogleCloudStorage.php +++ b/lib/Data/GoogleCloudStorage.php @@ -15,6 +15,7 @@ use Exception; use Google\Cloud\Core\Exception\NotFoundException; use Google\Cloud\Storage\Bucket; use Google\Cloud\Storage\StorageClient; +use PrivateBin\Exception\JsonException; use PrivateBin\Json; class GoogleCloudStorage extends AbstractData @@ -219,7 +220,12 @@ class GoogleCloudStorage extends AbstractData try { foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) { $data = $this->_bucket->object($key->name())->downloadAsString(); - $comment = Json::decode($data); + try { + $comment = Json::decode($data); + } catch (JsonException $e) { + error_log('failed to read comment from ' . $key->name() . ', ' . $e->getMessage()); + $comment = array(); + } $comment['id'] = basename($key->name()); $slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']); $comments[$slot] = $comment; diff --git a/lib/Data/S3Storage.php b/lib/Data/S3Storage.php index 526a9ec2..575f90f4 100644 --- a/lib/Data/S3Storage.php +++ b/lib/Data/S3Storage.php @@ -37,6 +37,7 @@ namespace PrivateBin\Data; use Aws\S3\Exception\S3Exception; use Aws\S3\S3Client; +use PrivateBin\Exception\JsonException; use PrivateBin\Json; class S3Storage extends AbstractData @@ -177,12 +178,14 @@ class S3Storage extends AbstractData 'ContentType' => 'application/json', 'Metadata' => $metadata, )); + return true; } catch (S3Exception $e) { error_log('failed to upload ' . $key . ' to ' . $this->_bucket . ', ' . trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); - return false; + } catch (JsonException $e) { + error_log('failed to JSON encode ' . $key . ', ' . $e->getMessage()); } - return true; + return false; } /** @@ -212,8 +215,10 @@ class S3Storage extends AbstractData } catch (S3Exception $e) { error_log('failed to read ' . $pasteid . ' from ' . $this->_bucket . ', ' . trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); - return false; + } catch (JsonException $e) { + error_log('failed to JSON decode ' . $key . ', ' . $e->getMessage()); } + return false; } /** diff --git a/lib/Exception/JsonException.php b/lib/Exception/JsonException.php new file mode 100644 index 00000000..dc844a2d --- /dev/null +++ b/lib/Exception/JsonException.php @@ -0,0 +1,37 @@ +_error = 'Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.'; $this->logErrorWithClassName('Error calling proxy: ' . $e->getMessage()); return; diff --git a/lib/Proxy/ShlinkProxy.php b/lib/Proxy/ShlinkProxy.php index ee4507cf..639a957c 100644 --- a/lib/Proxy/ShlinkProxy.php +++ b/lib/Proxy/ShlinkProxy.php @@ -12,6 +12,7 @@ namespace PrivateBin\Proxy; use PrivateBin\Configuration; +use PrivateBin\Exception\JsonException; use PrivateBin\Json; /** @@ -48,12 +49,17 @@ class ShlinkProxy extends AbstractProxy 'longUrl' => $link, ); - return array( - 'method' => 'POST', - 'header' => "Content-Type: application/json\r\n" . - 'X-Api-Key: ' . $shlink_api_key . "\r\n", - 'content' => Json::encode($body), - ); + try { + return array( + 'method' => 'POST', + 'header' => "Content-Type: application/json\r\n" . + 'X-Api-Key: ' . $shlink_api_key . "\r\n", + 'content' => Json::encode($body), + ); + } catch (JsonException $e) { + error_log('[' . get_class($this) . '] Error encoding body: ' . $e->getMessage()); + return array(); + } } /** diff --git a/lib/Request.php b/lib/Request.php index 24a083be..fb6fb37c 100644 --- a/lib/Request.php +++ b/lib/Request.php @@ -11,7 +11,7 @@ namespace PrivateBin; -use Exception; +use PrivateBin\Exception\JsonException; use PrivateBin\Model\Paste; /** @@ -113,7 +113,7 @@ class Request try { $data = file_get_contents(self::$_inputStream); $this->_params = Json::decode($data); - } catch (Exception $e) { + } catch (JsonException $e) { // ignore error, $this->_params will remain empty } break; diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index e842065d..b219f713 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -513,16 +513,19 @@ if ($FILEUPLOAD) : - From cfea0fb20e9024140dc5f2193f6f6c866aa3db43 Mon Sep 17 00:00:00 2001 From: Stephan Kristyn Date: Wed, 11 Feb 2026 19:03:34 +0100 Subject: [PATCH 84/89] Now leaving styling to customer if he wants the filename and filesize as a hyperlink or outside the hyperlink --- js/privatebin.js | 3 ++- lib/Configuration.php | 2 +- tpl/bootstrap.php | 5 +---- tpl/bootstrap5.php | 5 +---- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index cd5502fc..a65dcf42 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -2995,7 +2995,8 @@ jQuery.PrivateBin = (function($) { attachmentLink.attr('download', fileName); const fileSize = Helper.formatBytes(decodedData.length); - const span = template[0].querySelector('a > span'); + const spans = template[0].querySelectorAll('span'); + const span = spans[spans.length - 1]; span.textContent = ` (${fileName}, ${fileSize})`; } diff --git a/lib/Configuration.php b/lib/Configuration.php index 38c3f96c..10e4c069 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -122,7 +122,7 @@ class Configuration 'js/kjua-0.10.0.js' => 'sha512-BYj4xggowR7QD150VLSTRlzH62YPfhpIM+b/1EUEr7RQpdWAGKulxWnOvjFx1FUlba4m6ihpNYuQab51H6XlYg==', 'js/legacy.js' => 'sha512-RQEo1hxpNc37i+jz/D9/JiAZhG8GFx3+SNxjYnI7jUgirDIqrCSj6QPAAZeaidditcWzsJ3jxfEj5lVm7ZwTRQ==', 'js/prettify.js' => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==', - 'js/privatebin.js' => 'sha512-YOVC+ZjeGfgp/PBIZDJOKvbGRpFnQkXAXLKXLSgP/aXysO34DRxpNLFOmJeZlP9NfraS2tl9paeIcM0nV1uxcQ==', + 'js/privatebin.js' => 'sha512-KnVJOIXg/dcO/A1cEEWD/AlKfmGK2UNmNrqr0kD+ecj4nuwDiscimKy9Olt/iWcDAOQ8WG7JcJ/teQ3kfZWz9A==', 'js/purify-3.3.0.js' => 'sha512-lsHD5zxs4lu/NDzaaibe27Vd2t7Cy9JQ3qDHUvDfb4oZvKoWDNEhwUY+4bT3R68cGgpgCYp8U1x2ifeVxqurdQ==', 'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==', 'js/zlib-1.3.1-2.js' => 'sha512-4gT+v+BkBqdVBbKOO4qKGOAzuay+v1FmOLksS+bMgQ08Oo4xEb3X48Xq1Kv2b4HtiCQA7xq9dFRzxal7jmQI7w==', diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index b0d45142..6fcb4b20 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -691,10 +691,7 @@ endif; diff --git a/tpl/bootstrap5.php b/tpl/bootstrap5.php index 073feb62..e10118de 100644 --- a/tpl/bootstrap5.php +++ b/tpl/bootstrap5.php @@ -549,10 +549,7 @@ endif; From 5d22847ef1795c42c6f1dc54fcebe6d6325344ed Mon Sep 17 00:00:00 2001 From: Stephan Kristyn Date: Thu, 12 Feb 2026 13:48:49 +0100 Subject: [PATCH 85/89] ES6 Compat code broke everything. Reverting. E2E testing wth multiple files works --- js/privatebin.js | 2 +- lib/Configuration.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/js/privatebin.js b/js/privatebin.js index a65dcf42..378a89e5 100644 --- a/js/privatebin.js +++ b/js/privatebin.js @@ -2997,7 +2997,7 @@ jQuery.PrivateBin = (function($) { const fileSize = Helper.formatBytes(decodedData.length); const spans = template[0].querySelectorAll('span'); const span = spans[spans.length - 1]; - span.textContent = ` (${fileName}, ${fileSize})`; + span.textContent += ` (${fileName}, ${fileSize})`; } // sanitize SVG preview diff --git a/lib/Configuration.php b/lib/Configuration.php index 10e4c069..92347f77 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -122,7 +122,7 @@ class Configuration 'js/kjua-0.10.0.js' => 'sha512-BYj4xggowR7QD150VLSTRlzH62YPfhpIM+b/1EUEr7RQpdWAGKulxWnOvjFx1FUlba4m6ihpNYuQab51H6XlYg==', 'js/legacy.js' => 'sha512-RQEo1hxpNc37i+jz/D9/JiAZhG8GFx3+SNxjYnI7jUgirDIqrCSj6QPAAZeaidditcWzsJ3jxfEj5lVm7ZwTRQ==', 'js/prettify.js' => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==', - 'js/privatebin.js' => 'sha512-KnVJOIXg/dcO/A1cEEWD/AlKfmGK2UNmNrqr0kD+ecj4nuwDiscimKy9Olt/iWcDAOQ8WG7JcJ/teQ3kfZWz9A==', + 'js/privatebin.js' => 'sha512-n2IW9L2/VnsAJX1gumf7deXcgIqyp1RfnG40Cd8lK+uWNiX7gEqZ+rO6zrAa8hHMNyjbJiqXc/FYSE6xWJmZUw==', 'js/purify-3.3.0.js' => 'sha512-lsHD5zxs4lu/NDzaaibe27Vd2t7Cy9JQ3qDHUvDfb4oZvKoWDNEhwUY+4bT3R68cGgpgCYp8U1x2ifeVxqurdQ==', 'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==', 'js/zlib-1.3.1-2.js' => 'sha512-4gT+v+BkBqdVBbKOO4qKGOAzuay+v1FmOLksS+bMgQ08Oo4xEb3X48Xq1Kv2b4HtiCQA7xq9dFRzxal7jmQI7w==', From d1b45c1e7885bbb4810063e029f41282d744358c Mon Sep 17 00:00:00 2001 From: rugk Date: Tue, 17 Feb 2026 00:11:39 +0100 Subject: [PATCH 86/89] style(devcontainer): fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not a all relevant in any case, but still… --- .devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 961135ed..1d721037 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -41,6 +41,6 @@ 8080 ], "postCreateCommand": ".devcontainer/postCreateCommand.sh", - // alternatiuve: apache2ctl start (but requires root) + // alternative: apache2ctl start (but requires root) "postAttachCommand": "php -S 0.0.0.0:8080" } From a0fbafd1fc815d51df191bbb7fc049216c30f371 Mon Sep 17 00:00:00 2001 From: rugk Date: Tue, 17 Feb 2026 00:13:48 +0100 Subject: [PATCH 87/89] docs(templates): update PR template to remove WIP prepending nit/suggestion Given GitHub has full support for draft PRs nowadays, this "workaround" is not needed anymore and can be removed, IMHO. --- .github/PULL_REQUEST_TEMPLATE.md | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index f3a07b52..c03cca8a 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,7 +11,6 @@ This PR fixes * ## ToDo - * [ ] * [ ] * [ ] From 26e7e5eed9e633adadb5ee1af25de1812b546716 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Feb 2026 11:52:39 +0000 Subject: [PATCH 88/89] Bump dawidd6/action-download-artifact from 14 to 15 Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 14 to 15. - [Release notes](https://github.com/dawidd6/action-download-artifact/releases) - [Commits](https://github.com/dawidd6/action-download-artifact/compare/5c98f0b039f36ef966fdb7dfa9779262785ecb05...fe9d59ce33ce92db8a6ac90b2c8be6b6d90417c8) --- updated-dependencies: - dependency-name: dawidd6/action-download-artifact dependency-version: '15' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/test-results.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-results.yml b/.github/workflows/test-results.yml index 844e4af9..1d746d12 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@5c98f0b039f36ef966fdb7dfa9779262785ecb05 + uses: dawidd6/action-download-artifact@fe9d59ce33ce92db8a6ac90b2c8be6b6d90417c8 with: run_id: ${{ github.event.workflow_run.id }} path: artifacts From 7ed7cdd0e84c98fc6ec62150395cb9c417a3708d Mon Sep 17 00:00:00 2001 From: El RIDO Date: Wed, 18 Feb 2026 18:59:27 +0100 Subject: [PATCH 89/89] ignore CVE that breaks unit tests in Github actions see: https://github.com/firebase/php-jwt/issues/620 --- composer.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/composer.json b/composer.json index fa1bdb98..92a0b996 100644 --- a/composer.json +++ b/composer.json @@ -43,6 +43,11 @@ } }, "config" : { + "audit": { + "ignore": { + "CVE-2025-45769": "disputed on the basis that key lengths are expected to be set by an application, not by this library" + } + }, "autoloader-suffix" : "DontChange", "optimize-autoloader": true, "preferred-install": "dist",