Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
818da6a
event handlers function added
robonetphy Jul 16, 2022
52467f0
santization config added
robonetphy Jul 16, 2022
86c8e32
integrate with paste event
robonetphy Jul 16, 2022
a5aaa65
lint removed
robonetphy Jul 16, 2022
bbbd34b
Merge branch 'next' into fix/xss-problem
robonetphy Aug 3, 2022
5338890
remove old changes
robonetphy Aug 3, 2022
a9230d1
object based sanitization configuration support
robonetphy Aug 3, 2022
9131354
paste config updated
robonetphy Aug 25, 2022
2bbd6d0
logic updated
robonetphy Aug 25, 2022
26e557d
extract tag name from paste-config
robonetphy Aug 26, 2022
da2b87d
tool tags added
robonetphy Aug 26, 2022
2acfdee
multi tag sanitization added
robonetphy Aug 26, 2022
18684f8
the comments added
robonetphy Aug 26, 2022
cd631ef
lint removed
robonetphy Aug 26, 2022
3e308d4
Merge branch 'next' into fix/xss-problem
robonetphy Nov 7, 2022
a1f092a
Update types/configs/paste-config.d.ts
robonetphy Nov 7, 2022
dd8d6d6
update the changes
robonetphy Nov 7, 2022
36793eb
lint removed\
robonetphy Nov 7, 2022
f2df84b
return empty array by get tags
robonetphy Nov 11, 2022
0dce253
submoduble reset
robonetphy Nov 11, 2022
cecf49b
Update src/components/modules/paste.ts
robonetphy Nov 11, 2022
8aaff27
changelog added
robonetphy Nov 11, 2022
6cc153b
tool comments added
robonetphy Nov 11, 2022
aae5ee2
chore: docs, code comments updated
neSpecc Nov 11, 2022
b9a3969
fix: xss in processDataTransfer
neSpecc Nov 11, 2022
7fa8040
base tests added
neSpecc Nov 17, 2022
aea30fe
test added
neSpecc Nov 21, 2022
a001c01
rm 'only' from test suite
neSpecc Nov 21, 2022
0373eab
rm log
neSpecc Nov 21, 2022
15857b1
reorder test
neSpecc Nov 21, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
test added
  • Loading branch information
