mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-21 09:38:01 +02:00
Desktop: Security: Fixes #6004: Prevent XSS in Goto Anything
This commit is contained in:
parent
e0bfa0dbe6
commit
810018b41f
@ -1927,6 +1927,9 @@ packages/renderer/headerAnchor.js.map
|
|||||||
packages/renderer/htmlUtils.d.ts
|
packages/renderer/htmlUtils.d.ts
|
||||||
packages/renderer/htmlUtils.js
|
packages/renderer/htmlUtils.js
|
||||||
packages/renderer/htmlUtils.js.map
|
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.d.ts
|
||||||
packages/renderer/index.js
|
packages/renderer/index.js
|
||||||
packages/renderer/index.js.map
|
packages/renderer/index.js.map
|
||||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1917,6 +1917,9 @@ packages/renderer/headerAnchor.js.map
|
|||||||
packages/renderer/htmlUtils.d.ts
|
packages/renderer/htmlUtils.d.ts
|
||||||
packages/renderer/htmlUtils.js
|
packages/renderer/htmlUtils.js
|
||||||
packages/renderer/htmlUtils.js.map
|
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.d.ts
|
||||||
packages/renderer/index.js
|
packages/renderer/index.js
|
||||||
packages/renderer/index.js.map
|
packages/renderer/index.js.map
|
||||||
|
@ -19,6 +19,7 @@ const { mergeOverlappingIntervals } = require('@joplin/lib/ArrayUtils.js');
|
|||||||
import markupLanguageUtils from '../utils/markupLanguageUtils';
|
import markupLanguageUtils from '../utils/markupLanguageUtils';
|
||||||
import focusEditorIfEditorCommand from '@joplin/lib/services/commands/focusEditorIfEditorCommand';
|
import focusEditorIfEditorCommand from '@joplin/lib/services/commands/focusEditorIfEditorCommand';
|
||||||
import Logger from '@joplin/lib/Logger';
|
import Logger from '@joplin/lib/Logger';
|
||||||
|
import { MarkupToHtml } from '@joplin/renderer';
|
||||||
|
|
||||||
const logger = Logger.create('GotoAnything');
|
const logger = Logger.create('GotoAnything');
|
||||||
|
|
||||||
@ -81,7 +82,7 @@ class Dialog extends React.PureComponent<Props, State> {
|
|||||||
private inputRef: any;
|
private inputRef: any;
|
||||||
private itemListRef: any;
|
private itemListRef: any;
|
||||||
private listUpdateIID_: any;
|
private listUpdateIID_: any;
|
||||||
private markupToHtml_: any;
|
private markupToHtml_: MarkupToHtml;
|
||||||
private userCallback_: any = null;
|
private userCallback_: any = null;
|
||||||
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
const urlUtils = require('./urlUtils.js');
|
const urlUtils = require('./urlUtils.js');
|
||||||
const Entities = require('html-entities').AllHtmlEntities;
|
const Entities = require('html-entities').AllHtmlEntities;
|
||||||
const htmlentities = new Entities().encode;
|
const htmlentities = new Entities().encode;
|
||||||
const htmlparser2 = require('@joplin/fork-htmlparser2');
|
|
||||||
const { escapeHtml } = require('./string-utils.js');
|
const { escapeHtml } = require('./string-utils.js');
|
||||||
|
|
||||||
// [\s\S] instead of . for multiline matching
|
// [\s\S] instead of . for multiline matching
|
||||||
@ -138,40 +137,6 @@ class HtmlUtils {
|
|||||||
return output.join(' ');
|
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();
|
export default new HtmlUtils();
|
||||||
|
32
packages/renderer/htmlUtils.test.ts
Normal file
32
packages/renderer/htmlUtils.test.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import htmlUtils from './htmlUtils';
|
||||||
|
|
||||||
|
describe('htmlUtils', () => {
|
||||||
|
|
||||||
|
test('should strip off HTML', () => {
|
||||||
|
const testCases = [
|
||||||
|
[
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<b>test</b>',
|
||||||
|
'test',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'Joplin®',
|
||||||
|
'Joplin®',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'<b>test</b>',
|
||||||
|
'<b>test</b>',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const t of testCases) {
|
||||||
|
const [input, expected] = t;
|
||||||
|
const actual = htmlUtils.stripHtml(input);
|
||||||
|
expect(actual).toBe(expected);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
@ -97,8 +97,7 @@ class HtmlUtils {
|
|||||||
return selfClosingElements.includes(tagName.toLowerCase());
|
return selfClosingElements.includes(tagName.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: copied from @joplin/lib
|
public stripHtml(html: string) {
|
||||||
stripHtml(html: string) {
|
|
||||||
const output: string[] = [];
|
const output: string[] = [];
|
||||||
|
|
||||||
const tagStack: string[] = [];
|
const tagStack: string[] = [];
|
||||||
@ -130,7 +129,14 @@ class HtmlUtils {
|
|||||||
parser.write(html);
|
parser.write(html);
|
||||||
parser.end();
|
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, '<');
|
||||||
}
|
}
|
||||||
|
|
||||||
public sanitizeHtml(html: string, options: any = null) {
|
public sanitizeHtml(html: string, options: any = null) {
|
||||||
|
Loading…
Reference in New Issue
Block a user