mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-04-19 12:57:17 +00:00
fix: remove setting unsafe innerHTML
As it is vulnerable to stored Cross-Site Scripting. Ref: PNX-3669 Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
parent
ee43a12eb7
commit
e47fb2e651
@ -20,7 +20,7 @@ import { Installable } from '../../common/protocol';
|
||||
import { ListItemRenderer } from '../widgets/component-list/list-item-renderer';
|
||||
import { nls } from '@theia/core/lib/common';
|
||||
import { LibraryFilterRenderer } from '../widgets/component-list/filter-renderer';
|
||||
import { findChildTheiaButton } from '../utils/dom';
|
||||
import { findChildTheiaButton, splitByBoldTag } from '../utils/dom';
|
||||
|
||||
@injectable()
|
||||
export class LibraryListWidget extends ListWidget<
|
||||
@ -81,7 +81,7 @@ export class LibraryListWidget extends ListWidget<
|
||||
let installDependencies: boolean | undefined = undefined;
|
||||
if (dependencies.length) {
|
||||
const message = document.createElement('div');
|
||||
message.innerHTML =
|
||||
const textContent =
|
||||
dependencies.length === 1
|
||||
? nls.localize(
|
||||
'arduino/library/needsOneDependency',
|
||||
@ -95,6 +95,22 @@ export class LibraryListWidget extends ListWidget<
|
||||
item.name,
|
||||
version
|
||||
);
|
||||
const segments = splitByBoldTag(textContent);
|
||||
if (!segments) {
|
||||
message.textContent = textContent;
|
||||
} else {
|
||||
segments.map((segment) => {
|
||||
const span = document.createElement('span');
|
||||
if (typeof segment === 'string') {
|
||||
span.textContent = segment;
|
||||
} else {
|
||||
const bold = document.createElement('b');
|
||||
bold.textContent = segment.textContent;
|
||||
span.appendChild(bold);
|
||||
}
|
||||
message.appendChild(span);
|
||||
});
|
||||
}
|
||||
const listContainer = document.createElement('div');
|
||||
listContainer.style.maxHeight = '300px';
|
||||
listContainer.style.overflowY = 'auto';
|
||||
|
@ -35,3 +35,35 @@ export function findChildTheiaButton(
|
||||
function isHTMLElement(element: Element): element is HTMLElement {
|
||||
return element instanceof HTMLElement;
|
||||
}
|
||||
|
||||
type Segment = string | { textContent: string; bold: true };
|
||||
/**
|
||||
* Returns with an array of `Segments` by splitting raw HTML text on the `<b></b>` groups. If splitting is not possible, returns `undefined`.
|
||||
* Example: `one<b>two</b>three<b>four</b>five` will provide an five element length array. Where the 1<sup>st</sup> and 3<sup>rd</sup> elements are objects and the rest are string.
|
||||
*/
|
||||
export function splitByBoldTag(text: string): Segment[] | undefined {
|
||||
const matches = text.matchAll(new RegExp(/<\s*b[^>]*>(.*?)<\s*\/\s*b>/gm));
|
||||
if (!matches) {
|
||||
return undefined;
|
||||
}
|
||||
const segments: Segment[] = [];
|
||||
const textLength = text.length;
|
||||
let processedLength = 0;
|
||||
for (const match of matches) {
|
||||
const { index } = match;
|
||||
if (typeof index === 'number') {
|
||||
if (!segments.length && index) {
|
||||
segments.push(text.substring(0, index));
|
||||
}
|
||||
if (processedLength > 0) {
|
||||
segments.push(text.substring(processedLength, index));
|
||||
}
|
||||
segments.push({ textContent: match[1], bold: true });
|
||||
processedLength = index + match[0].length;
|
||||
}
|
||||
}
|
||||
if (segments.length && textLength > processedLength) {
|
||||
segments.push(text.substring(processedLength));
|
||||
}
|
||||
return segments.length ? segments : undefined;
|
||||
}
|
||||
|
52
arduino-ide-extension/src/test/browser/dom.test.ts
Normal file
52
arduino-ide-extension/src/test/browser/dom.test.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { splitByBoldTag } from '../../browser/utils/dom';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('dom', () => {
|
||||
describe('splitByBoldTag', () => {
|
||||
it('should split by bold tags', () => {
|
||||
const actual = splitByBoldTag('one<b>matchOne</b>two');
|
||||
const expected = ['one', { textContent: 'matchOne', bold: true }, 'two'];
|
||||
expect(actual).to.be.deep.equal(expected);
|
||||
});
|
||||
|
||||
it('should handle starting bold tags', () => {
|
||||
const actual = splitByBoldTag(
|
||||
'<b>matchOne</b>one<b>matchTwo</b> two <b>matchThree</b> three'
|
||||
);
|
||||
const expected = [
|
||||
{ textContent: 'matchOne', bold: true },
|
||||
'one',
|
||||
{ textContent: 'matchTwo', bold: true },
|
||||
' two ',
|
||||
{ textContent: 'matchThree', bold: true },
|
||||
' three',
|
||||
];
|
||||
expect(actual).to.be.deep.equal(expected);
|
||||
});
|
||||
|
||||
it('should handle unclosed bold tags', () => {
|
||||
const actual = splitByBoldTag(
|
||||
'<b>matchOne</b>one<b>matchTwo</b> two <b>matchThree</b> three <b> '
|
||||
);
|
||||
const expected = [
|
||||
{ textContent: 'matchOne', bold: true },
|
||||
'one',
|
||||
{ textContent: 'matchTwo', bold: true },
|
||||
' two ',
|
||||
{ textContent: 'matchThree', bold: true },
|
||||
' three <b> ',
|
||||
];
|
||||
expect(actual).to.be.deep.equal(expected);
|
||||
});
|
||||
|
||||
it('should handle no matches', () => {
|
||||
const actual = splitByBoldTag('<b>alma');
|
||||
expect(actual).to.be.undefined;
|
||||
});
|
||||
|
||||
it('should handle empty strings', () => {
|
||||
const actual = splitByBoldTag('');
|
||||
expect(actual).to.be.undefined;
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user