neSpecc committed Nov 21, 2022
commit aea30fe1dacdd28c2aa10ec6cd2d66dd2045e9ba
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- `Fix` — Wrong element not highlighted anymore when popover opened.
- `Fix` — When Tunes Menu open keydown events can not be handled inside plugins.
- `Fix` — If a Tool specifies some tags to substitute on paste, all attributes of that tags will be removed before passing them to the tool. Possible XSS vulnerability fixed.
- `Fix` — Workaround for the HTMLJanitor bug with Tables (https://github.com/guardian/html-janitor/issues/3) added
- `Improvement` — *Tools API* — `pasteConfig().tags` now support sanitizing configuration. It allows you to leave some explicitly specified attributes for pasted content.

### 2.25.0
Expand Down
2 changes: 2 additions & 0 deletions src/components/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,8 @@ export default class Dom {
'ruby',
'section',
'table',
'tbody',
'thead',
'tr',
'tfoot',
'ul',
Expand Down
25 changes: 22 additions & 3 deletions src/components/modules/paste.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ export default class Paste extends Module {
/**
* Reduce the tags or sanitize configs to a single array of sanitize config.
* For example:
* If sanitaze config is
* If sanitize config is
* [ 'tbody',
* {
* table: {
Expand Down Expand Up @@ -651,7 +651,20 @@ export default class Paste extends Module {

const customConfig = Object.assign({}, toolTags, tool.baseSanitizeConfig);

content.innerHTML = clean(content.innerHTML, customConfig);
/**
* A workaround for the HTMLJanitor bug with Tables (incorrect sanitizing of table.innerHTML)
* https://github.com/guardian/html-janitor/issues/3
*/
if (content.tagName.toLowerCase() === 'table') {
const cleanTableHTML = clean(content.outerHTML, customConfig);
const tmpWrapper = $.make('div', undefined, {
innerHTML: cleanTableHTML,
});

content = tmpWrapper.firstChild;
} else {
content.innerHTML = clean(content.innerHTML, customConfig);
}

const event = this.composePasteEvent('tag', {
data: content,
Expand All @@ -664,7 +677,12 @@ export default class Paste extends Module {
event,
};
})
.filter((data) => !$.isNodeEmpty(data.content) || $.isSingleTag(data.content));
.filter((data) => {
const isEmpty = $.isEmpty(data.content);
const isSingleTag = $.isSingleTag(data.content);

return !isEmpty || isSingleTag;
});
}

/**
Expand Down Expand Up @@ -977,3 +995,4 @@ export default class Paste extends Module {
}) as PasteEvent;
}
}

227 changes: 224 additions & 3 deletions test/cypress/tests/api/tools.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,10 @@ describe('Editor Tools Api', () => {
})
.then(() => {
expect(pastedElement).not.to.be.undefined;

/**
* Check that the <img> has only "src" attribute
*/
expect(pastedElement.tagName.toLowerCase()).eq('img');
expect(pastedElement.getAttribute('src')).eq('foo');
expect(pastedElement.attributes.length).eq(1);
Expand All @@ -673,16 +677,168 @@ describe('Editor Tools Api', () => {

/**
* tags: [
* 'tbody',
* 'video',
* {
* table: {
* width: true
* source: {
* src: true
* }
* }
* ]
*/
it('should support mixed tag names and sanitizer config ', () => {
/**
* Variable used for spying the pasted element we are passing to the Tool
*/
let pastedElement;

/**
* Test tool with pasteConfig.tags specified
*/
class TestTool {
/** config specified handled tag */
public static get pasteConfig(): PasteConfig {
return {
tags: [
'video', // video should not have attributes
{
source: { // source should have only src attribute
src: true,
},
},
],
};
}

/** onPaste callback will be stubbed below */
public onPaste(): void {}

/** save is required for correct implementation of the BlockTool class */
public save(): void {}

/** render is required for correct implementation of the BlockTool class */
public render(): HTMLElement {
return document.createElement('tbody');
}
}

/**
* Stub the onPaste method to access the PasteEvent data for assertion
*/
cy.stub(TestTool.prototype, 'onPaste').callsFake((event: HTMLPasteEvent) => {
pastedElement = event.detail.data;
});

cy.createEditor({
tools: {
testTool: TestTool,
},
});

cy.get('[data-cy=editorjs]')
.get('div.ce-block')
.click()
.paste({
'text/html': '<video width="100"><source src="movie.mp4" type="video/mp4"></video>',
})
.then(() => {
expect(pastedElement).not.to.be.undefined;

/**
* Check that <video> has no attributes
*/
expect(pastedElement.tagName.toLowerCase()).eq('video');
expect(pastedElement.attributes.length).eq(0);

/**
* Check that the <source> has only 'src' attribute
*/
expect(pastedElement.firstChild.tagName.toLowerCase()).eq('source');
expect(pastedElement.firstChild.getAttribute('src')).eq('movie.mp4');
expect(pastedElement.firstChild.attributes.length).eq(1);
});
});

/**
* It covers a workaround HTMLJanitor bug with tables (incorrect sanitizing of table.innerHTML)
* https://github.com/guardian/html-janitor/issues/3
*/
it('should correctly sanitize Table structure (test for HTMLJanitor bug)', () => {
/**
* Variable used for spying the pasted element we are passing to the Tool
*/
let pastedElement;

/**
* Test tool with pasteConfig.tags specified
*/
class TestTool {
/** config specified handled tag */
public static get pasteConfig(): PasteConfig {
return {
tags: [
'table',
'tbody',
{
td: {
width: true,
},
tr: {
height: true,
},
},
],
};
}

/** onPaste callback will be stubbed below */
public onPaste(): void {}

/** save is required for correct implementation of the BlockTool class */
public save(): void {}

/** render is required for correct implementation of the BlockTool class */
public render(): HTMLElement {
return document.createElement('tbody');
}
}

/**
* Stub the onPaste method to access the PasteEvent data for assertion
*/
cy.stub(TestTool.prototype, 'onPaste').callsFake((event: HTMLPasteEvent) => {
console.log('onpaste', (event.detail.data as HTMLElement).outerHTML);

pastedElement = event.detail.data;
});

cy.createEditor({
tools: {
testTool: TestTool,
},
});

cy.get('[data-cy=editorjs]')
.get('div.ce-block')
.click()
.paste({
'text/html': '<table><tr height="50"><td width="300">Ho-Ho-Ho</td></tr></table>',
})
.then(() => {
expect(pastedElement).not.to.be.undefined;
expect(pastedElement.tagName.toLowerCase()).eq('table');

/**
* Check that the <tr> has the 'height' attribute
*/
expect(pastedElement.querySelector('tr')).not.to.be.undefined;
expect(pastedElement.querySelector('tr').getAttribute('height')).eq('50');

/**
* Check that the <td> has the 'width' attribute
*/
expect(pastedElement.querySelector('td')).not.to.be.undefined;
expect(pastedElement.querySelector('td').getAttribute('width')).eq('300');
});
});

/**
Expand All @@ -694,7 +850,72 @@ describe('Editor Tools Api', () => {
* ]
*/
it('should support config with several keys as the single entry', () => {
/**
* Variable used for spying the pasted element we are passing to the Tool
*/
let pastedElement;

/**
* Test tool with pasteConfig.tags specified
*/
class TestTool {
/** config specified handled tag */
public static get pasteConfig(): PasteConfig {
return {
tags: [
{
video: {
width: true,
},
source: {
src: true,
},
},
],
};
}

/** onPaste callback will be stubbed below */
public onPaste(): void {}

/** save is required for correct implementation of the BlockTool class */
public save(): void {}

/** render is required for correct implementation of the BlockTool class */
public render(): HTMLElement {
return document.createElement('tbody');
}
}

/**
* Stub the onPaste method to access the PasteEvent data for assertion
*/
cy.stub(TestTool.prototype, 'onPaste').callsFake((event: HTMLPasteEvent) => {
pastedElement = event.detail.data;
});

cy.createEditor({
tools: {
testTool: TestTool,
},
});

cy.get('[data-cy=editorjs]')
.get('div.ce-block')
.click()
.paste({
'text/html': '<video width="100"><source src="movie.mp4" type="video/mp4"></video>',
})
.then(() => {
expect(pastedElement).not.to.be.undefined;
expect(pastedElement.tagName.toLowerCase()).eq('video');

/**
* Check that the <tr> has the 'height' attribute
*/
expect(pastedElement.firstChild.tagName.toLowerCase()).eq('source');
expect(pastedElement.firstChild.getAttribute('src')).eq('movie.mp4');
});
});
});
});
Expand Down