From f71fd1be956db8546cdba7410365631f7d172d2a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 Jul 2026 08:25:54 +0000 Subject: [PATCH 1/8] chore(deps): bump ws from 8.20.1 to 8.21.0 in /js Bumps [ws](https://github.com/websockets/ws) from 8.20.1 to 8.21.0. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/8.20.1...8.21.0) --- updated-dependencies: - dependency-name: ws dependency-version: 8.21.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] --- js/package-lock.json | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/js/package-lock.json b/js/package-lock.json index 4addd989..03f7c7d7 100644 --- a/js/package-lock.json +++ b/js/package-lock.json @@ -1,12 +1,12 @@ { "name": "privatebin", - "version": "2.0.4", + "version": "2.0.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "privatebin", - "version": "2.0.4", + "version": "2.0.5", "license": "zlib-acknowledgement", "devDependencies": { "@peculiar/webcrypto": "^1.5.0", @@ -3993,10 +3993,11 @@ "dev": true }, "node_modules/ws": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", - "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, From c03cd8f278f696fb60c1db6953e65e929e779295 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Sat, 11 Jul 2026 11:47:08 +0200 Subject: [PATCH 2/8] chore: prepare for next release --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d45a9a2e..b7810631 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # PrivateBin version history +## 2.0.6 (not yet released) + ## 2.0.5 (2026-07-11) * CHANGED: Show OS-specific copy hotkey hint (Cmd+c on Mac, Ctrl+c on others) (#1506) * FIXED: Prevent browsers from rendering unsafe attachments like HTML in a new tab From a0c0d154ee0c7b69ebbfdd0b22f4ee9c5e85c62d Mon Sep 17 00:00:00 2001 From: TowyTowy Date: Sun, 12 Jul 2026 23:02:26 +0200 Subject: [PATCH 3/8] fix: handle YOURLS 200 response without shorturl gracefully When YOURLS replies with statusCode 200 but no "shorturl" field, YourlsProxy::_extractShortUrl() returned int 0 for its ?string return type. Under the file's declare(strict_types=1) this raises an uncaught TypeError, aborting paste creation instead of surfacing the intended "Error parsing proxy response" message. Fall back to null (matching ShlinkProxy) so the existing graceful error path is taken. Co-Authored-By: Claude --- CHANGELOG.md | 1 + lib/Proxy/YourlsProxy.php | 2 +- tst/YourlsProxyTest.php | 12 ++++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7810631..543bc6c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # PrivateBin version history ## 2.0.6 (not yet released) +* FIXED: Gracefully handle YOURLS replies with a 200 status code but no shorturl, instead of raising a TypeError ## 2.0.5 (2026-07-11) * CHANGED: Show OS-specific copy hotkey hint (Cmd+c on Mac, Ctrl+c on others) (#1506) diff --git a/lib/Proxy/YourlsProxy.php b/lib/Proxy/YourlsProxy.php index 76bd1c15..6fb9b0d3 100644 --- a/lib/Proxy/YourlsProxy.php +++ b/lib/Proxy/YourlsProxy.php @@ -66,7 +66,7 @@ class YourlsProxy extends AbstractProxy protected function _extractShortUrl(array $data): ?string { if ((int) ($data['statusCode'] ?? 0) === 200) { - return $data['shorturl'] ?? 0; + return $data['shorturl'] ?? null; } return null; } diff --git a/tst/YourlsProxyTest.php b/tst/YourlsProxyTest.php index 05b0b91e..fffc2e39 100644 --- a/tst/YourlsProxyTest.php +++ b/tst/YourlsProxyTest.php @@ -135,6 +135,18 @@ class YourlsProxyTest extends TestCase $this->assertEquals($yourls->getError(), 'Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.'); } + public function testYourlsSuccessWithoutShortUrl() + { + // YOURLS may reply with statusCode 200 but without a shorturl field; + // this must be handled gracefully as an error instead of raising a + // TypeError (the method is declared to return ?string) + file_put_contents($this->_mock_yourls_service, '{"statusCode":200}'); + + $yourls = new YourlsProxy($this->_conf, 'https://example.com/?foo#bar'); + $this->assertTrue($yourls->isError()); + $this->assertEquals($yourls->getError(), 'Proxy error: Error parsing proxy response. This can be a configuration issue, like wrong or missing config keys.'); + } + public function testServerError() { // simulate some other server error that results in a non-JSON reply From bb8b77d71429349f70a4a6885ec0ae3b54172213 Mon Sep 17 00:00:00 2001 From: TowyTowy Date: Tue, 14 Jul 2026 08:51:35 +0200 Subject: [PATCH 4/8] Use a string statusCode in the YourlsProxy test YOURLS returns the status code as a string (see #1844); mirror that in the regression test so it stays correct if the comparison ever changes. --- tst/YourlsProxyTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tst/YourlsProxyTest.php b/tst/YourlsProxyTest.php index fffc2e39..eb9b4993 100644 --- a/tst/YourlsProxyTest.php +++ b/tst/YourlsProxyTest.php @@ -139,8 +139,9 @@ class YourlsProxyTest extends TestCase { // YOURLS may reply with statusCode 200 but without a shorturl field; // this must be handled gracefully as an error instead of raising a - // TypeError (the method is declared to return ?string) - file_put_contents($this->_mock_yourls_service, '{"statusCode":200}'); + // TypeError (the method is declared to return ?string). YOURLS returns + // the status code as a string, so mirror that here. + file_put_contents($this->_mock_yourls_service, '{"statusCode":"200"}'); $yourls = new YourlsProxy($this->_conf, 'https://example.com/?foo#bar'); $this->assertTrue($yourls->isError()); From 4a50fed67863129ac60bb76a3641dd09c41f4e03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Jul 2026 11:52:20 +0000 Subject: [PATCH 5/8] chore(deps): bump actions/setup-node from 6 to 7 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 6 to 7. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/eslint.yml | 2 +- .github/workflows/tests.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index ab127b4d..a3747291 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -24,7 +24,7 @@ jobs: persist-credentials: false - name: Set up Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version: '20' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1057fe4b..b52540dd 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -224,7 +224,7 @@ jobs: persist-credentials: false - name: Setup Node - uses: actions/setup-node@v6 + uses: actions/setup-node@v7 with: node-version: '20' cache: 'npm' From 2f17c5f1d610df246c42d092f006c2161317ea59 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Wed, 15 Jul 2026 06:54:26 +0200 Subject: [PATCH 6/8] chore(deps): update DOMpurify to 3.4.12 --- CHANGELOG.md | 1 + js/common.js | 2 +- js/purify-3.4.1.js | 3 --- js/purify-3.4.12.js | 2 ++ lib/Configuration.php | 2 +- tpl/bootstrap.php | 2 +- tpl/bootstrap5.php | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) delete mode 100644 js/purify-3.4.1.js create mode 100644 js/purify-3.4.12.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 543bc6c0..76cbea67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # PrivateBin version history ## 2.0.6 (not yet released) +* CHANGED: Upgrading libraries to: DOMpurify 3.4.12 * FIXED: Gracefully handle YOURLS replies with a 200 status code but no shorturl, instead of raising a TypeError ## 2.0.5 (2026-07-11) diff --git a/js/common.js b/js/common.js index 8cd0992d..3b660f51 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.4.1'); +global.DOMPurify = require('./purify-3.4.12'); global.baseX = require('./base-x-5.0.1').baseX; global.Legacy = require('./legacy').Legacy; require('./privatebin'); diff --git a/js/purify-3.4.1.js b/js/purify-3.4.1.js deleted file mode 100644 index 45bdb2da..00000000 --- a/js/purify-3.4.1.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! @license DOMPurify 3.4.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.1/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]:T;if(t&&t(e,null),!h(o))return e;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 x(e){for(let t=0;t/gm),K=a(/\$\{[\w\W]*/gm),V=a(/^data-[\-\w.\u00B7-\uFFFF]+$/),Z=a(/^aria-[\-\w]+$/),J=a(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),Q=a(/^(?:\w+script|data):/i),ee=a(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),te=a(/^html$/i),ne=a(/^[a-z][.\w]*(-[.\w]+)+$/i);var oe=Object.freeze({__proto__:null,ARIA_ATTR:Z,ATTR_WHITESPACE:ee,CUSTOM_ELEMENT:ne,DATA_ATTR:V,DOCTYPE_NAME:te,ERB_EXPR:$,IS_ALLOWED_URI:J,IS_SCRIPT_OR_DATA:Q,MUSTACHE_EXPR:q,TMPLIT_EXPR:K});const re=1,ie=3,ae=7,le=8,ce=9,se=function(){return"undefined"==typeof window?null:window};var ue=function t(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:se();const o=e=>t(e);if(o.version="3.4.1",o.removed=[],!n||!n.document||n.document.nodeType!==ce||!n.Element)return o.isSupported=!1,o;let{document:r}=n;const a=r,c=a.currentScript,{DocumentFragment:s,HTMLTemplateElement:C,Node:L,Element:x,NodeFilter:q,NamedNodeMap:$=n.NamedNodeMap||n.MozNamedAttrMap,HTMLFormElement:K,DOMParser:V,trustedTypes:Z}=n,Q=x.prototype,ee=M(Q,"cloneNode"),ne=M(Q,"remove"),ue=M(Q,"nextSibling"),me=M(Q,"childNodes"),fe=M(Q,"parentNode");if("function"==typeof C){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let pe,de="";const{implementation:he,createNodeIterator:Te,createDocumentFragment:ge,getElementsByTagName:ye}=r,{importNode:Ae}=a;let Ee={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]};o.isSupported="function"==typeof e&&"function"==typeof fe&&he&&void 0!==he.createHTMLDocument;const{MUSTACHE_EXPR:_e,ERB_EXPR:Se,TMPLIT_EXPR:be,DATA_ATTR:Ne,ARIA_ATTR:Re,IS_SCRIPT_OR_DATA:De,ATTR_WHITESPACE:Oe,CUSTOM_ELEMENT:Ie}=oe;let{IS_ALLOWED_URI:we}=oe,Ce=null;const Le=k({},[...P,...U,...z,...H,...G]);let ke=null;const xe=k({},[...W,...j,...Y,...X]);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}})),Me=null,Pe=null;const Ue=Object.seal(l(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let ze=!0,Fe=!0,He=!1,Be=!0,Ge=!1,We=!0,je=!1,Ye=!1,Xe=!1,qe=!1,$e=!1,Ke=!1,Ve=!0,Ze=!1;const Je="user-content-";let Qe=!0,et=!1,tt={},nt=null;const ot=k({},["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 rt=null;const it=k({},["audio","video","img","source","image","track"]);let at=null;const lt=k({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),ct="http://www.w3.org/1998/Math/MathML",st="http://www.w3.org/2000/svg",ut="http://www.w3.org/1999/xhtml";let mt=ut,ft=!1,pt=null;const dt=k({},[ct,st,ut],g);let ht=k({},["mi","mo","mn","ms","mtext"]),Tt=k({},["annotation-xml"]);const gt=k({},["title","style","font","a","script"]);let yt=null;const At=["application/xhtml+xml","text/html"];let Et=null,_t=null;const St=r.createElement("form"),bt=function(e){return e instanceof RegExp||e instanceof Function},Nt=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if(_t&&_t===e)return;e&&"object"==typeof e||(e={}),e=v(e),yt=-1===At.indexOf(e.PARSER_MEDIA_TYPE)?"text/html":e.PARSER_MEDIA_TYPE,Et="application/xhtml+xml"===yt?g:T,Ce=D(e,"ALLOWED_TAGS")&&h(e.ALLOWED_TAGS)?k({},e.ALLOWED_TAGS,Et):Le,ke=D(e,"ALLOWED_ATTR")&&h(e.ALLOWED_ATTR)?k({},e.ALLOWED_ATTR,Et):xe,pt=D(e,"ALLOWED_NAMESPACES")&&h(e.ALLOWED_NAMESPACES)?k({},e.ALLOWED_NAMESPACES,g):dt,at=D(e,"ADD_URI_SAFE_ATTR")&&h(e.ADD_URI_SAFE_ATTR)?k(v(lt),e.ADD_URI_SAFE_ATTR,Et):lt,rt=D(e,"ADD_DATA_URI_TAGS")&&h(e.ADD_DATA_URI_TAGS)?k(v(it),e.ADD_DATA_URI_TAGS,Et):it,nt=D(e,"FORBID_CONTENTS")&&h(e.FORBID_CONTENTS)?k({},e.FORBID_CONTENTS,Et):ot,Me=D(e,"FORBID_TAGS")&&h(e.FORBID_TAGS)?k({},e.FORBID_TAGS,Et):v({}),Pe=D(e,"FORBID_ATTR")&&h(e.FORBID_ATTR)?k({},e.FORBID_ATTR,Et):v({}),tt=!!D(e,"USE_PROFILES")&&(e.USE_PROFILES&&"object"==typeof e.USE_PROFILES?v(e.USE_PROFILES):e.USE_PROFILES),ze=!1!==e.ALLOW_ARIA_ATTR,Fe=!1!==e.ALLOW_DATA_ATTR,He=e.ALLOW_UNKNOWN_PROTOCOLS||!1,Be=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,Ge=e.SAFE_FOR_TEMPLATES||!1,We=!1!==e.SAFE_FOR_XML,je=e.WHOLE_DOCUMENT||!1,qe=e.RETURN_DOM||!1,$e=e.RETURN_DOM_FRAGMENT||!1,Ke=e.RETURN_TRUSTED_TYPE||!1,Xe=e.FORCE_BODY||!1,Ve=!1!==e.SANITIZE_DOM,Ze=e.SANITIZE_NAMED_PROPS||!1,Qe=!1!==e.KEEP_CONTENT,et=e.IN_PLACE||!1,we=function(e){try{return I(e,""),!0}catch(e){return!1}}(e.ALLOWED_URI_REGEXP)?e.ALLOWED_URI_REGEXP:J,mt="string"==typeof e.NAMESPACE?e.NAMESPACE:ut,ht=D(e,"MATHML_TEXT_INTEGRATION_POINTS")&&e.MATHML_TEXT_INTEGRATION_POINTS&&"object"==typeof e.MATHML_TEXT_INTEGRATION_POINTS?v(e.MATHML_TEXT_INTEGRATION_POINTS):k({},["mi","mo","mn","ms","mtext"]),Tt=D(e,"HTML_INTEGRATION_POINTS")&&e.HTML_INTEGRATION_POINTS&&"object"==typeof e.HTML_INTEGRATION_POINTS?v(e.HTML_INTEGRATION_POINTS):k({},["annotation-xml"]);const t=D(e,"CUSTOM_ELEMENT_HANDLING")&&e.CUSTOM_ELEMENT_HANDLING&&"object"==typeof e.CUSTOM_ELEMENT_HANDLING?v(e.CUSTOM_ELEMENT_HANDLING):l(null);if(ve=l(null),D(t,"tagNameCheck")&&bt(t.tagNameCheck)&&(ve.tagNameCheck=t.tagNameCheck),D(t,"attributeNameCheck")&&bt(t.attributeNameCheck)&&(ve.attributeNameCheck=t.attributeNameCheck),D(t,"allowCustomizedBuiltInElements")&&"boolean"==typeof t.allowCustomizedBuiltInElements&&(ve.allowCustomizedBuiltInElements=t.allowCustomizedBuiltInElements),Ge&&(Fe=!1),$e&&(qe=!0),tt&&(Ce=k({},G),ke=l(null),!0===tt.html&&(k(Ce,P),k(ke,W)),!0===tt.svg&&(k(Ce,U),k(ke,j),k(ke,X)),!0===tt.svgFilters&&(k(Ce,z),k(ke,j),k(ke,X)),!0===tt.mathMl&&(k(Ce,H),k(ke,Y),k(ke,X))),Ue.tagCheck=null,Ue.attributeCheck=null,D(e,"ADD_TAGS")&&("function"==typeof e.ADD_TAGS?Ue.tagCheck=e.ADD_TAGS:h(e.ADD_TAGS)&&(Ce===Le&&(Ce=v(Ce)),k(Ce,e.ADD_TAGS,Et))),D(e,"ADD_ATTR")&&("function"==typeof e.ADD_ATTR?Ue.attributeCheck=e.ADD_ATTR:h(e.ADD_ATTR)&&(ke===xe&&(ke=v(ke)),k(ke,e.ADD_ATTR,Et))),D(e,"ADD_URI_SAFE_ATTR")&&h(e.ADD_URI_SAFE_ATTR)&&k(at,e.ADD_URI_SAFE_ATTR,Et),D(e,"FORBID_CONTENTS")&&h(e.FORBID_CONTENTS)&&(nt===ot&&(nt=v(nt)),k(nt,e.FORBID_CONTENTS,Et)),D(e,"ADD_FORBID_CONTENTS")&&h(e.ADD_FORBID_CONTENTS)&&(nt===ot&&(nt=v(nt)),k(nt,e.ADD_FORBID_CONTENTS,Et)),Qe&&(Ce["#text"]=!0),je&&k(Ce,["html","head","body"]),Ce.table&&(k(Ce,["tbody"]),delete Me.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw w('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw w('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');pe=e.TRUSTED_TYPES_POLICY,de=pe.createHTML("")}else void 0===pe&&(pe=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}}(Z,c)),null!==pe&&"string"==typeof de&&(de=pe.createHTML(""));i&&i(e),_t=e},Rt=k({},[...U,...z,...F]),Dt=k({},[...H,...B]),Ot=function(e){p(o.removed,{element:e});try{fe(e).removeChild(e)}catch(t){ne(e)}},It=function(e,t){try{p(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){p(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e)if(qe||$e)try{Ot(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},wt=function(e){let t=null,n=null;if(Xe)e=""+e;else{const t=y(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===yt&&mt===ut&&(e=''+e+"");const o=pe?pe.createHTML(e):e;if(mt===ut)try{t=(new V).parseFromString(o,yt)}catch(e){}if(!t||!t.documentElement){t=he.createDocument(mt,"template",null);try{t.documentElement.innerHTML=ft?de:o}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(r.createTextNode(n),i.childNodes[0]||null),mt===ut?ye.call(t,je?"html":"body")[0]:je?t.documentElement:i},Ct=function(e){return Te.call(e.ownerDocument||e,e,q.SHOW_ELEMENT|q.SHOW_COMMENT|q.SHOW_TEXT|q.SHOW_PROCESSING_INSTRUCTION|q.SHOW_CDATA_SECTION,null)},Lt=function(e){return e instanceof K&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof $)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},kt=function(e){return"function"==typeof L&&e instanceof L};function xt(e,t,n){u(e,e=>{e.call(o,t,n,_t)})}const vt=function(e){let t=null;if(xt(Ee.beforeSanitizeElements,e,null),Lt(e))return Ot(e),!0;const n=Et(e.nodeName);if(xt(Ee.uponSanitizeElement,e,{tagName:n,allowedTags:Ce}),We&&e.hasChildNodes()&&!kt(e.firstElementChild)&&I(/<[/\w!]/g,e.innerHTML)&&I(/<[/\w!]/g,e.textContent))return Ot(e),!0;if(We&&e.namespaceURI===ut&&"style"===n&&kt(e.firstElementChild))return Ot(e),!0;if(e.nodeType===ae)return Ot(e),!0;if(We&&e.nodeType===le&&I(/<[/\w]/g,e.data))return Ot(e),!0;if(Me[n]||!(Ue.tagCheck instanceof Function&&Ue.tagCheck(n))&&!Ce[n]){if(!Me[n]&&Ut(n)){if(ve.tagNameCheck instanceof RegExp&&I(ve.tagNameCheck,n))return!1;if(ve.tagNameCheck instanceof Function&&ve.tagNameCheck(n))return!1}if(Qe&&!nt[n]){const t=fe(e)||e.parentNode,n=me(e)||e.childNodes;if(n&&t){for(let o=n.length-1;o>=0;--o){const r=ee(n[o],!0);t.insertBefore(r,ue(e))}}}return Ot(e),!0}return e instanceof x&&!function(e){let t=fe(e);t&&t.tagName||(t={namespaceURI:mt,tagName:"template"});const n=T(e.tagName),o=T(t.tagName);return!!pt[e.namespaceURI]&&(e.namespaceURI===st?t.namespaceURI===ut?"svg"===n:t.namespaceURI===ct?"svg"===n&&("annotation-xml"===o||ht[o]):Boolean(Rt[n]):e.namespaceURI===ct?t.namespaceURI===ut?"math"===n:t.namespaceURI===st?"math"===n&&Tt[o]:Boolean(Dt[n]):e.namespaceURI===ut?!(t.namespaceURI===st&&!Tt[o])&&!(t.namespaceURI===ct&&!ht[o])&&!Dt[n]&&(gt[n]||!Rt[n]):!("application/xhtml+xml"!==yt||!pt[e.namespaceURI]))}(e)?(Ot(e),!0):"noscript"!==n&&"noembed"!==n&&"noframes"!==n||!I(/<\/no(script|embed|frames)/i,e.innerHTML)?(Ge&&e.nodeType===ie&&(t=e.textContent,u([_e,Se,be],e=>{t=A(t,e," ")}),e.textContent!==t&&(p(o.removed,{element:e.cloneNode()}),e.textContent=t)),xt(Ee.afterSanitizeElements,e,null),!1):(Ot(e),!0)},Mt=function(e,t,n){if(Pe[t])return!1;if(Ve&&("id"===t||"name"===t)&&(n in r||n in St))return!1;if(Fe&&!Pe[t]&&I(Ne,t));else if(ze&&I(Re,t));else if(Ue.attributeCheck instanceof Function&&Ue.attributeCheck(t,e));else if(!ke[t]||Pe[t]){if(!(Ut(e)&&(ve.tagNameCheck instanceof RegExp&&I(ve.tagNameCheck,e)||ve.tagNameCheck instanceof Function&&ve.tagNameCheck(e))&&(ve.attributeNameCheck instanceof RegExp&&I(ve.attributeNameCheck,t)||ve.attributeNameCheck instanceof Function&&ve.attributeNameCheck(t,e))||"is"===t&&ve.allowCustomizedBuiltInElements&&(ve.tagNameCheck instanceof RegExp&&I(ve.tagNameCheck,n)||ve.tagNameCheck instanceof Function&&ve.tagNameCheck(n))))return!1}else if(at[t]);else if(I(we,A(n,Oe,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==E(n,"data:")||!rt[e]){if(He&&!I(De,A(n,Oe,"")));else if(n)return!1}else;return!0},Pt=k({},["annotation-xml","color-profile","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","missing-glyph"]),Ut=function(e){return!Pt[T(e)]&&I(Ie,e)},zt=function(e){xt(Ee.beforeSanitizeAttributes,e,null);const{attributes:t}=e;if(!t||Lt(e))return;const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:ke,forceKeepAttr:void 0};let r=t.length;for(;r--;){const i=t[r],{name:a,namespaceURI:l,value:c}=i,s=Et(a),m=c;let p="value"===a?m:_(m);if(n.attrName=s,n.attrValue=p,n.keepAttr=!0,n.forceKeepAttr=void 0,xt(Ee.uponSanitizeAttribute,e,n),p=n.attrValue,!Ze||"id"!==s&&"name"!==s||0===E(p,Je)||(It(a,e),p=Je+p),We&&I(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i,p)){It(a,e);continue}if("attributename"===s&&y(p,"href")){It(a,e);continue}if(n.forceKeepAttr)continue;if(!n.keepAttr){It(a,e);continue}if(!Be&&I(/\/>/i,p)){It(a,e);continue}Ge&&u([_e,Se,be],e=>{p=A(p,e," ")});const d=Et(e.nodeName);if(Mt(d,s,p)){if(pe&&"object"==typeof Z&&"function"==typeof Z.getAttributeType)if(l);else switch(Z.getAttributeType(d,s)){case"TrustedHTML":p=pe.createHTML(p);break;case"TrustedScriptURL":p=pe.createScriptURL(p)}if(p!==m)try{l?e.setAttributeNS(l,a,p):e.setAttribute(a,p),Lt(e)?Ot(e):f(o.removed)}catch(t){It(a,e)}}else It(a,e)}xt(Ee.afterSanitizeAttributes,e,null)},Ft=function(e){let t=null;const n=Ct(e);for(xt(Ee.beforeSanitizeShadowDOM,e,null);t=n.nextNode();)xt(Ee.uponSanitizeShadowNode,t,null),vt(t),zt(t),t.content instanceof s&&Ft(t.content);xt(Ee.afterSanitizeShadowDOM,e,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(ft=!e,ft&&(e="\x3c!--\x3e"),"string"!=typeof e&&!kt(e)&&"string"!=typeof(e=function(e){switch(typeof e){case"string":return e;case"number":return S(e);case"boolean":return b(e);case"bigint":return N?N(e):"0";case"symbol":return R?R(e):"Symbol()";case"undefined":default:return O(e);case"function":case"object":{if(null===e)return O(e);const t=e,n=M(t,"toString");if("function"==typeof n){const e=n(t);return"string"==typeof e?e:O(e)}return O(e)}}}(e)))throw w("dirty is not a string, aborting");if(!o.isSupported)return e;if(Ye||Nt(t),o.removed=[],"string"==typeof e&&(et=!1),et){const t=e.nodeName;if("string"==typeof t){const e=Et(t);if(!Ce[e]||Me[e])throw w("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof L)n=wt("\x3c!----\x3e"),r=n.ownerDocument.importNode(e,!0),r.nodeType===re&&"BODY"===r.nodeName||"HTML"===r.nodeName?n=r:n.appendChild(r);else{if(!qe&&!Ge&&!je&&-1===e.indexOf("<"))return pe&&Ke?pe.createHTML(e):e;if(n=wt(e),!n)return qe?null:Ke?de:""}n&&Xe&&Ot(n.firstChild);const c=Ct(et?e:n);for(;i=c.nextNode();)vt(i),zt(i),i.content instanceof s&&Ft(i.content);if(et)return e;if(qe){if(Ge){n.normalize();let e=n.innerHTML;u([_e,Se,be],t=>{e=A(e,t," ")}),n.innerHTML=e}if($e)for(l=ge.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return(ke.shadowroot||ke.shadowrootmode)&&(l=Ae.call(a,l,!0)),l}let m=je?n.outerHTML:n.innerHTML;return je&&Ce["!doctype"]&&n.ownerDocument&&n.ownerDocument.doctype&&n.ownerDocument.doctype.name&&I(te,n.ownerDocument.doctype.name)&&(m="\n"+m),Ge&&u([_e,Se,be],e=>{m=A(m,e," ")}),pe&&Ke?pe.createHTML(m):m},o.setConfig=function(){Nt(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}),Ye=!0},o.clearConfig=function(){_t=null,Ye=!1},o.isValidAttribute=function(e,t,n){_t||Nt({});const o=Et(e),r=Et(t);return Mt(o,r,n)},o.addHook=function(e,t){"function"==typeof t&&p(Ee[e],t)},o.removeHook=function(e,t){if(void 0!==t){const n=m(Ee[e],t);return-1===n?void 0:d(Ee[e],n,1)[0]}return f(Ee[e])},o.removeHooks=function(e){Ee[e]=[]},o.removeAllHooks=function(){Ee={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}},o}();return ue}); -//# sourceMappingURL=purify.min.js.map diff --git a/js/purify-3.4.12.js b/js/purify-3.4.12.js new file mode 100644 index 00000000..1a09c376 --- /dev/null +++ b/js/purify-3.4.12.js @@ -0,0 +1,2 @@ +/*! @license DOMPurify 3.4.12 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.12/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";function e(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=Array(t);n2?n-2:0),r=2;r1?t-1:0),o=1;o1?n-1:0),r=1;r2&&void 0!==arguments[2]?arguments[2]:T;if(o&&o(e,null),!b(t))return e;let i=t.length;for(;i--;){let o=t[i];if("string"==typeof o){const e=n(o);e!==o&&(r(t)||(t[i]=e),o=e)}e[o]=!0}return e}function z(e){for(let t=0;t/g),J=c(/\${[\w\W]*/g),Q=c(/^data-[\-\w.\u00B7-\uFFFF]+$/),ee=c(/^aria-[\-\w]+$/),te=c(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|matrix):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),ne=c(/^(?:\w+script|data):/i),oe=c(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),re=c(/^html$/i),ie=c(/^[a-z][.\w]*(-[.\w]+)+$/i),ae=c(/<[/\w!]/g),le=c(/<[/\w]/g),ce=c(/<\/no(script|embed|frames)/i),se=c(/\/>/i),ue=1,fe=3,pe=7,me=8,de=9,he=11,ge=function(){return"undefined"==typeof window?null:window},ye=function(e,t,n,o){return R(e,t)&&b(e[t])?M(o.base?P(o.base):{},e[t],o.transform):n};var be=function e(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:ge();const o=t=>e(t);if(o.version="3.4.12",o.removed=[],!t||!t.document||t.document.nodeType!==de||!t.Element)return o.isSupported=!1,o;let r=t.document;const i=r,a=i.currentScript;t.DocumentFragment;const u=t.HTMLTemplateElement,f=t.Node,p=t.Element,k=t.NodeFilter,L=t.NamedNodeMap;void 0===L&&(t.NamedNodeMap||t.MozNamedAttrMap),t.HTMLFormElement;const z=t.DOMParser,be=t.trustedTypes,Te=p.prototype,Se=U(Te,"cloneNode"),Ee=U(Te,"remove"),Ae=U(Te,"nextSibling"),Ne=U(Te,"childNodes"),_e=U(Te,"parentNode"),we=U(Te,"shadowRoot"),Oe=U(Te,"attributes"),ve=f&&f.prototype?U(f.prototype,"nodeType"):null,De=f&&f.prototype?U(f.prototype,"nodeName"):null;if("function"==typeof u){const e=r.createElement("template");e.content&&e.content.ownerDocument&&(r=e.content.ownerDocument)}let Re,Ce,Ie="",xe=!1,ke=0;const Le=function(){if(ke>0)throw x('A configured TRUSTED_TYPES_POLICY callback (createHTML or createScriptURL) must not call DOMPurify.sanitize, as that causes infinite recursion. Do not pass a policy whose callbacks wrap DOMPurify as TRUSTED_TYPES_POLICY; see the "DOMPurify and Trusted Types" section of the README.')},Me=function(e){Le(),ke++;try{return Re.createHTML(e)}finally{ke--}},ze=function(){return xe||(Ce=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}}(be,a),xe=!0),Ce},Pe=r,Ue=Pe.implementation,Fe=Pe.createNodeIterator,He=Pe.createDocumentFragment,je=Pe.getElementsByTagName,Be=i.importNode;let Ge={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]};o.isSupported="function"==typeof n&&"function"==typeof _e&&Ue&&void 0!==Ue.createHTMLDocument;const We=V,Ye=Z,qe=J,Xe=Q,$e=ee,Ke=ne,Ve=oe,Ze=ie;let Je=te,Qe=null;const et=M({},[...F,...H,...j,...G,...Y]);let tt=null;const nt=M({},[...q,...X,...$,...K]);let ot=Object.seal(s(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}})),rt=null,it=null;const at=Object.seal(s(null,{tagCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeCheck:{writable:!0,configurable:!1,enumerable:!0,value:null}}));let lt=!0,ct=!0,st=!1,ut=!0,ft=!1,pt=!0,mt=!1,dt=!1,ht=null,gt=null,yt=!1,bt=!1,Tt=!1,St=!1,Et=!0,At=!1;const Nt="user-content-";let _t=!0,wt=!1,Ot={},vt=null;const Dt=M({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","selectedcontent","style","svg","template","thead","title","video","xmp"]);let Rt=null;const Ct=M({},["audio","video","img","source","image","track"]);let It=null;const xt=M({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),kt="http://www.w3.org/1998/Math/MathML",Lt="http://www.w3.org/2000/svg",Mt="http://www.w3.org/1999/xhtml";let zt=Mt,Pt=!1,Ut=null;const Ft=M({},[kt,Lt,Mt],S),Ht=l(["mi","mo","mn","ms","mtext"]);let jt=M({},Ht);const Bt=l(["annotation-xml"]);let Gt=M({},Bt);const Wt=M({},["title","style","font","a","script"]);let Yt=null;const qt=["application/xhtml+xml","text/html"];let Xt=null,$t=null;const Kt=r.createElement("form"),Vt=function(e){return e instanceof RegExp||e instanceof Function},Zt=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};if($t&&$t===e)return;e&&"object"==typeof e||(e={}),e=P(e),Yt=-1===qt.indexOf(e.PARSER_MEDIA_TYPE)?"text/html":e.PARSER_MEDIA_TYPE,Xt="application/xhtml+xml"===Yt?S:T,Qe=ye(e,"ALLOWED_TAGS",et,{transform:Xt}),tt=ye(e,"ALLOWED_ATTR",nt,{transform:Xt}),Ut=ye(e,"ALLOWED_NAMESPACES",Ft,{transform:S}),It=ye(e,"ADD_URI_SAFE_ATTR",xt,{transform:Xt,base:xt}),Rt=ye(e,"ADD_DATA_URI_TAGS",Ct,{transform:Xt,base:Ct}),vt=ye(e,"FORBID_CONTENTS",Dt,{transform:Xt}),rt=ye(e,"FORBID_TAGS",P({}),{transform:Xt}),it=ye(e,"FORBID_ATTR",P({}),{transform:Xt}),Ot=!!R(e,"USE_PROFILES")&&(e.USE_PROFILES&&"object"==typeof e.USE_PROFILES?P(e.USE_PROFILES):e.USE_PROFILES),lt=!1!==e.ALLOW_ARIA_ATTR,ct=!1!==e.ALLOW_DATA_ATTR,st=e.ALLOW_UNKNOWN_PROTOCOLS||!1,ut=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,ft=e.SAFE_FOR_TEMPLATES||!1,pt=!1!==e.SAFE_FOR_XML,mt=e.WHOLE_DOCUMENT||!1,bt=e.RETURN_DOM||!1,Tt=e.RETURN_DOM_FRAGMENT||!1,St=e.RETURN_TRUSTED_TYPE||!1,yt=e.FORCE_BODY||!1,Et=!1!==e.SANITIZE_DOM,At=e.SANITIZE_NAMED_PROPS||!1,_t=!1!==e.KEEP_CONTENT,wt=e.IN_PLACE||!1,Je=function(e){try{return I(e,""),!0}catch(e){return!1}}(e.ALLOWED_URI_REGEXP)?e.ALLOWED_URI_REGEXP:te,zt="string"==typeof e.NAMESPACE?e.NAMESPACE:Mt,jt=R(e,"MATHML_TEXT_INTEGRATION_POINTS")&&e.MATHML_TEXT_INTEGRATION_POINTS&&"object"==typeof e.MATHML_TEXT_INTEGRATION_POINTS?P(e.MATHML_TEXT_INTEGRATION_POINTS):M({},Ht),Gt=R(e,"HTML_INTEGRATION_POINTS")&&e.HTML_INTEGRATION_POINTS&&"object"==typeof e.HTML_INTEGRATION_POINTS?P(e.HTML_INTEGRATION_POINTS):M({},Bt);const t=R(e,"CUSTOM_ELEMENT_HANDLING")&&e.CUSTOM_ELEMENT_HANDLING&&"object"==typeof e.CUSTOM_ELEMENT_HANDLING?P(e.CUSTOM_ELEMENT_HANDLING):s(null);if(ot=s(null),R(t,"tagNameCheck")&&Vt(t.tagNameCheck)&&(ot.tagNameCheck=t.tagNameCheck),R(t,"attributeNameCheck")&&Vt(t.attributeNameCheck)&&(ot.attributeNameCheck=t.attributeNameCheck),R(t,"allowCustomizedBuiltInElements")&&"boolean"==typeof t.allowCustomizedBuiltInElements&&(ot.allowCustomizedBuiltInElements=t.allowCustomizedBuiltInElements),c(ot),ft&&(ct=!1),Tt&&(bt=!0),Ot&&(Qe=M({},Y),tt=s(null),!0===Ot.html&&(M(Qe,F),M(tt,q)),!0===Ot.svg&&(M(Qe,H),M(tt,X),M(tt,K)),!0===Ot.svgFilters&&(M(Qe,j),M(tt,X),M(tt,K)),!0===Ot.mathMl&&(M(Qe,G),M(tt,$),M(tt,K))),at.tagCheck=null,at.attributeCheck=null,R(e,"ADD_TAGS")&&("function"==typeof e.ADD_TAGS?at.tagCheck=e.ADD_TAGS:b(e.ADD_TAGS)&&(Qe===et&&(Qe=P(Qe)),M(Qe,e.ADD_TAGS,Xt))),R(e,"ADD_ATTR")&&("function"==typeof e.ADD_ATTR?at.attributeCheck=e.ADD_ATTR:b(e.ADD_ATTR)&&(tt===nt&&(tt=P(tt)),M(tt,e.ADD_ATTR,Xt))),R(e,"ADD_URI_SAFE_ATTR")&&b(e.ADD_URI_SAFE_ATTR)&&M(It,e.ADD_URI_SAFE_ATTR,Xt),R(e,"FORBID_CONTENTS")&&b(e.FORBID_CONTENTS)&&(vt===Dt&&(vt=P(vt)),M(vt,e.FORBID_CONTENTS,Xt)),R(e,"ADD_FORBID_CONTENTS")&&b(e.ADD_FORBID_CONTENTS)&&(vt===Dt&&(vt=P(vt)),M(vt,e.ADD_FORBID_CONTENTS,Xt)),_t&&(Qe["#text"]=!0),mt&&M(Qe,["html","head","body"]),Qe.table&&(M(Qe,["tbody"]),delete rt.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw x('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw x('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');const t=Re;Re=e.TRUSTED_TYPES_POLICY;try{Ie=Me("")}catch(e){throw Re=t,e}}else null===e.TRUSTED_TYPES_POLICY?(Re=void 0,Ie=""):(void 0===Re&&(Re=ze()),Re&&"string"==typeof Ie&&(Ie=Me("")));l&&l(e),$t=e},Jt=M({},[...H,...j,...B]),Qt=M({},[...G,...W]),en=function(e){let t=_e(e);t&&t.tagName||(t={namespaceURI:zt,tagName:"template"});const n=T(e.tagName),o=T(t.tagName);return!!Ut[e.namespaceURI]&&(e.namespaceURI===Lt?function(e,t,n){return t.namespaceURI===Mt?"svg"===e:t.namespaceURI===kt?"svg"===e&&("annotation-xml"===n||jt[n]):Boolean(Jt[e])}(n,t,o):e.namespaceURI===kt?function(e,t,n){return t.namespaceURI===Mt?"math"===e:t.namespaceURI===Lt?"math"===e&&Gt[n]:Boolean(Qt[e])}(n,t,o):e.namespaceURI===Mt?function(e,t,n){return!(t.namespaceURI===Lt&&!Gt[n])&&!(t.namespaceURI===kt&&!jt[n])&&!Qt[e]&&(Wt[e]||!Jt[e])}(n,t,o):!("application/xhtml+xml"!==Yt||!Ut[e.namespaceURI]))},tn=function(e){g(o.removed,{element:e});try{_e(e).removeChild(e)}catch(t){if(Ee(e),!_e(e))throw x("a node selected for removal could not be detached from its tree and cannot be safely returned; refusing to sanitize in place")}},nn=function(e){an(e);const t=Ne(e);if(t){const e=[];m(t,t=>{g(e,t)}),m(e,e=>{try{Ee(e)}catch(e){}})}const n=Oe(e);if(n)for(let t=n.length-1;t>=0;--t){const o=n[t],r=o&&o.name;if("string"==typeof r)try{e.removeAttribute(r)}catch(e){}}},on=function(e,t){try{g(o.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){g(o.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e)if(bt||Tt)try{tn(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},rn=function(e){const t=Oe(e);if(t)for(let n=t.length-1;n>=0;--n){const o=t[n],r=o&&o.name;if("string"==typeof r&&!tt[Xt(r)])try{e.removeAttribute(r)}catch(e){}}},an=function(e){const t=[e];for(;t.length>0;){const e=t.pop();(ve?ve(e):e.nodeType)===ue&&rn(e);const n=Ne(e);if(n)for(let e=n.length-1;e>=0;--e)t.push(n[e])}},ln=function(e){let t=null,n=null;if(yt)e=""+e;else{const t=E(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===Yt&&zt===Mt&&(e=''+e+"");const o=Re?Me(e):e;if(zt===Mt)try{t=(new z).parseFromString(o,Yt)}catch(e){}if(!t||!t.documentElement){t=Ue.createDocument(zt,"template",null);try{t.documentElement.innerHTML=Pt?Ie:o}catch(e){}}const i=t.body||t.documentElement;return e&&n&&i.insertBefore(r.createTextNode(n),i.childNodes[0]||null),zt===Mt?je.call(t,mt?"html":"body")[0]:mt?t.documentElement:i},cn=function(e){return Fe.call(e.ownerDocument||e,e,k.SHOW_ELEMENT|k.SHOW_COMMENT|k.SHOW_TEXT|k.SHOW_PROCESSING_INSTRUCTION|k.SHOW_CDATA_SECTION,null)},sn=function(e){return e=A(e,We," "),e=A(e,Ye," "),e=A(e,qe," ")},un=function(e){var t;e.normalize();const n=Fe.call(e.ownerDocument||e,e,k.SHOW_TEXT|k.SHOW_COMMENT|k.SHOW_CDATA_SECTION|k.SHOW_PROCESSING_INSTRUCTION,null);let o=n.nextNode();for(;o;)o.data=sn(o.data),o=n.nextNode();const r=null===(t=e.querySelectorAll)||void 0===t?void 0:t.call(e,"template");r&&m(r,e=>{pn(e.content)&&un(e.content)})},fn=function(e){const t=De?De(e):null;return"string"==typeof t&&("form"===Xt(t)&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||e.attributes!==Oe(e)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes||e.nodeType!==ve(e)||e.childNodes!==Ne(e)))},pn=function(e){if(!ve||"object"!=typeof e||null===e)return!1;try{return ve(e)===he}catch(e){return!1}},mn=function(e){if(!ve||"object"!=typeof e||null===e)return!1;try{return"number"==typeof ve(e)}catch(e){return!1}};function dn(e,t,n){0!==e.length&&m(e,e=>{e.call(o,t,n,$t)})}const hn=function(e,t){if(dn(Ge.beforeSanitizeElements,e,null),e!==t&&null===_e(e))return!0;if(fn(e))return tn(e),!0;const n=Xt(De?De(e):e.nodeName);if(dn(Ge.uponSanitizeElement,e,{tagName:n,allowedTags:Qe}),e!==t&&null===_e(e))return!0;if(function(e,t){return!!(pt&&e.hasChildNodes()&&!mn(e.firstElementChild)&&I(ae,e.textContent)&&I(ae,e.innerHTML))||!(!pt||e.namespaceURI!==Mt||"style"!==t||!mn(e.firstElementChild))||e.nodeType===pe||!(!pt||e.nodeType!==me||!I(le,e.data))}(e,n))return tn(e),!0;if(rt[n]||!(at.tagCheck instanceof Function&&at.tagCheck(n))&&!Qe[n]){const t=function(e,t){if(!rt[t]&&bn(t)){if(ot.tagNameCheck instanceof RegExp&&I(ot.tagNameCheck,t))return!1;if(ot.tagNameCheck instanceof Function&&ot.tagNameCheck(t))return!1}if(_t&&!vt[t]){const t=_e(e),n=Ne(e);if(n&&t)for(let o=n.length-1;o>=0;--o){const r=wt?n[o]:Se(n[o],!0);t.insertBefore(r,Ae(e))}}return tn(e),!0}(e,n);return!1===t&&dn(Ge.afterSanitizeElements,e,null),t}if((ve?ve(e):e.nodeType)===ue&&!en(e))return tn(e),!0;if(("noscript"===n||"noembed"===n||"noframes"===n)&&I(ce,e.innerHTML))return tn(e),!0;if(ft&&e.nodeType===fe){const t=sn(e.textContent);e.textContent!==t&&(g(o.removed,{element:e.cloneNode()}),e.textContent=t)}return dn(Ge.afterSanitizeElements,e,null),!1},gn=function(e,t,n){if(it[t])return!1;if(pt&&"patchsrc"===t)return!1;if(pt&&"for"===t&&"label"!==e&&"output"!==e)return!1;if(Et&&("id"===t||"name"===t)&&(n in r||n in Kt))return!1;const o=tt[t]||at.attributeCheck instanceof Function&&at.attributeCheck(t,e);if(ct&&I(Xe,t));else if(lt&&I($e,t));else if(o)if(It[t]);else if(I(Je,A(n,Ve,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==N(n,"data:")||!Rt[e]){if(st&&!I(Ke,A(n,Ve,"")));else if(n)return!1}else;else if(!(bn(e)&&(ot.tagNameCheck instanceof RegExp&&I(ot.tagNameCheck,e)||ot.tagNameCheck instanceof Function&&ot.tagNameCheck(e))&&(ot.attributeNameCheck instanceof RegExp&&I(ot.attributeNameCheck,t)||ot.attributeNameCheck instanceof Function&&ot.attributeNameCheck(t,e))||"is"===t&&ot.allowCustomizedBuiltInElements&&(ot.tagNameCheck instanceof RegExp&&I(ot.tagNameCheck,n)||ot.tagNameCheck instanceof Function&&ot.tagNameCheck(n))))return!1;return!0},yn=M({},["annotation-xml","color-profile","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","missing-glyph"]),bn=function(e){return!yn[T(e)]&&I(Ze,e)},Tn=function(e,t,n,o){if(Re&&"object"==typeof be&&"function"==typeof be.getAttributeType&&!n)switch(be.getAttributeType(e,t)){case"TrustedHTML":return Me(o);case"TrustedScriptURL":return function(e){Le(),ke++;try{return Re.createScriptURL(e)}finally{ke--}}(o)}return o},Sn=function(e,t,n,r){try{n?e.setAttributeNS(n,t,r):e.setAttribute(t,r),fn(e)?tn(e):h(o.removed)}catch(n){on(t,e)}},En=function(e){dn(Ge.beforeSanitizeAttributes,e,null);const t=e.attributes;if(!t||fn(e))return;const n={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:tt,forceKeepAttr:void 0};let o=t.length;const r=Xt(e.nodeName);for(;o--;){const i=t[o],a=i.name,l=i.namespaceURI,c=i.value,s=Xt(a),u=c;let f="value"===a?u:_(u);n.attrName=s,n.attrValue=f,n.keepAttr=!0,n.forceKeepAttr=void 0,dn(Ge.uponSanitizeAttribute,e,n),f=n.attrValue,!At||"id"!==s&&"name"!==s||0===N(f,Nt)||(on(a,e),f=Nt+f),pt&&I(/((--!?|])>)|<\/(style|script|title|xmp|textarea|noscript|iframe|noembed|noframes)/i,f)?on(a,e):"attributename"===s&&E(f,"href")?on(a,e):n.forceKeepAttr||(n.keepAttr&&(ut||!I(se,f))?(ft&&(f=sn(f)),gn(r,s,f)?(f=Tn(r,s,l,f),f!==u&&Sn(e,a,l,f)):on(a,e)):on(a,e))}dn(Ge.afterSanitizeAttributes,e,null)},An=function(e){let t=null;const n=cn(e);for(dn(Ge.beforeSanitizeShadowDOM,e,null);t=n.nextNode();){dn(Ge.uponSanitizeShadowNode,t,null),hn(t,e),En(t),pn(t.content)&&An(t.content);if((ve?ve(t):t.nodeType)===ue){const e=we(t);pn(e)&&(Nn(e),An(e))}}dn(Ge.afterSanitizeShadowDOM,e,null)},Nn=function(e){const t=[{node:e,shadow:null}];for(;t.length>0;){const e=t.pop();if(e.shadow){An(e.shadow);continue}const n=e.node,o=(ve?ve(n):n.nodeType)===ue,r=Ne(n);if(r)for(let e=r.length-1;e>=0;--e)t.push({node:r[e],shadow:null});if(o){const e=De?De(n):null;if("string"==typeof e&&"template"===Xt(e)){const e=n.content;pn(e)&&t.push({node:e,shadow:null})}}if(o){const e=we(n);pn(e)&&t.push({node:null,shadow:e},{node:e,shadow:null})}}};return o.sanitize=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=null,r=null,a=null,l=null;if(Pt=!e,Pt&&(e="\x3c!--\x3e"),"string"!=typeof e&&!mn(e)&&"string"!=typeof(e=function(e){switch(typeof e){case"string":return e;case"number":return w(e);case"boolean":return O(e);case"bigint":return v?v(e):"0";case"symbol":return D?D(e):"Symbol()";case"undefined":default:return C(e);case"function":case"object":{if(null===e)return C(e);const t=e,n=U(t,"toString");if("function"==typeof n){const e=n(t);return"string"==typeof e?e:C(e)}return C(e)}}}(e)))throw x("dirty is not a string, aborting");if(!o.isSupported)return e;dt?(Qe=ht,tt=gt):Zt(t),(Ge.uponSanitizeElement.length>0||Ge.uponSanitizeAttribute.length>0)&&(Qe=P(Qe)),Ge.uponSanitizeAttribute.length>0&&(tt=P(tt)),o.removed=[];const c=wt&&"string"!=typeof e&&mn(e);if(c){!function(e){if(!pt)return;const t=[e];for(;t.length>0;){const e=t.pop(),n=ve?ve(e):e.nodeType;if(n===pe||n===me&&I(le,e.data)){try{Ee(e)}catch(e){}continue}if(n===ue){const t=e,n=Xt(De?De(e):e.nodeName);try{t.hasAttribute&&t.hasAttribute("patchsrc")&&t.removeAttribute("patchsrc"),t.hasAttribute&&t.hasAttribute("for")&&"label"!==n&&"output"!==n&&t.removeAttribute("for")}catch(e){}}const o=Ne(e);if(o)for(let e=o.length-1;e>=0;--e)t.push(o[e])}}(e);const t=De?De(e):e.nodeName;if("string"==typeof t){const n=Xt(t);if(!Qe[n]||rt[n])throw nn(e),x("root node is forbidden and cannot be sanitized in-place")}if(fn(e))throw nn(e),x("root node is clobbered and cannot be sanitized in-place");try{Nn(e)}catch(t){throw nn(e),t}}else if(mn(e))n=ln("\x3c!----\x3e"),r=n.ownerDocument.importNode(e,!0),r.nodeType===ue&&"BODY"===r.nodeName||"HTML"===r.nodeName?n=r:n.appendChild(r),Nn(r);else{if(!bt&&!ft&&!mt&&-1===e.indexOf("<"))return Re&&St?Me(e):e;if(n=ln(e),!n)return bt?null:St?Ie:""}n&&yt&&tn(n.firstChild);const s=c?e:n,u=cn(s);try{for(;a=u.nextNode();)hn(a,s),En(a),pn(a.content)&&An(a.content)}catch(t){throw c&&(nn(e),m(o.removed,e=>{e.element&&an(e.element)})),t}if(c)return m(o.removed,e=>{e.element&&an(e.element)}),ft&&un(e),e;if(bt){if(ft&&un(n),Tt)for(l=He.call(n.ownerDocument);n.firstChild;)l.appendChild(n.firstChild);else l=n;return(tt.shadowroot||tt.shadowrootmode)&&(l=Be.call(i,l,!0)),l}let f=mt?n.outerHTML:n.innerHTML;return mt&&Qe["!doctype"]&&n.ownerDocument&&n.ownerDocument.doctype&&n.ownerDocument.doctype.name&&I(re,n.ownerDocument.doctype.name)&&(f="\n"+f),ft&&(f=sn(f)),Re&&St?Me(f):f},o.setConfig=function(){Zt(arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}),dt=!0,ht=Qe,gt=tt},o.clearConfig=function(){$t=null,dt=!1,ht=null,gt=null,Re=Ce,Ie=""},o.isValidAttribute=function(e,t,n){$t||Zt({});const o=Xt(e),r=Xt(t);return gn(o,r,n)},o.addHook=function(e,t){"function"==typeof t&&R(Ge,e)&&g(Ge[e],t)},o.removeHook=function(e,t){if(R(Ge,e)){if(void 0!==t){const n=d(Ge[e],t);return-1===n?void 0:y(Ge[e],n,1)[0]}return h(Ge[e])}},o.removeHooks=function(e){R(Ge,e)&&(Ge[e]=[])},o.removeAllHooks=function(){Ge={afterSanitizeAttributes:[],afterSanitizeElements:[],afterSanitizeShadowDOM:[],beforeSanitizeAttributes:[],beforeSanitizeElements:[],beforeSanitizeShadowDOM:[],uponSanitizeAttribute:[],uponSanitizeElement:[],uponSanitizeShadowNode:[]}},o}();return be}); diff --git a/lib/Configuration.php b/lib/Configuration.php index 8f7c1a50..34a7389c 100644 --- a/lib/Configuration.php +++ b/lib/Configuration.php @@ -123,7 +123,7 @@ class Configuration 'js/legacy.js' => 'sha512-RQEo1hxpNc37i+jz/D9/JiAZhG8GFx3+SNxjYnI7jUgirDIqrCSj6QPAAZeaidditcWzsJ3jxfEj5lVm7ZwTRQ==', 'js/prettify.js' => 'sha512-puO0Ogy++IoA2Pb9IjSxV1n4+kQkKXYAEUtVzfZpQepyDPyXk8hokiYDS7ybMogYlyyEIwMLpZqVhCkARQWLMg==', 'js/privatebin.js' => 'sha512-g58rPgrKnvxBDnT6lzYazdEfgY3XhRh7Yt7i1jkJw4oSvN0k/VPZ4zBSL/dCGfXGIOHqlqmX8DO5UFv2XYqsXg==', - 'js/purify-3.4.1.js' => 'sha512-280a/Vb6fVFsYaeRrkuDp4EDmdYlt2XS+dlDEO/U9qljPrAraA2bIzHTNmP+9dpwPDDwTML+RS+h5iaagPwTzA==', + 'js/purify-3.4.12.js' => 'sha512-Akf6HnAJZm0sWWWI4gp2GYff0NDnHUB02XJE5S7Hdq/Z5xtMjkuFacsDA8ZtViv1gi+onBxMhEMIaGyQeGxBng==', 'js/showdown-2.1.0.js' => 'sha512-WYXZgkTR0u/Y9SVIA4nTTOih0kXMEd8RRV6MLFdL6YU8ymhR528NLlYQt1nlJQbYz4EW+ZsS0fx1awhiQJme1Q==', 'js/zlib-1.3.2.js' => 'sha512-RAhJgxg9siMIA8ky4c10Rc2zUgnK80olHB8Tt1IOYWY4Eh1WmrviQkDn+sgBlb38ZHq3tzufGC41kP360gmosQ==', 'js/zlib.js' => 'sha512-QOaEwssHqHRRcWJ2Un3Kl2Zhyprzl7T8zmsKN2FppFxW3VR+8UChYOx2iuL0HbXK42fuBWJm5PNQJxufulrt/w==', diff --git a/tpl/bootstrap.php b/tpl/bootstrap.php index 4a60b534..9b29ead4 100644 --- a/tpl/bootstrap.php +++ b/tpl/bootstrap.php @@ -66,7 +66,7 @@ if ($MARKDOWN) : - _scriptTag('js/purify-3.4.1.js', 'defer'); ?> + _scriptTag('js/purify-3.4.12.js', 'defer'); ?> _scriptTag('js/legacy.js', 'defer'); ?> _scriptTag('js/privatebin.js', 'defer'); ?> diff --git a/tpl/bootstrap5.php b/tpl/bootstrap5.php index a1ac5600..e2ecb1e8 100644 --- a/tpl/bootstrap5.php +++ b/tpl/bootstrap5.php @@ -50,7 +50,7 @@ if ($MARKDOWN) : - _scriptTag('js/purify-3.4.1.js', 'defer'); ?> + _scriptTag('js/purify-3.4.12.js', 'defer'); ?> _scriptTag('js/legacy.js', 'defer'); ?> _scriptTag('js/privatebin.js', 'defer'); ?> From e80efda7c2c5d7ac42a07aefea3e3776a062535c Mon Sep 17 00:00:00 2001 From: marouanehassine Date: Fri, 17 Jul 2026 14:44:04 +0200 Subject: [PATCH 7/8] fix: return "Invalid data." instead of HTTP 500 on malformed v2 payloads Under strict_types on PHP 8, malformed JSON API v2 payloads triggered uncaught TypeErrors (HTTP 500) instead of the standard JSON error response {"status":1,"message":"Invalid data."}: - a non-numeric `v` reached `$data['v'] + 0` in Request::getData() - a scalar `meta` reached `count($message['meta'])` in FormatV2::isValid() - non-array adata[0] / non-string ct fields reached base64_decode(), strlen() and count() with the wrong types Validate the types before the numeric coercion and before the string/count operations, so all such inputs are rejected cleanly with "Invalid data." instead of a fatal error. Both crashes are remotely triggerable without authentication (availability only, no data exposure). Adds FormatV2Test coverage for the malformed-type inputs. Fixes #1883 --- lib/FormatV2.php | 17 ++++++++++++++++- lib/Request.php | 9 +++++++-- tst/FormatV2Test.php | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/lib/FormatV2.php b/lib/FormatV2.php index 7f1912fa..b7e749f9 100644 --- a/lib/FormatV2.php +++ b/lib/FormatV2.php @@ -56,7 +56,21 @@ class FormatV2 return false; } - $cipherParams = $isComment ? $message['adata'] : $message['adata'][0]; + $cipherParams = $isComment ? $message['adata'] : ($message['adata'][0] ?? null); + + // Make sure the cipher parameters are a properly sized array. + if (!is_array($cipherParams) || count($cipherParams) < 8) { + return false; + } + + // Make sure the ciphertext and the cipher parameters used in the + // string operations below are actually strings, so that malformed + // input yields "Invalid data." instead of a fatal type error. + if (!is_string($message['ct']) || + !is_string($cipherParams[0] ?? null) || + !is_string($cipherParams[1] ?? null)) { + return false; + } // Make sure some fields are base64 data: // - initialization vector @@ -119,6 +133,7 @@ class FormatV2 // require only the key 'expire' in the metadata of pastes if (!$isComment && ( + !is_array($message['meta']) || count($message['meta']) === 0 || !array_key_exists('expire', $message['meta']) || count($message['meta']) > 1 diff --git a/lib/Request.php b/lib/Request.php index 31233472..c1902b72 100644 --- a/lib/Request.php +++ b/lib/Request.php @@ -189,8 +189,13 @@ class Request foreach ($required_keys as $key) { $data[$key] = $this->getParam($key, $key === 'v' ? 1 : ''); } - // forcing a cast to int or float - $data['v'] = $data['v'] + 0; + // forcing a cast to int or float, but only when the value is + // numeric; a non-numeric version is left untouched so the format + // validator rejects it with "Invalid data." instead of a fatal + // type error + if (is_numeric($data['v'])) { + $data['v'] = $data['v'] + 0; + } return $data; } diff --git a/tst/FormatV2Test.php b/tst/FormatV2Test.php index 2e13d8f5..b5dfca1b 100644 --- a/tst/FormatV2Test.php +++ b/tst/FormatV2Test.php @@ -72,4 +72,41 @@ class FormatV2Test extends TestCase $paste = Helper::getPaste(); $this->assertFalse(FormatV2::isValid($paste), 'invalid meta key'); } + + public function testFormatV2ValidatorRejectsMalformedTypes() + { + // malformed input must yield "false" (Invalid data) rather than a + // fatal type error under strict_types + $paste = Helper::getPastePost(); + $paste['v'] = 'abc'; + $this->assertFalse(FormatV2::isValid($paste), 'non-numeric version'); + + $paste = Helper::getPastePost(); + $paste['v'] = array(); + $this->assertFalse(FormatV2::isValid($paste), 'array version'); + + $paste = Helper::getPastePost(); + $paste['ct'] = array('not', 'a', 'string'); + $this->assertFalse(FormatV2::isValid($paste), 'non-string ciphertext'); + + $paste = Helper::getPastePost(); + $paste['meta'] = 'not-an-array'; + $this->assertFalse(FormatV2::isValid($paste), 'non-array meta'); + + $paste = Helper::getPastePost(); + $paste['adata'][0] = 'not-an-array'; + $this->assertFalse(FormatV2::isValid($paste), 'non-array cipher parameters'); + + $paste = Helper::getPastePost(); + $paste['adata'][0] = array(); + $this->assertFalse(FormatV2::isValid($paste), 'empty cipher parameters'); + + $paste = Helper::getPastePost(); + $paste['adata'][0][0] = array(); + $this->assertFalse(FormatV2::isValid($paste), 'non-string iv'); + + $comment = Helper::getCommentPost(); + $comment['ct'] = 42; + $this->assertFalse(FormatV2::isValid($comment, true), 'non-string comment ciphertext'); + } } From abd1418bc8784448f0979648f80f7eee3064530b Mon Sep 17 00:00:00 2001 From: marouanehassine Date: Sat, 18 Jul 2026 08:16:21 +0200 Subject: [PATCH 8/8] fix: reject malformed JSON API bodies instead of crashing Address review feedback on #1885: - reject valid JSON scalar bodies (number, bool, string) that decode without error but are not a usable parameter set, so they no longer trigger a type error further down (Request::getParams) - drop the numeric coercion of `v` in Request::getData() and let the FormatV2 validator reject non-numeric versions, as suggested - add RequestTest::testPostScalarJson coverage for scalar bodies - align FormatV2Test assignments per StyleCI - add CHANGELOG entry referencing #1883 --- CHANGELOG.md | 1 + lib/Request.php | 12 +++++------- tst/FormatV2Test.php | 6 +++--- tst/RequestTest.php | 20 ++++++++++++++++++++ 4 files changed, 29 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76cbea67..60d82c83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 2.0.6 (not yet released) * CHANGED: Upgrading libraries to: DOMpurify 3.4.12 * FIXED: Gracefully handle YOURLS replies with a 200 status code but no shorturl, instead of raising a TypeError +* FIXED: Return "Invalid data." instead of HTTP 500 on malformed v2 JSON payloads (#1883) ## 2.0.5 (2026-07-11) * CHANGED: Show OS-specific copy hotkey hint (Cmd+c on Mac, Ctrl+c on others) (#1506) diff --git a/lib/Request.php b/lib/Request.php index c1902b72..a8272131 100644 --- a/lib/Request.php +++ b/lib/Request.php @@ -113,6 +113,11 @@ class Request try { $data = file_get_contents(self::$_inputStream); $this->_params = Json::decode($data); + // a valid JSON scalar (number, bool or string) decodes + // without error, but is not a usable set of parameters + if (!is_array($this->_params)) { + $this->_params = array(); + } } catch (JsonException $e) { // ignore error, $this->_params will remain empty } @@ -189,13 +194,6 @@ class Request foreach ($required_keys as $key) { $data[$key] = $this->getParam($key, $key === 'v' ? 1 : ''); } - // forcing a cast to int or float, but only when the value is - // numeric; a non-numeric version is left untouched so the format - // validator rejects it with "Invalid data." instead of a fatal - // type error - if (is_numeric($data['v'])) { - $data['v'] = $data['v'] + 0; - } return $data; } diff --git a/tst/FormatV2Test.php b/tst/FormatV2Test.php index b5dfca1b..c8b855dc 100644 --- a/tst/FormatV2Test.php +++ b/tst/FormatV2Test.php @@ -93,15 +93,15 @@ class FormatV2Test extends TestCase $paste['meta'] = 'not-an-array'; $this->assertFalse(FormatV2::isValid($paste), 'non-array meta'); - $paste = Helper::getPastePost(); + $paste = Helper::getPastePost(); $paste['adata'][0] = 'not-an-array'; $this->assertFalse(FormatV2::isValid($paste), 'non-array cipher parameters'); - $paste = Helper::getPastePost(); + $paste = Helper::getPastePost(); $paste['adata'][0] = array(); $this->assertFalse(FormatV2::isValid($paste), 'empty cipher parameters'); - $paste = Helper::getPastePost(); + $paste = Helper::getPastePost(); $paste['adata'][0][0] = array(); $this->assertFalse(FormatV2::isValid($paste), 'non-string iv'); diff --git a/tst/RequestTest.php b/tst/RequestTest.php index d7e4d9ac..fc168b27 100644 --- a/tst/RequestTest.php +++ b/tst/RequestTest.php @@ -158,6 +158,26 @@ class RequestTest extends TestCase $this->assertEquals('create', $request->getOperation()); } + public function testPostScalarJson() + { + // a valid JSON scalar body (number, boolean or quoted string) must not + // be treated as a set of parameters, so that it is rejected cleanly + // instead of triggering a type error further down + foreach (array('1', '0.0', 'true', 'false', '"example"') as $scalar) { + $this->reset(); + $_SERVER['REQUEST_METHOD'] = 'POST'; + $_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest'; + $file = Helper::createTempFile(); + file_put_contents($file, $scalar); + Request::setInputStream($file); + $request = new Request; + unlink($file); + $this->assertTrue($request->isJsonApiCall(), 'is JSON API call'); + $this->assertEquals('create', $request->getOperation()); + $this->assertEquals('', $request->getParam('ct'), "scalar body {$scalar} yields no parameters"); + } + } + public function testReadWithNegotiation() { $this->reset();