CVE-2023-3672 (JavaScript, published 2023-07-14T10:15Z)
Description: value: Cross-site Scripting (XSS) - DOM in GitHub repository plaidweb/webmention.js prior to 0.5.5.
Severity: nan (CVSS v3 base score: 6.1)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
Repository: https://github.com/plaidweb/webmention.js
Files changed:
static/webmention.js
static/webmention.min.js
tests/xss-test.html
Commit message: XSS mitigation

This change fixes a security issue raised by @tyage and reported by @psmoros,
in which the `p-name` property wasn't being properly entity-escaped, leading
to an XSS injection attack.

As part of this change I also improved the way that entities are being escaped.


Vulnerable code:
    return text.replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;");
          linktext = c.name;
!function(){"use strict";window.i18next=window.i18next||{t:function(n){return n}};const n=window.i18next.t.bind(window.i18next);function e(n,e){return document.currentScript.getAttribute("data-"+n)||e}const t=e("page-url",window.location.href.replace(/#.*$/,"")),o=e("add-urls",void 0),r=e("id","webmentions"),s=e("wordcount"),i=e("max-webmentions",30),l=e("prevent-spoofing")?"wm-source":"url",a=e("sort-by","published"),c=e("sort-dir","up"),u=e("comments-are-reactions",!1),p={"in-reply-to":n("replied"),"like-of":n("liked"),"repost-of":n("reposted"),"bookmark-of":n("bookmarked"),"mention-of":n("mentioned"),rsvp:n("RSVPed"),"follow-of":n("followed")},f={"in-reply-to":"💬","like-of":"❤️","repost-of":"🔄","bookmark-of":"⭐️","mention-of":"💬",rsvp:"📅","follow-of":"🐜"},m={yes:"✅",no:"❌",interested:"💡",maybe:"💭"};function d(n){return n.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function w(e,t){const o=d(e.author?.name||e.url.split("/")[2]);let r=p[e["wm-property"]]||n("reacted");!t&&e.content&&e.content.text&&(r+=": "+x(e));let s="";s=e.author&&e.author.photo?`\n        <img\n          src="${d(e.author.photo)}"\n          loading="lazy"\n          decoding="async"\n          alt="${o}"\n        >\n      `:`\n        <img\n          class="missing"\n          src="data:image/webp;base64,UklGRkoCAABXRUJQVlA4TD4CAAAvP8APAIV0WduUOLr/m/iqY6SokDJSMD5xYX23SQizRsVdZmIj/f6goYUbiOj/BED7MOPReuBNT3vBesSzIex+SeqMFFkjebFmzH3S7POxDSJ1yaCbCmMnS2R46cRMPyQLw4GBK4esdK60pYwsZakecUCl5zsHv/5cPH08nx9/7i6rEEVCg2hR8VSd30PxMZpVoJZQO6Dixgg6X5oKFCmlVHIDmmMFShWumAXgCuyqVN8hHff/k+9fj8+ei7BVjpxBmZCUJv+6DhWGZwWvs+UoLHFCKsPYpfJtIcEXBTopEEsKwedZUv4ku1FZErKULLyQwFGgnmTs2vBD5qu44xwnG9uyjgrFOd+KRVlXyQfwQlauydaU6AVI7OjKXLUEqNtxJBmQegNDZgV7lxxqYMOMrDyC1NdAGbdiH9Ij0skjG+oTyfO0lmjdgvoH8iIgreuBMRYLSH+R3sAztXgL+XfS7E2bmfo6gnS0TrpnzHT7kL+skj7PgHuBwv/zpN8LDLQg7zfJZLBubMKnyeh6ZGyfDEfc2LYpnlUtG7JqsSHq1WoASbUS4KVaLwB8be5mfsGMDwBcm5VxbuxWxx3nkFanB6lYqsqSkOGkKicoDvXsneR7BkKU7DtaEuT7+pxBGVwx+9gVyqf2pVA9sC2CsmjZ1RJqEJHS4Tj/pCcS0JoyBYOsB91Xjh3OFfQPQhvCAYyeLJlaOoFp0XNNuD0BC8exr8uPx7D1JWkwFdZIXmD3MOPReuDNzHjBesSzIbQD"\n          alt="${o}$"\n        >\n      `;let i="";return e.rsvp&&m[e.rsvp]&&(i=`<sub>${m[e.rsvp]}</sub>`),`\n      <a\n        class="reaction"\n        rel="nofollow ugc"\n        title="${o} ${r}"\n        href="${e[l]}"\n      >\n        ${s}\n        ${f[e["wm-property"]]||"💥"}\n        ${i}\n      </a>\n    `}function h(n){return n.substr(n.indexOf("//"))}function g(n){const e=[],t={};return n.forEach((function(n){const o=h(n.url);t[o]||(e.push(n),t[o]=!0)})),e}function x(n){let e=d(n.content.text);if(s){let n=e.replace(/\s+/g," ").split(" ",s+1);n.length>s&&(n[s-1]+="&hellip;",n=n.slice(0,s),e=n.join(" "))}return e}window.addEventListener("load",(async function(){const e=document.getElementById(r);if(!e)return;const s=[h(t)];o&&o.split("|").forEach((function(n){s.push(h(n))}));let p=`https://webmention.io/api/mentions.jf2?per-page=${i}&sort-by=${a}&sort-dir=${c}`;s.forEach((function(n){p+=`&target[]=${encodeURIComponent("http:"+n)}&target[]=${encodeURIComponent("https:"+n)}`}));let f={};try{const n=await window.fetch(p);n.status>=200&&n.status<300?f=await n.json():(console.error("Could not parse response"),new Error(n.statusText))}catch(n){console.error("Request failed",n)}let m=[];const $=[];u&&(m=$);const y={"in-reply-to":m,"like-of":$,"repost-of":$,"bookmark-of":$,"follow-of":$,"mention-of":m,rsvp:m};f.children.forEach((function(n){const e=y[n["wm-property"]];e&&e.push(n)}));let b="";m.length>0&&m!==$&&(b=function(e){return`\n      <h2>${n("Responses")}</h2>\n      <ul class="comments">${e.map((e=>{const t=w(e,!0);let o=d(e.url.split("/")[2]);e.author&&e.author.name&&(o=d(e.author.name));const r=`<a class="source" rel="nofollow ugc" href="${e[l]}">${o}</a>`;let s="name",i=`(${n("mention")})`;return e.name?(s="name",i=e.name):e.content&&e.content.text&&(s="text",i=x(e)),`<li>${t} ${r} <span class="${s}">${i}</span></li>`})).join("")}</ul>\n    `}(g(m)));let k="";var B;$.length>0&&(B=g($),k=`\n      <h2>${n("Reactions")}</h2>\n      <ul class="reacts">${B.map((n=>w(n))).join("")}</ul>\n    `),e.innerHTML=`${b}${k}`}))}();
<script src="../static/webmention.js" data-page-url="https://beesbuzz.biz/blog/chatter/4201-Just-testing-something-on-webmention-io" async></script>

Fixed code:
    return text.replace(/[&<>"]/g, (tag) => ({
      '&': '&amp;',
      '<': '&lt;',
      '>': '&gt;',
      '"': '&quot;',
    }[tag] || tag));
          linktext = entities(c.name);
!function(){"use strict";window.i18next=window.i18next||{t:function(n){return n}};const n=window.i18next.t.bind(window.i18next);function t(n,t){return document.currentScript.getAttribute("data-"+n)||t}const e=t("page-url",window.location.href.replace(/#.*$/,"")),o=t("add-urls",void 0),r=t("id","webmentions"),s=t("wordcount"),i=t("max-webmentions",30),l=t("prevent-spoofing")?"wm-source":"url",a=t("sort-by","published"),c=t("sort-dir","up"),u=t("comments-are-reactions",!1),p={"in-reply-to":n("replied"),"like-of":n("liked"),"repost-of":n("reposted"),"bookmark-of":n("bookmarked"),"mention-of":n("mentioned"),rsvp:n("RSVPed"),"follow-of":n("followed")},f={"in-reply-to":"💬","like-of":"❤️","repost-of":"🔄","bookmark-of":"⭐️","mention-of":"💬",rsvp:"📅","follow-of":"🐜"},m={yes:"✅",no:"❌",interested:"💡",maybe:"💭"};function d(n){return n.replace(/[&<>"]/g,(n=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[n]||n)))}function w(t,e){const o=d(t.author?.name||t.url.split("/")[2]);let r=p[t["wm-property"]]||n("reacted");!e&&t.content&&t.content.text&&(r+=": "+x(t));let s="";s=t.author&&t.author.photo?`\n        <img\n          src="${d(t.author.photo)}"\n          loading="lazy"\n          decoding="async"\n          alt="${o}"\n        >\n      `:`\n        <img\n          class="missing"\n          src="data:image/webp;base64,UklGRkoCAABXRUJQVlA4TD4CAAAvP8APAIV0WduUOLr/m/iqY6SokDJSMD5xYX23SQizRsVdZmIj/f6goYUbiOj/BED7MOPReuBNT3vBesSzIex+SeqMFFkjebFmzH3S7POxDSJ1yaCbCmMnS2R46cRMPyQLw4GBK4esdK60pYwsZakecUCl5zsHv/5cPH08nx9/7i6rEEVCg2hR8VSd30PxMZpVoJZQO6Dixgg6X5oKFCmlVHIDmmMFShWumAXgCuyqVN8hHff/k+9fj8+ei7BVjpxBmZCUJv+6DhWGZwWvs+UoLHFCKsPYpfJtIcEXBTopEEsKwedZUv4ku1FZErKULLyQwFGgnmTs2vBD5qu44xwnG9uyjgrFOd+KRVlXyQfwQlauydaU6AVI7OjKXLUEqNtxJBmQegNDZgV7lxxqYMOMrDyC1NdAGbdiH9Ij0skjG+oTyfO0lmjdgvoH8iIgreuBMRYLSH+R3sAztXgL+XfS7E2bmfo6gnS0TrpnzHT7kL+skj7PgHuBwv/zpN8LDLQg7zfJZLBubMKnyeh6ZGyfDEfc2LYpnlUtG7JqsSHq1WoASbUS4KVaLwB8be5mfsGMDwBcm5VxbuxWxx3nkFanB6lYqsqSkOGkKicoDvXsneR7BkKU7DtaEuT7+pxBGVwx+9gVyqf2pVA9sC2CsmjZ1RJqEJHS4Tj/pCcS0JoyBYOsB91Xjh3OFfQPQhvCAYyeLJlaOoFp0XNNuD0BC8exr8uPx7D1JWkwFdZIXmD3MOPReuDNzHjBesSzIbQD"\n          alt="${o}$"\n        >\n      `;let i="";return t.rsvp&&m[t.rsvp]&&(i=`<sub>${m[t.rsvp]}</sub>`),`\n      <a\n        class="reaction"\n        rel="nofollow ugc"\n        title="${o} ${r}"\n        href="${t[l]}"\n      >\n        ${s}\n        ${f[t["wm-property"]]||"💥"}\n        ${i}\n      </a>\n    `}function h(n){return n.substr(n.indexOf("//"))}function g(n){const t=[],e={};return n.forEach((function(n){const o=h(n.url);e[o]||(t.push(n),e[o]=!0)})),t}function x(n){let t=d(n.content.text);if(s){let n=t.replace(/\s+/g," ").split(" ",s+1);n.length>s&&(n[s-1]+="&hellip;",n=n.slice(0,s),t=n.join(" "))}return t}window.addEventListener("load",(async function(){const t=document.getElementById(r);if(!t)return;const s=[h(e)];o&&o.split("|").forEach((function(n){s.push(h(n))}));let p=`https://webmention.io/api/mentions.jf2?per-page=${i}&sort-by=${a}&sort-dir=${c}`;s.forEach((function(n){p+=`&target[]=${encodeURIComponent("http:"+n)}&target[]=${encodeURIComponent("https:"+n)}`}));let f={};try{const n=await window.fetch(p);n.status>=200&&n.status<300?f=await n.json():(console.error("Could not parse response"),new Error(n.statusText))}catch(n){console.error("Request failed",n)}let m=[];const $=[];u&&(m=$);const y={"in-reply-to":m,"like-of":$,"repost-of":$,"bookmark-of":$,"follow-of":$,"mention-of":m,rsvp:m};f.children.forEach((function(n){const t=y[n["wm-property"]];t&&t.push(n)}));let b="";m.length>0&&m!==$&&(b=function(t){return`\n      <h2>${n("Responses")}</h2>\n      <ul class="comments">${t.map((t=>{const e=w(t,!0);let o=d(t.url.split("/")[2]);t.author&&t.author.name&&(o=d(t.author.name));const r=`<a class="source" rel="nofollow ugc" href="${t[l]}">${o}</a>`;let s="name",i=`(${n("mention")})`;return t.name?(s="name",i=d(t.name)):t.content&&t.content.text&&(s="text",i=x(t)),`<li>${e} ${r} <span class="${s}">${i}</span></li>`})).join("")}</ul>\n    `}(g(m)));let k="";var B;$.length>0&&(B=g($),k=`\n      <h2>${n("Reactions")}</h2>\n      <ul class="reacts">${B.map((n=>w(n))).join("")}</ul>\n    `),t.innerHTML=`${b}${k}`}))}();
<script src="../static/webmention.js" data-page-url="https://warez.beesbuzz.biz/static/webmention-xss-test.html" async></script>