Skip to content

Commit 3609671

Browse files
Merge pull request #67 from contentstack/RT-360
Rt 360
2 parents 7a51491 + b2cd96a commit 3609671

File tree

7 files changed

+253
-3
lines changed

7 files changed

+253
-3
lines changed

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ On the other hand, the `customTextWrapper` parser function provides the followin
161161
- `child`: The HTML string that specifies the child element
162162
- `value`: The value passed against the child element
163163

164+
164165
You can use the following customized JSON RTE Serializer code to convert your JSON RTE field data into HTML format.
165166

166167
```javascript
@@ -230,6 +231,60 @@ The resulting HTML data will look as follows:
230231
```HTML
231232
<p><social-embed url="https://twitter.com/Contentstack/status/1508911909038436365?cxt=HHwWmsC9-d_Y3fApAAAA"></social-embed></p><p>This <color data-color="red">is</color> text.</p>
232233
```
234+
<br>
235+
<br>
236+
237+
##### <p>You can pass the option `skipURLSanitization` as true to bypass the validation checks and sanitization for the src URLs of JSON element types - social embed and embed.</p>
238+
<div>By default, this option is set to false.</div>
239+
240+
#### Examples:
241+
242+
1. For the following JSON, with src url containing script tags
243+
```JSON
244+
{
245+
"type": "doc",
246+
"attrs": {},
247+
"children": [
248+
{
249+
"type": "social-embeds",
250+
"attrs": {
251+
"src": "https://www.youtube.com/watch?v=Gw7EqoOYC9A\"></iframe><script>alert(document.cookie)</script><iframe ",
252+
"width": 560,
253+
"height": 320
254+
},
255+
}
256+
]
257+
}
258+
```
259+
The resulting HTML:
260+
```HTML
261+
<iframe src="https://www.youtube.com/watch?v=Gw7EqoOYC9A" width="560" height="320" data-type="social-embeds" ></iframe>
262+
```
263+
264+
2. For any JSON containing src urls violating expected protocols, the src attribute will be removed when converted to HTML
265+
266+
```JSON
267+
{
268+
"type": "doc",
269+
"attrs": {},
270+
"children": [
271+
{
272+
"type": "social-embeds",
273+
"attrs": {
274+
"src": "www.youtube.com/watch?v=Gw7EqoOYC9A\">",
275+
"width": 560,
276+
"height": 320
277+
},
278+
}
279+
]
280+
}
281+
```
282+
The resulting HTML:
283+
```HTML
284+
<iframe width="560" height="320" data-type="social-embeds" ></iframe>
285+
```
286+
287+
<br>
233288

234289
### Convert HTML to JSON
235290

package-lock.json

Lines changed: 31 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
},
5050
"dependencies": {
5151
"array-flat-polyfill": "^1.0.1",
52+
"dompurify": "^3.2.3",
5253
"lodash": "^4.17.21",
5354
"lodash.clonedeep": "^4.5.0",
5455
"lodash.flatten": "^4.4.0",

src/toRedactor.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import kebbab from 'lodash.kebabcase'
22
import isEmpty from 'lodash.isempty'
3-
3+
import DOMPurify from 'dompurify'
44
import {IJsonToHtmlElementTags, IJsonToHtmlOptions, IJsonToHtmlTextTags} from './types'
55
import isPlainObject from 'lodash.isplainobject'
66

@@ -494,6 +494,19 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
494494
}
495495
figureStyles.fieldsEdited.push(figureStyles.caption)
496496
}
497+
498+
if (!options?.skipURLSanitization && (jsonValue['type'] === 'social-embeds' || jsonValue['type'] === 'embed')) {
499+
const sanitizedHTML = DOMPurify.sanitize(allattrs['src']);
500+
501+
const urlMatch = sanitizedHTML.match(/https?:\/\/[^\s"'<>()]+/);
502+
503+
if (urlMatch) {
504+
attrsJson['src'] = decodeURIComponent(urlMatch[0]);
505+
} else {
506+
delete attrsJson['src'];
507+
}
508+
}
509+
497510
if(!(options?.customElementTypes && !isEmpty(options.customElementTypes) && options.customElementTypes[jsonValue['type']])) {
498511
delete attrsJson['url']
499512
}

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ export interface IJsonToHtmlOptions {
2020
customElementTypes?: IJsonToHtmlElementTags,
2121
customTextWrapper?: IJsonToHtmlTextTags,
2222
allowNonStandardTypes?: boolean,
23+
skipURLSanitization?:boolean
2324
}

test/expectedJson.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,6 +1999,137 @@ export default {
19991999
}
20002000
],
20012001
"htmlUpdated": "<p></p><img asset_uid=\"blt5523ee02703e39f5\" src=\"https://images.com/captain_pardip.jpg\" width=\"24.193548387096776\" height=\"auto\" style=\"width: 24.193548387096776%; height: auto;height: auto;\" type=\"asset\" sys-style-type=\"download\"/><p></p><iframe src=\"https://www.***REMOVED***.com/embed/CSvFpBOe8eY\"></iframe><img asset_uid=\"blta2aad0332073026c\" src=\"https://images.com/logo_1.jpg\" height=\"auto\" type=\"asset\" sys-style-type=\"download\"/>"
2002+
},
2003+
"RT-360":{
2004+
"html": [
2005+
`<iframe src="https://www.youtube.com/watch?v=Gw7EqoOYC9A" width="560" height="320" data-type="social-embeds" ></iframe><iframe allowfullscreen=\"true\" src=\"https://www.youtube.com/watch?v=Gw7EqoOYC9A\"></iframe>`,
2006+
`<iframe width="560" height="320" data-type="social-embeds" ></iframe><iframe allowfullscreen=\"true\"></iframe>`,
2007+
'<iframe width="560" height="320" data-type="social-embeds" ></iframe><iframe allowfullscreen=\"true\"></iframe>',
2008+
],
2009+
"json":
2010+
[
2011+
{
2012+
"type": "doc",
2013+
"attrs": {},
2014+
"uid": "18396bf67f1f4b0a9da57643ac0542ca",
2015+
"children": [
2016+
{
2017+
"uid": "45a850acbeb949db86afe415625ad1ce",
2018+
"type": "social-embeds",
2019+
"attrs": {
2020+
"src": "https://www.youtube.com/watch?v=Gw7EqoOYC9A\"></iframe><script>alert(document.cookie)</script><iframe ",
2021+
"width": 560,
2022+
"height": 320
2023+
},
2024+
"children": [
2025+
{
2026+
"text": ""
2027+
}
2028+
]
2029+
},
2030+
{
2031+
"uid": "d3c2ab78a5e547b082f95dc01123b0c1",
2032+
"type": "doc",
2033+
"_version": 11,
2034+
"attrs": {},
2035+
"children": [
2036+
{
2037+
"uid": "87fed1cc68ce435caa0f71d17788c618",
2038+
"type": "embed",
2039+
"attrs": {
2040+
"src": "https://www.youtube.com/watch?v=Gw7EqoOYC9A\"></iframe><script>alert(document.cookie)</script><iframe ",
2041+
"redactor-attributes": {
2042+
"allowfullscreen": true
2043+
}
2044+
}
2045+
}
2046+
]
2047+
}
2048+
],
2049+
"_version": 1
2050+
},
2051+
{
2052+
"type": "doc",
2053+
"attrs": {},
2054+
"uid": "18396bf67f1f4b0a9da57643ac0542ca",
2055+
"children": [
2056+
{
2057+
"uid": "45a850acbeb949db86afe415625ad1ce",
2058+
"type": "social-embeds",
2059+
"attrs": {
2060+
"src": null,
2061+
"width": 560,
2062+
"height": 320
2063+
},
2064+
"children": [
2065+
{
2066+
"text": ""
2067+
}
2068+
]
2069+
},
2070+
{
2071+
"uid": "d3c2ab78a5e547b082f95dc01123b0c1",
2072+
"type": "doc",
2073+
"_version": 11,
2074+
"attrs": {},
2075+
"children": [
2076+
{
2077+
"uid": "87fed1cc68ce435caa0f71d17788c618",
2078+
"type": "embed",
2079+
"attrs": {
2080+
"src": null,
2081+
"redactor-attributes": {
2082+
"allowfullscreen": true
2083+
}
2084+
}
2085+
}
2086+
]
2087+
}
2088+
],
2089+
"_version": 1
2090+
},
2091+
{
2092+
"type": "doc",
2093+
"attrs": {},
2094+
"uid": "18396bf67f1f4b0a9da57643ac0542ca",
2095+
"children": [
2096+
{
2097+
"uid": "45a850acbeb949db86afe415625ad1ce",
2098+
"type": "social-embeds",
2099+
"attrs": {
2100+
"src": "www.youtube.com/watch?v=Gw7EqoOYC9A",
2101+
"width": 560,
2102+
"height": 320
2103+
},
2104+
"children": [
2105+
{
2106+
"text": ""
2107+
}
2108+
]
2109+
},
2110+
{
2111+
"uid": "d3c2ab78a5e547b082f95dc01123b0c1",
2112+
"type": "doc",
2113+
"_version": 11,
2114+
"attrs": {},
2115+
"children": [
2116+
{
2117+
"uid": "87fed1cc68ce435caa0f71d17788c618",
2118+
"type": "embed",
2119+
"attrs": {
2120+
"src": "www.youtube.com/embed/VD6xJq8NguY",
2121+
"redactor-attributes": {
2122+
"allowfullscreen": true
2123+
}
2124+
}
2125+
}
2126+
]
2127+
}
2128+
],
2129+
"_version": 1
2130+
}
2131+
]
2132+
20022133
}
20032134

20042135
}

test/toRedactor.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,5 +248,25 @@ describe("Testing json to html conversion", () => {
248248
const html = toRedactor(json);
249249
expect(html).toBe(`<iframe src="https://www.***REMOVED***.com/embed/3V-Sq7_uHXQ" width="560" height="320" data-type="social-embeds" ></iframe>`);
250250
})
251+
252+
describe("RT-360", () =>{
253+
it("should remove script and/or other tags from src links in HTML for social-embeds", () => {
254+
const json = expectedValue["RT-360"].json[0]
255+
const html = toRedactor(json);
256+
expect(html).toBe(expectedValue["RT-360"].html[0]);
257+
})
258+
259+
it("should handle undefined or null cases",()=>{
260+
const json = expectedValue["RT-360"].json[1]
261+
const html = toRedactor(json);
262+
expect(html).toBe(expectedValue["RT-360"].html[1]);
263+
})
264+
265+
it("should handle src without protocol",()=>{
266+
const json = expectedValue["RT-360"].json[2]
267+
const html = toRedactor(json);
268+
expect(html).toBe(expectedValue["RT-360"].html[2]);
269+
})
270+
})
251271
})
252272

0 commit comments

Comments
 (0)