Skip to content
Snippets Groups Projects
Commit 774a57f8 authored by Bartłomiej Górnicki's avatar Bartłomiej Górnicki
Browse files

fix: sanitizer does not force `href` attribute for `<a>` tag

parent 71c66ed7
No related branches found
No related tags found
1 merge request!557Move hive renderer to internal packages
......@@ -81,7 +81,7 @@ describe('DefaultRender', () => {
{
name: 'Allow for anchor id tags',
raw: "<a id='anchor'></a>",
expected: '<p><a href="#" id="anchor" class="hive-test external"></a></p>'
expected: '<p><a id="anchor" class="hive-test"></a></p>'
},
{
name: 'Allows links embedded via <a> tags with additional class added when condition is matching',
......@@ -91,7 +91,7 @@ describe('DefaultRender', () => {
{
name: 'Should remove additional unsafe attributes from a tag',
raw: "<a fake='test'></a>",
expected: '<p><a href="#" class="hive-test external"></a></p>'
expected: '<p><a class="hive-test"></a></p>'
}
];
......
import {expect} from 'chai';
import {Localization} from '../LocalizationOptions';
import {TagTransformingSanitizer} from './TagTransformingSanitizer';
const options = {
iframeWidth: 100,
iframeHeight: 100,
addNofollowToLinks: false,
addTargetBlankToLinks: false,
cssClassForInternalLinks: 'internal',
cssClassForExternalLinks: 'external',
noImage: false,
isLinkSafeFn: (url: string) => url != 'https://unsafe.com',
addExternalCssClassToMatchingLinksFn: (url: string) => url != 'https://engrave.dev'
};
describe('TagTransformingSanitizer', () => {
describe('a', () => {
[
{
description: 'should add internal class to internal links',
input: '<a href="https://engrave.dev">Example</a>',
expected: '<a href="https://engrave.dev" class="internal">Example</a>'
},
{
description: 'should add external class to external links',
input: '<a href="https://example.com">Example</a>',
expected: '<a href="https://example.com" class="external">Example</a>'
},
{
description: 'should remove unwanted attributes',
input: '<a href="https://engrave.dev" media="all">engrave.dev</a>',
expected: '<a href="https://engrave.dev" class="internal">engrave.dev</a>'
},
{
description: 'should allow anchor links with no href',
input: '<a id="anchor">Example</a>',
expected: '<a id="anchor" class="internal">Example</a>'
},
{
description: 'should trim href',
input: '<a href=" https://engrave.dev ">Example</a>',
expected: '<a href="https://engrave.dev" class="internal">Example</a>'
},
{
description: 'should mark unsafe links as potentially phishing',
input: '<a href="https://unsafe.com">Example</a>',
expected:
'<a href="https://unsafe.com" rel="noopener" title="Link expanded to plain text; beware of a potential phishing attempt" target="_self" class="external">Example</a>'
},
{
description: 'should not allow invalid schemes',
input: '<a href="ptth://engrave.dev">Example</a>',
expected: '<a class="external">Example</a>'
}
].forEach(({description, expected, input}) => {
it(description, () => {
const sanitizer = new TagTransformingSanitizer(options, Localization.DEFAULT);
const sanitized = sanitizer.sanitize(input);
expect(sanitized).to.be.equal(expected);
});
});
});
});
......@@ -140,20 +140,18 @@ export class TagTransformingSanitizer {
return retTag;
},
a: (tagName, attribs) => {
const attys: sanitize.Attributes = {...attribs};
let {href} = attribs;
if (!href) {
href = '#';
if (href) {
href = href.trim();
attys.href = href;
}
href = href.trim();
const attys: sanitize.Attributes = {...attribs, href};
// If it's not a (relative or absolute) URL...
if (!this.options.isLinkSafeFn(href)) {
// attys.target = '_blank' // pending iframe impl https://mathiasbynens.github.io/rel-noopener/
if (href && !this.options.isLinkSafeFn(href)) {
attys.rel = this.options.addNofollowToLinks ? 'nofollow noopener' : 'noopener';
attys.title = this.localization.phishingWarning;
attys.target = this.options.addTargetBlankToLinks ? '_blank' : '_self';
}
if (this.options.addExternalCssClassToMatchingLinksFn(href)) {
if (href && this.options.addExternalCssClassToMatchingLinksFn(href)) {
attys.class = this.options.cssClassForExternalLinks ? this.options.cssClassForExternalLinks : '';
} else {
attys.class = this.options.cssClassForInternalLinks ? this.options.cssClassForInternalLinks : '';
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment