Skip to content

Commit

Permalink
Desktop: Security: Fixes #6004: Prevent XSS in Goto Anything
Browse files Browse the repository at this point in the history
  • Loading branch information
laurent22 committed Jan 15, 2022
1 parent e0bfa0d commit 810018b
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 39 deletions.
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -1927,6 +1927,9 @@ packages/renderer/headerAnchor.js.map
packages/renderer/htmlUtils.d.ts
packages/renderer/htmlUtils.js
packages/renderer/htmlUtils.js.map
packages/renderer/htmlUtils.test.d.ts
packages/renderer/htmlUtils.test.js
packages/renderer/htmlUtils.test.js.map
packages/renderer/index.d.ts
packages/renderer/index.js
packages/renderer/index.js.map
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -1917,6 +1917,9 @@ packages/renderer/headerAnchor.js.map
packages/renderer/htmlUtils.d.ts
packages/renderer/htmlUtils.js
packages/renderer/htmlUtils.js.map
packages/renderer/htmlUtils.test.d.ts
packages/renderer/htmlUtils.test.js
packages/renderer/htmlUtils.test.js.map
packages/renderer/index.d.ts
packages/renderer/index.js
packages/renderer/index.js.map
Expand Down
3 changes: 2 additions & 1 deletion packages/app-desktop/plugins/GotoAnything.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const { mergeOverlappingIntervals } = require('@joplin/lib/ArrayUtils.js');
import markupLanguageUtils from '../utils/markupLanguageUtils';
import focusEditorIfEditorCommand from '@joplin/lib/services/commands/focusEditorIfEditorCommand';
import Logger from '@joplin/lib/Logger';
import { MarkupToHtml } from '@joplin/renderer';

const logger = Logger.create('GotoAnything');

Expand Down Expand Up @@ -81,7 +82,7 @@ class Dialog extends React.PureComponent<Props, State> {
private inputRef: any;
private itemListRef: any;
private listUpdateIID_: any;
private markupToHtml_: any;
private markupToHtml_: MarkupToHtml;
private userCallback_: any = null;

constructor(props: Props) {
Expand Down
35 changes: 0 additions & 35 deletions packages/lib/htmlUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const urlUtils = require('./urlUtils.js');
const Entities = require('html-entities').AllHtmlEntities;
const htmlentities = new Entities().encode;
const htmlparser2 = require('@joplin/fork-htmlparser2');
const { escapeHtml } = require('./string-utils.js');

// [\s\S] instead of . for multiline matching
Expand Down Expand Up @@ -138,40 +137,6 @@ class HtmlUtils {
return output.join(' ');
}

public stripHtml(html: string) {
const output: string[] = [];

const tagStack: any[] = [];

const currentTag = () => {
if (!tagStack.length) return '';
return tagStack[tagStack.length - 1];
};

const disallowedTags = ['script', 'style', 'head', 'iframe', 'frameset', 'frame', 'object', 'base'];

const parser = new htmlparser2.Parser({

onopentag: (name: string) => {
tagStack.push(name.toLowerCase());
},

ontext: (decodedText: string) => {
if (disallowedTags.includes(currentTag())) return;
output.push(decodedText);
},

onclosetag: (name: string) => {
if (currentTag() === name.toLowerCase()) tagStack.pop();
},

}, { decodeEntities: true });

parser.write(html);
parser.end();

return output.join('').replace(/\s+/g, ' ');
}
}

export default new HtmlUtils();
Expand Down
32 changes: 32 additions & 0 deletions packages/renderer/htmlUtils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import htmlUtils from './htmlUtils';

describe('htmlUtils', () => {

test('should strip off HTML', () => {
const testCases = [
[
'',
'',
],
[
'<b>test</b>',
'test',
],
[
'Joplin&circledR;',
'Joplin®',
],
[
'&lt;b&gttest&lt;/b&gt',
'&lt;b>test&lt;/b>',
],
];

for (const t of testCases) {
const [input, expected] = t;
const actual = htmlUtils.stripHtml(input);
expect(actual).toBe(expected);
}
});

});
12 changes: 9 additions & 3 deletions packages/renderer/htmlUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,7 @@ class HtmlUtils {
return selfClosingElements.includes(tagName.toLowerCase());
}

// TODO: copied from @joplin/lib
stripHtml(html: string) {
public stripHtml(html: string) {
const output: string[] = [];

const tagStack: string[] = [];
Expand Down Expand Up @@ -130,7 +129,14 @@ class HtmlUtils {
parser.write(html);
parser.end();

return output.join('').replace(/\s+/g, ' ');
// In general, we want to get back plain text from this function, so all
// HTML entities are decoded. Howver, to prevent XSS attacks, we
// re-encode all the "<" characters, which should break any attempt to
// inject HTML tags.

return output.join('')
.replace(/\s+/g, ' ')
.replace(/</g, '&lt;');
}

public sanitizeHtml(html: string, options: any = null) {
Expand Down

0 comments on commit 810018b

Please sign in to comment.