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:
Akos Kitta
2023-05-11 14:55:46 +02:00
committed by Akos Kitta
parent ee43a12eb7
commit e47fb2e651
3 changed files with 102 additions and 2 deletions

View File

@@ -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';

View File

@@ -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;
}