Browse Source

Escape title user inputs. Fix our version of CVE-2019-17625.

We take the simple/cheap way out and use ExtJS htmlEncode on all
entry points for this.

This is still mostly limited to 'doing it to yourself'.

The main fix is in app/view/main/Main.js where the title is rendered
out, and will apparently execute arbitrary javascript within a title
tag(!). This is an ExtJS thing, apparently, so we make it
unconditionally encode it to render on the bar.

Apparently this isn't the only place arbitrary execution can occur,
so just be safe(r).
pull/3202/head
TheGoddessInari 5 years ago
parent
commit
63ef9554dd
No known key found for this signature in database
GPG Key ID: 1209B1B7632D69A
  1. 8
      app/Application.js
  2. 2
      app/store/Services.js
  3. 4
      app/util/IconLoader.js
  4. 4
      app/util/Notifier.js
  5. 4
      app/ux/WebView.js
  6. 4
      app/view/add/Add.js
  7. 2
      app/view/add/AddController.js
  8. 3
      app/view/main/Main.js
  9. 12
      app/view/main/MainController.js
  10. 2
      app/view/preferences/Preferences.js

8
app/Application.js

@ -65,15 +65,15 @@ Ext.define('Hamsket.Application', {
newValue = parseInt(newValue);
if ( newValue > 0 ) {
if ( Ext.cq1('app-main').getActiveTab().record ) {
document.title = 'Hamsket (' + Hamsket.util.Format.formatNumber(newValue) + ') - '+Ext.cq1('app-main').getActiveTab().record.get('name');
document.title = `Hamsket (${Hamsket.util.Format.formatNumber(newValue)}) - ${Ext.String.htmlEncode(Ext.cq1('app-main').getActiveTab().record.get('name'))}`;
} else {
document.title = 'Hamsket (' + Hamsket.util.Format.formatNumber(newValue) + ')';
document.title = `Hamsket (${Hamsket.util.Format.formatNumber(newValue)})`;
}
} else {
if ( Ext.cq1('app-main') && Ext.cq1('app-main').getActiveTab().record ) {
document.title = 'Hamsket - '+Ext.cq1('app-main').getActiveTab().record.get('name');
document.title = `Hamsket - ${Ext.String.htmlEncode(Ext.cq1('app-main').getActiveTab().record.get('name'))}`;
} else {
document.title = 'Hamsket';
document.title = `Hamsket`;
}
}
}

2
app/store/Services.js

@ -32,7 +32,7 @@ Ext.define('Hamsket.store.Services', {
const cfg = {
xtype: 'webview'
,id: 'tab_'+service.get('id')
,title: service.get('name')
,title: Ext.String.htmlEncode(service.get('name'))
,icon: service.get('type') !== 'custom' ? 'resources/icons/'+service.get('logo') : ( service.get('logo') === '' ? 'resources/icons/custom.png' : service.get('logo'))
,src: service.get('url')
,type: service.get('type')

4
app/util/IconLoader.js

@ -39,8 +39,8 @@ Ext.define('Hamsket.util.IconLoader', {
return bg.slice(5, -2);
})();`).then(function (backgroundImage) {
if (backgroundImage) {
service.setTitle(`<img src="${backgroundImage}" width="" style="background-color: white;border-radius: 50%;position: absolute;left: 18px;top: 17px;width: 12px;">`+service.title);
service.fireEvent('iconchange', service, backgroundImage, service.icon);
service.setTitle(`<img src="${Ext.String.htmlEncode(backgroundImage)}" width="" style="background-color: white;border-radius: 50%;position: absolute;left: 18px;top: 17px;width: 12px;">${Ext.String.htmlEncode(service.title)}`);
service.fireEvent('iconchange', service, Ext.String.htmlEncode(backgroundImage), service.icon);
}
return true;
}

4
app/util/Notifier.js

@ -42,8 +42,8 @@ Ext.define('Hamsket.util.Notifier', {
this.dispatchNotification = function(view, count) {
const text = getNotificationText(view, count);
const notification = new Notification(view.record.get('name'), {
body: text,
const notification = new Notification(Ext.String.htmlEncode(view.record.get('name')), {
body: Ext.String.htmlEncode(text),
icon: view.icon,
silent: view.record.get('muted')
});

4
app/ux/WebView.js

@ -39,7 +39,7 @@ Ext.define('Hamsket.ux.WebView',{
Ext.apply(me, {
items: me.webViewConstructor()
,title: me.record.get('tabname') ? me.record.get('name') : ''
,title: me.record.get('tabname') ? Ext.String.htmlEncode(me.record.get('name')) : ''
,icon: me.record.get('type') === 'custom' ? (me.record.get('logo') === '' ? 'resources/icons/custom.png' : me.record.get('logo')) : 'resources/icons/'+me.record.get('logo')
,src: me.record.get('url')
,type: me.record.get('type')
@ -297,7 +297,7 @@ Ext.define('Hamsket.ux.WebView',{
if ( e.url.indexOf('slack.com/call/') >= 0 ) {
me.add({
xtype: 'window'
,title: e.options.title
,title: Ext.String.htmlEncode(e.options.title)
,width: e.options.width
,height: e.options.height
,maximizable: true

4
app/view/add/Add.js

@ -28,7 +28,7 @@ Ext.define('Hamsket.view.add.Add',{
,initComponent() {
const me = this;
me.title = (!me.edit ? locale['app.window[0]'] : locale['app.window[1]']) + ' ' + me.record.get('name');
me.title = `${(!me.edit ? locale['app.window[0]'] : locale['app.window[1]'])} ${Ext.String.htmlEncode(me.record.get('name'))}`;
me.icon = me.record.get('type') === 'custom' ? (!me.edit ? 'resources/icons/custom.png' : (me.record.get('logo') === '' ? 'resources/icons/custom.png' : me.record.get('logo'))) : 'resources/icons/'+me.record.get('logo');
me.items = [
{
@ -38,7 +38,7 @@ Ext.define('Hamsket.view.add.Add',{
xtype: 'textfield'
,fieldLabel: locale['app.window[2]']
,labelWidth: 40
,value: me.record.get('type') === 'custom' ? (me.edit ? me.record.get('name') : '') : me.record.get('name')
,value: me.record.get('type') === 'custom' ? (me.edit ? Ext.String.htmlEncode(me.record.get('name')) : '') : Ext.String.htmlEncode(me.record.get('name'))
,name: 'serviceName'
,allowBlank: true
,listeners: { specialkey: 'onEnter' }

2
app/view/add/AddController.js

@ -52,7 +52,7 @@ Ext.define('Hamsket.view.add.AddController', {
const view = Ext.getCmp('tab_'+win.record.get('id'));
// Change the title of the Tab
view.setTitle( formValues.tabname ? formValues.serviceName : '' );
view.setTitle( formValues.tabname ? Ext.String.htmlEncode(formValues.serviceName) : '' );
// Change sound of the Tab
view.setAudioMuted(formValues.muted);
// Change notifications of the Tab

3
app/view/main/Main.js

@ -171,7 +171,8 @@ Ext.define('Hamsket.view.main.Main', {
,editor: {
xtype: 'textfield'
,allowBlank: true
}
},
renderer: Ext.String.htmlEncode
}
,{
xtype: 'actioncolumn'

12
app/view/main/MainController.js

@ -44,9 +44,9 @@ Ext.define('Hamsket.view.main.MainController', {
// Update the main window so it includes the active tab title.
if ( Hamsket.app.getTotalNotifications() > 0 ) {
document.title = 'Hamsket ('+ Hamsket.app.getTotalNotifications() +') - ' + newTab.record.get('name');
document.title = `Hamsket (${Hamsket.app.getTotalNotifications()}) - ${Ext.String.htmlEncode(newTab.record.get('name'))}`;
} else {
document.title = 'Hamsket - ' + newTab.record.get('name');
document.title = `Hamsket - ${Ext.String.htmlEncode(newTab.record.get('name'))}`;
}
}
@ -90,7 +90,7 @@ Ext.define('Hamsket.view.main.MainController', {
e.record.commit();
// Change the title of the Tab
Ext.getCmp('tab_'+e.record.get('id')).setTitle(e.record.get('name'));
Ext.getCmp('tab_'+e.record.get('id')).setTitle(Ext.String.htmlEncode(e.record.get('name')));
}
,onEnableDisableService(cc, rowIndex, checked, obj, hideTab) {
@ -102,7 +102,7 @@ Ext.define('Hamsket.view.main.MainController', {
Ext.cq1('app-main').insert(rec.get('align') === 'left' ? rec.get('position') : rec.get('position')+1, {
xtype: 'webview'
,id: 'tab_'+rec.get('id')
,title: rec.get('name')
,title: `${Ext.String.htmlEncode(rec.get('name'))}`
,icon: rec.get('type') !== 'custom' ? 'resources/icons/'+rec.get('logo') : ( rec.get('logo') === '' ? 'resources/icons/custom.png' : rec.get('logo'))
,src: rec.get('url')
,type: rec.get('type')
@ -183,7 +183,7 @@ Ext.define('Hamsket.view.main.MainController', {
,removeService( gridView, rowIndex, colIndex, col, e, rec, rowEl ) {
const me = this;
Ext.Msg.confirm(locale['app.window[12]'], locale['app.window[13]']+' <b>'+rec.get('name')+'</b>?', function(btnId) {
Ext.Msg.confirm(locale['app.window[12]'], locale['app.window[13]']+' <b>'+Ext.String.htmlEncode(rec.get('name'))+'</b>?', function(btnId) {
if ( btnId === 'yes' ) {
Ext.Msg.wait('Please wait until we clear all.', 'Removing...');
me.removeServiceFn(rec.get('id'), 1, 1);
@ -281,7 +281,7 @@ Ext.define('Hamsket.view.main.MainController', {
fn(record) {
if ( record.get('type') === 'custom' ) return true;
if ( !Ext.Array.contains(Ext.Object.getKeys(cg.getValue()), record.get('type')) ) return false;
return record.get('name').toLowerCase().indexOf(newValue.toLowerCase()) > -1 ? true : false;
return Ext.String.htmlEncode(record.get('name')).toLowerCase().indexOf(newValue.toLowerCase()) > -1 ? true : false;
}
});
} else {

2
app/view/preferences/Preferences.js

@ -44,7 +44,7 @@ Ext.define('Hamsket.view.preferences.Preferences',{
Ext.getStore('Services').each(function(rec) {
defaultServiceOptions.push({
value: rec.get('id')
,label: rec.get('name')
,label: Ext.String.htmlEncode(rec.get('name'))
});
});

Loading…
Cancel
Save