Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit e47fb2e

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
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>
1 parent ee43a12 commit e47fb2e

File tree

3 files changed

+102
-2
lines changed

3 files changed

+102
-2
lines changed

‎arduino-ide-extension/src/browser/library/library-list-widget.ts‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { Installable } from '../../common/protocol';
2020
import { ListItemRenderer } from '../widgets/component-list/list-item-renderer';
2121
import { nls } from '@theia/core/lib/common';
2222
import { LibraryFilterRenderer } from '../widgets/component-list/filter-renderer';
23-
import { findChildTheiaButton } from '../utils/dom';
23+
import { findChildTheiaButton,splitByBoldTag } from '../utils/dom';
2424

2525
@injectable()
2626
export class LibraryListWidget extends ListWidget<
@@ -81,7 +81,7 @@ export class LibraryListWidget extends ListWidget<
8181
let installDependencies: boolean | undefined = undefined;
8282
if (dependencies.length) {
8383
const message = document.createElement('div');
84-
message.innerHTML =
84+
consttextContent =
8585
dependencies.length === 1
8686
? nls.localize(
8787
'arduino/library/needsOneDependency',
@@ -95,6 +95,22 @@ export class LibraryListWidget extends ListWidget<
9595
item.name,
9696
version
9797
);
98+
const segments = splitByBoldTag(textContent);
99+
if (!segments) {
100+
message.textContent = textContent;
101+
} else {
102+
segments.map((segment) => {
103+
const span = document.createElement('span');
104+
if (typeof segment === 'string') {
105+
span.textContent = segment;
106+
} else {
107+
const bold = document.createElement('b');
108+
bold.textContent = segment.textContent;
109+
span.appendChild(bold);
110+
}
111+
message.appendChild(span);
112+
});
113+
}
98114
const listContainer = document.createElement('div');
99115
listContainer.style.maxHeight = '300px';
100116
listContainer.style.overflowY = 'auto';

‎arduino-ide-extension/src/browser/utils/dom.ts‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,35 @@ export function findChildTheiaButton(
3535
function isHTMLElement(element: Element): element is HTMLElement {
3636
return element instanceof HTMLElement;
3737
}
38+
39+
type Segment = string | { textContent: string; bold: true };
40+
/**
41+
* Returns with an array of `Segments` by splitting raw HTML text on the `<b></b>` groups. If splitting is not possible, returns `undefined`.
42+
* 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.
43+
*/
44+
export function splitByBoldTag(text: string): Segment[] | undefined {
45+
const matches = text.matchAll(new RegExp(/<\s*b[^>]*>(.*?)<\s*\/\s*b>/gm));
46+
if (!matches) {
47+
return undefined;
48+
}
49+
const segments: Segment[] = [];
50+
const textLength = text.length;
51+
let processedLength = 0;
52+
for (const match of matches) {
53+
const { index } = match;
54+
if (typeof index === 'number') {
55+
if (!segments.length && index) {
56+
segments.push(text.substring(0, index));
57+
}
58+
if (processedLength > 0) {
59+
segments.push(text.substring(processedLength, index));
60+
}
61+
segments.push({ textContent: match[1], bold: true });
62+
processedLength = index + match[0].length;
63+
}
64+
}
65+
if (segments.length && textLength > processedLength) {
66+
segments.push(text.substring(processedLength));
67+
}
68+
return segments.length ? segments : undefined;
69+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { splitByBoldTag } from '../../browser/utils/dom';
2+
import { expect } from 'chai';
3+
4+
describe('dom', () => {
5+
describe('splitByBoldTag', () => {
6+
it('should split by bold tags', () => {
7+
const actual = splitByBoldTag('one<b>matchOne</b>two');
8+
const expected = ['one', { textContent: 'matchOne', bold: true }, 'two'];
9+
expect(actual).to.be.deep.equal(expected);
10+
});
11+
12+
it('should handle starting bold tags', () => {
13+
const actual = splitByBoldTag(
14+
'<b>matchOne</b>one<b>matchTwo</b> two <b>matchThree</b> three'
15+
);
16+
const expected = [
17+
{ textContent: 'matchOne', bold: true },
18+
'one',
19+
{ textContent: 'matchTwo', bold: true },
20+
' two ',
21+
{ textContent: 'matchThree', bold: true },
22+
' three',
23+
];
24+
expect(actual).to.be.deep.equal(expected);
25+
});
26+
27+
it('should handle unclosed bold tags', () => {
28+
const actual = splitByBoldTag(
29+
'<b>matchOne</b>one<b>matchTwo</b> two <b>matchThree</b> three <b> '
30+
);
31+
const expected = [
32+
{ textContent: 'matchOne', bold: true },
33+
'one',
34+
{ textContent: 'matchTwo', bold: true },
35+
' two ',
36+
{ textContent: 'matchThree', bold: true },
37+
' three <b> ',
38+
];
39+
expect(actual).to.be.deep.equal(expected);
40+
});
41+
42+
it('should handle no matches', () => {
43+
const actual = splitByBoldTag('<b>alma');
44+
expect(actual).to.be.undefined;
45+
});
46+
47+
it('should handle empty strings', () => {
48+
const actual = splitByBoldTag('');
49+
expect(actual).to.be.undefined;
50+
});
51+
});
52+
});

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /