diff --git a/tgui/packages/tgui-panel/chat/middleware.js b/tgui/packages/tgui-panel/chat/middleware.js
index a33bbef2866..88a896b9920 100644
--- a/tgui/packages/tgui-panel/chat/middleware.js
+++ b/tgui/packages/tgui-panel/chat/middleware.js
@@ -4,6 +4,7 @@
* @license MIT
*/
+import DOMPurify from 'dompurify';
import { storage } from 'common/storage';
import { loadSettings, updateSettings } from '../settings/actions';
import { selectSettings } from '../settings/selectors';
@@ -13,6 +14,14 @@ import { createMessage, serializeMessage } from './model';
import { chatRenderer } from './renderer';
import { selectChat, selectCurrentChatPage } from './selectors';
+// List of blacklisted tags
+const FORBID_TAGS = [
+ 'a',
+ 'iframe',
+ 'link',
+ 'video',
+];
+
const saveChatToStorage = async store => {
const state = selectChat(store.getState());
const fromIndex = Math.max(0,
@@ -35,6 +44,13 @@ const loadChatFromStorage = async store => {
return;
}
if (messages) {
+ for (let message of messages) {
+ if (message.html) {
+ message.html = DOMPurify.sanitize(message.html, {
+ FORBID_TAGS,
+ });
+ }
+ }
const batch = [
...messages,
createMessage({
diff --git a/tgui/packages/tgui-panel/package.json b/tgui/packages/tgui-panel/package.json
index 8a2e2bd75e7..d3109af6f2c 100644
--- a/tgui/packages/tgui-panel/package.json
+++ b/tgui/packages/tgui-panel/package.json
@@ -4,6 +4,7 @@
"version": "4.2.0",
"dependencies": {
"common": "workspace:*",
+ "dompurify": "^2.0.12",
"inferno": "^7.4.2",
"tgui": "workspace:*",
"tgui-dev-server": "workspace:*",
diff --git a/tgui/packages/tgui/sanitize.js b/tgui/packages/tgui/sanitize.js
index f6dee1d281d..6a8eb8b5e7a 100644
--- a/tgui/packages/tgui/sanitize.js
+++ b/tgui/packages/tgui/sanitize.js
@@ -6,10 +6,26 @@ import DOMPurify from 'dompurify';
// Default values
let defTag = [
- 'br', 'code', 'li', 'p', 'pre',
- 'span', 'table', 'td', 'tr', 'i',
- 'th', 'ul', 'ol', 'menu', 'font', 'b',
- 'center', 'table', 'tr', 'th', 'hr',
+ 'b',
+ 'br',
+ 'center',
+ 'code',
+ 'div',
+ 'font',
+ 'hr',
+ 'i',
+ 'li',
+ 'menu',
+ 'ol',
+ 'p',
+ 'pre',
+ 'span',
+ 'table',
+ 'td',
+ 'th',
+ 'tr',
+ 'u',
+ 'ul',
];
let defAttr = ['class', 'style'];
diff --git a/tgui/public/tgui-common.chunk.js b/tgui/public/tgui-common.chunk.js
index b07610f0bcb..627e5c8c9c9 100644
--- a/tgui/public/tgui-common.chunk.js
+++ b/tgui/public/tgui-common.chunk.js
@@ -1 +1 @@
-(window.webpackJsonp=window.webpackJsonp||[]).push([[0],[function(e,t,n){"use strict";t.__esModule=!0;var r=n(451);Object.keys(r).forEach((function(e){"default"!==e&&"__esModule"!==e&&(t[e]=r[e])}))},function(e,t,n){"use strict";t.__esModule=!0,t.TimeDisplay=t.Tooltip=t.Tabs=t.TextArea=t.Table=t.Slider=t.Section=t.RoundGauge=t.ProgressBar=t.NumberInput=t.NoticeBox=t.Modal=t.LabeledList=t.LabeledControls=t.Knob=t.Input=t.Icon=t.Grid=t.Flex=t.Dropdown=t.DraggableControl=t.Divider=t.Dimmer=t.ColorBox=t.Collapsible=t.Chart=t.ByondUi=t.Button=t.Box=t.BlockQuote=t.Blink=t.AnimatedNumber=void 0;var r=n(102);t.AnimatedNumber=r.AnimatedNumber;var o=n(466);t.Blink=o.Blink;var i=n(467);t.BlockQuote=i.BlockQuote;var a=n(18);t.Box=a.Box;var u=n(193);t.Button=u.Button;var c=n(469);t.ByondUi=c.ByondUi;var s=n(471);t.Chart=s.Chart;var l=n(472);t.Collapsible=l.Collapsible;var f=n(473);t.ColorBox=f.ColorBox;var d=n(195);t.Dimmer=d.Dimmer;var p=n(196);t.Divider=p.Divider;var h=n(138);t.DraggableControl=h.DraggableControl;var g=n(474);t.Dropdown=g.Dropdown;var v=n(139);t.Flex=v.Flex;var m=n(475);t.Grid=m.Grid;var y=n(103);t.Icon=y.Icon;var b=n(198);t.Input=b.Input;var x=n(476);t.Knob=x.Knob;var w=n(477);t.LabeledControls=w.LabeledControls;var _=n(199);t.LabeledList=_.LabeledList;var k=n(478);t.Modal=k.Modal;var E=n(479);t.NoticeBox=E.NoticeBox;var S=n(140);t.NumberInput=S.NumberInput;var C=n(480);t.ProgressBar=C.ProgressBar;var N=n(481);t.RoundGauge=N.RoundGauge;var A=n(482);t.Section=A.Section;var T=n(483);t.Slider=T.Slider;var O=n(197);t.Table=O.Table;var I=n(484);t.TextArea=I.TextArea;var M=n(485);t.Tabs=M.Tabs;var L=n(194);t.Tooltip=L.Tooltip;var V=n(486);t.TimeDisplay=V.TimeDisplay},function(e,t,n){"use strict";(function(e){t.__esModule=!0,t.useSharedState=t.useLocalState=t.useBackend=t.selectBackend=t.sendAct=t.sendMessage=t.backendMiddleware=t.backendReducer=t.backendSuspendSuccess=t.backendSuspendStart=t.backendSetSharedState=t.backendUpdate=void 0;var r=n(99),o=n(190),i=n(191),a=n(35),u=n(135);var c=(0,a.createLogger)("backend"),s=function(e){return{type:"backend/update",payload:e}};t.backendUpdate=s;var l=function(e,t){return{type:"backend/setSharedState",payload:{key:e,nextState:t}}};t.backendSetSharedState=l;t.backendSuspendStart=function(){return{type:"backend/suspendStart"}};var f=function(){return{type:"backend/suspendSuccess",payload:{timestamp:Date.now()}}};t.backendSuspendSuccess=f;var d={config:{},data:{},shared:{},suspended:Date.now(),suspending:!1};t.backendReducer=function(e,t){void 0===e&&(e=d);var n=t.type,r=t.payload;if("backend/update"===n){var o=Object.assign({},e.config,r.config),i=Object.assign({},e.data,r.static_data,r.data),a=Object.assign({},e.shared);if(r.shared)for(var u=0,c=Object.keys(r.shared);u "+e+"
/gi,"\n").replace(/<\/?[a-z0-9-_]+[^>]*>/gi,"").replace(/&(nbsp|amp|quot|lt|gt|apos);/g,(function(e,n){return t[n]})).replace(/?([0-9]+);/gi,(function(e,t){var n=parseInt(t,10);return String.fromCharCode(n)})).replace(/?([0-9a-f]+);/gi,(function(e,t){var n=parseInt(t,16);return String.fromCharCode(n)}))};t.buildQueryString=function(e){return Object.keys(e).map((function(t){return encodeURIComponent(t)+"="+encodeURIComponent(e[t])})).join("&")}},function(e,t,n){"use strict";t.__esModule=!0,t.Box=t.computeBoxClassName=t.computeBoxProps=t.halfUnit=t.unit=void 0;var r=n(6),o=n(0),i=n(468),a=n(42);var u=function(e){return"string"==typeof e?e.endsWith("px")&&!Byond.IS_LTE_IE8?parseFloat(e)/12+"rem":e:"number"==typeof e?Byond.IS_LTE_IE8?12*e+"px":e+"rem":void 0};t.unit=u;var c=function(e){return"string"==typeof e?u(e):"number"==typeof e?u(.5*e):void 0};t.halfUnit=c;var s=function(e){return"string"==typeof e&&a.CSS_COLORS.includes(e)},l=function(e){return function(t,n){"number"!=typeof n&&"string"!=typeof n||(t[e]=n)}},f=function(e,t){return function(n,r){"number"!=typeof r&&"string"!=typeof r||(n[e]=t(r))}},d=function(e,t){return function(n,r){r&&(n[e]=t)}},p=function(e,t,n){return function(r,o){if("number"==typeof o||"string"==typeof o)for(var i=0;ic||n!=n?l*Infinity:l*n}},function(e,t,n){"use strict";var r=n(4),o=Math.hypot,i=Math.abs,a=Math.sqrt;r({target:"Math",stat:!0,forced:!!o&&o(Infinity,NaN)!==Infinity},{hypot:function(e,t){for(var n,r,o=0,u=0,c=arguments.length,s=0;u
\n":"'+(n?e:ee(e,!0))+"
\n"}return e}(),t.blockquote=function(){function e(e){return""+(n?e:ee(e,!0))+"\n"+e+"
\n"}return e}(),t.html=function(){function e(e){return e}return e}(),t.heading=function(){function e(e,t,n,r){return this.options.headerIds?"
\n":"
\n"}return e}(),t.list=function(){function e(e,t,n){var r=t?"ol":"ul";return"<"+r+(t&&1!==n?' start="'+n+'"':"")+">\n"+e+""+r+">\n"}return e}(),t.listitem=function(){function e(e){return"\n\n"+e+"\n"+t+"
\n"}return e}(),t.tablerow=function(){function e(e){return"\n"+e+" \n"}return e}(),t.tablecell=function(){function e(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' align="'+t.align+'">':"<"+n+">")+e+""+n+">\n"}return e}(),t.strong=function(){function e(e){return""+e+""}return e}(),t.em=function(){function e(e){return""+e+""}return e}(),t.codespan=function(){function e(e){return""+e+""}return e}(),t.br=function(){function e(){return this.options.xhtml?"
":"
"}return e}(),t.del=function(){function e(e){return""+e+""}return e}(),t.link=function(){function e(e,t,n){if(null===(e=J(this.options.sanitize,this.options.baseUrl,e)))return n;var r='"+n+""}return e}(),t.image=function(){function e(e,t,n){if(null===(e=J(this.options.sanitize,this.options.baseUrl,e)))return n;var r='":">"}return e}(),t.text=function(){function e(e){return e}return e}(),e}(),ne=function(){function e(){}var t=e.prototype;return t.strong=function(){function e(e){return e}return e}(),t.em=function(){function e(e){return e}return e}(),t.codespan=function(){function e(e){return e}return e}(),t.del=function(){function e(e){return e}return e}(),t.html=function(){function e(e){return e}return e}(),t.text=function(){function e(e){return e}return e}(),t.link=function(){function e(e,t,n){return""+n}return e}(),t.image=function(){function e(e,t,n){return""+n}return e}(),t.br=function(){function e(){return""}return e}(),e}(),re=function(){function e(){this.seen={}}return e.prototype.slug=function(){function e(e){var t=e.toLowerCase().trim().replace(/<[!\/a-z].*?>/gi,"").replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g,"").replace(/\s/g,"-");if(this.seen.hasOwnProperty(t)){var n=t;do{this.seen[n]++,t=n+"-"+this.seen[n]}while(this.seen.hasOwnProperty(t))}return this.seen[t]=0,t}return e}(),e}(),oe=a.defaults,ie=I.unescape,ae=function(){function e(e){this.options=e||oe,this.options.renderer=this.options.renderer||new te,this.renderer=this.options.renderer,this.renderer.options=this.options,this.textRenderer=new ne,this.slugger=new re}e.parse=function(){function t(t,n){return new e(n).parse(t)}return t}();var t=e.prototype;return t.parse=function(){function e(e,t){void 0===t&&(t=!0);var n,r,o,i,a,u,c,s,l,f,d,p,h,g,v,m,y,b,x="",w=e.length;for(n=0;n
"+se(c.message+"",!0)+"";throw c}}return pe.options=pe.setOptions=function(e){return ue(pe.defaults,e),fe(pe.defaults),pe},pe.getDefaults=le,pe.defaults=de,pe.use=function(e){var t=ue({},e);if(e.renderer&&function(){var n=pe.defaults.renderer||new te,r=function(){function t(t){var r=n[t];n[t]=function(){for(var o=arguments.length,i=new Array(o),a=0;a
'+(n?e:ee(e,!0))+"\n":""+(n?e:ee(e,!0))+"\n"}return e}(),t.blockquote=function(){function e(e){return"\n"+e+"\n"}return e}(),t.html=function(){function e(e){return e}return e}(),t.heading=function(){function e(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"}return e}(),t.table=function(){function e(e,t){return t&&(t=""+t+""),""+e+""}return e}(),t.br=function(){function e(){return this.options.xhtml?""+se(c.message+"",!0)+"";throw c}}return pe.options=pe.setOptions=function(e){return ue(pe.defaults,e),fe(pe.defaults),pe},pe.getDefaults=le,pe.defaults=de,pe.use=function(e){var t=ue({},e);if(e.renderer&&function(){var n=pe.defaults.renderer||new te,r=function(){function t(t){var r=n[t];n[t]=function(){for(var o=arguments.length,i=new Array(o),a=0;a