Skip to content

Commit b902612

Browse files
authored
Prevent links from being rendered in user bios (#13643)
* Prevent links from being rendered in user bios
1 parent 65ac1f6 commit b902612

File tree

4 files changed

+38
-5
lines changed

4 files changed

+38
-5
lines changed

src/amo/pages/UserProfile/index.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,10 @@ export class UserProfileBase extends React.Component<InternalProps> {
416416
>
417417
<div
418418
// eslint-disable-next-line react/no-danger
419-
dangerouslySetInnerHTML={sanitizeUserHTML(user.biography)}
419+
dangerouslySetInnerHTML={sanitizeUserHTML(
420+
user.biography,
421+
{ allowLinks: false },
422+
)}
420423
/>
421424
</Definition>
422425
) : null}

src/amo/utils/index.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,11 @@ export function nl2br(text: ?string): string {
220220
* Developer Hub when you hover over the *Some HTML Supported* link under
221221
* the textarea field.
222222
*/
223-
export function sanitizeUserHTML(text: ?string): {| __html: string |} {
224-
return sanitizeHTML(nl2br(text), [
225-
'a',
223+
export function sanitizeUserHTML(
224+
text: ?string,
225+
{ allowLinks = true }: { allowLinks: boolean } = {},
226+
): {| __html: string |} {
227+
const allowTags = [
226228
'abbr',
227229
'acronym',
228230
'b',
@@ -235,7 +237,11 @@ export function sanitizeUserHTML(text: ?string): {| __html: string |} {
235237
'ol',
236238
'strong',
237239
'ul',
238-
]);
240+
];
241+
if (allowLinks === true) {
242+
allowTags.unshift('a');
243+
}
244+
return sanitizeHTML(nl2br(text), allowTags);
239245
}
240246

241247
export function isAddonAuthor({

tests/unit/amo/pages/TestUserProfile.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,22 @@ describe(__filename, () => {
379379
).toHaveTextContent(biographyText);
380380
});
381381

382+
it('does not render links in biography', () => {
383+
const biographyText = 'Veni vidi vici';
384+
const biography = `<a href="//example.com"><b>${biographyText}</b></a>`;
385+
signInUserAndRenderUserProfile({ biography });
386+
387+
expect(screen.getByText('Biography')).toBeInTheDocument();
388+
expect(screen.getByClassName('UserProfile-biography')).toHaveTextContent(
389+
biographyText,
390+
);
391+
expect(
392+
within(screen.getByClassName('UserProfile-biography')).queryByTagName(
393+
'a',
394+
),
395+
).not.toBeInTheDocument();
396+
});
397+
382398
it('omits a null biography', () => {
383399
signInUserAndRenderUserProfile({ biography: null });
384400

tests/unit/amo/utils/test_index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,14 @@ describe(__filename, () => {
672672
expect(sanitize(customHtml)).toEqual(customHtml);
673673
});
674674

675+
it('can disallow links', () => {
676+
const customHtml =
677+
'<b>check</b> <i>out</i> <a href="http://mysite">my site</a>';
678+
expect(sanitize(customHtml, { allowLinks: false })).toEqual(
679+
'<b>check</b> <i>out</i> my site',
680+
);
681+
});
682+
675683
it('does not allow certain tags', () => {
676684
expect(
677685
sanitize('<b>my add-on</b> <script>alert("does XSS")</script>'),

0 commit comments

Comments
 (0)