Skip to content

fix: encode social-embeds and embeds src urls #72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
56 changes: 2 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,60 +231,6 @@ The resulting HTML data will look as follows:
```HTML
<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>
```
<br>
<br>

##### <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>
<div>By default, this option is set to false.</div>

#### Examples:

1. For the following JSON, with src url containing script tags
```JSON
{
"type": "doc",
"attrs": {},
"children": [
{
"type": "social-embeds",
"attrs": {
"src": "https://www.youtube.com/watch?v=Gw7EqoOYC9A\"></iframe><script>alert(document.cookie)</script><iframe ",
"width": 560,
"height": 320
},
}
]
}
```
The resulting HTML:
```HTML
<iframe src="https://www.youtube.com/watch?v=Gw7EqoOYC9A" width="560" height="320" data-type="social-embeds" ></iframe>
```

2. For any JSON containing src urls violating expected protocols, the src attribute will be removed when converted to HTML

```JSON
{
"type": "doc",
"attrs": {},
"children": [
{
"type": "social-embeds",
"attrs": {
"src": "www.youtube.com/watch?v=Gw7EqoOYC9A\">",
"width": 560,
"height": 320
},
}
]
}
```
The resulting HTML:
```HTML
<iframe width="560" height="320" data-type="social-embeds" ></iframe>
```

<br>

### Convert HTML to JSON

Expand Down Expand Up @@ -411,6 +357,8 @@ The resulting JSON-formatted data will look as follows:

## Automatic Conversion

> **_Note_**: `src` url's provided for social-embeds and embed items will by default be <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURI">uri encoded</a>.

By default, the JSON Rich Text Editor field supports limited HTML tags within the editor. Due to this, the JSON RTE Serializer tool is not able to recognize each and every standard HTML tag.

To help the JSON RTE Serializer recognize and process additional tags that are commonly used across HTML, you can use the automatic conversion option. When using this option, you need to pass the `allowNonStandardTags: true` parameter within the `jsonToHtml` or `htmlToJson` method to manipulate the working of the JSON RTE Serializer package as per your requirements. When you pass this parameter, it customizes your JSON RTE Serializer code to allow the support for all standard HTML-recognized tags or element types in the JSON Rich Text Editor field.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/json-rte-serializer",
"version": "2.1.0",
"version": "2.0.13",
"description": "This Package converts Html Document to Json and vice-versa.",
"main": "lib/index.js",
"module": "lib/index.mjs",
Expand Down
14 changes: 3 additions & 11 deletions src/toRedactor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -498,17 +498,9 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
figureStyles.fieldsEdited.push(figureStyles.caption)
}

if (!options?.skipURLSanitization && (jsonValue['type'] === 'social-embeds' || jsonValue['type'] === 'embed')) {
const sanitizedHTML = DOMPurify.sanitize(allattrs['src']);

const urlMatch = sanitizedHTML.match(/https?:\/\/[^\s"'<>()]+/);

if (urlMatch) {
attrsJson['src'] = decodeURIComponent(urlMatch[0]);
} else {
delete attrsJson['src'];
}
}
if (jsonValue['type'] === 'social-embeds' || jsonValue['type'] === 'embed') {
attrsJson['src'] = encodeURI(allattrs['src']);
}

if(!(options?.customElementTypes && !isEmpty(options.customElementTypes) && options.customElementTypes[jsonValue['type']])) {
delete attrsJson['url']
Expand Down
1 change: 0 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,4 @@ export interface IJsonToHtmlOptions {
customElementTypes?: IJsonToHtmlElementTags,
customTextWrapper?: IJsonToHtmlTextTags,
allowNonStandardTypes?: boolean,
skipURLSanitization?:boolean
}
49 changes: 45 additions & 4 deletions test/expectedJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2002,9 +2002,10 @@ export default {
},
"RT-360":{
"html": [
`<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>`,
`<iframe src=\"https://www.youtube.com/watch?v=Gw7EqoOYC9A%22%3E%3C/iframe%3E%3Cscript%3Ealert(document.cookie)%3C/script%3E%3Ciframe%20\" width=\"560\" height=\"320\" data-type=\"social-embeds\" ></iframe><iframe allowfullscreen=\"true\" src=\"https://www.youtube.com/watch?v=Gw7EqoOYC9A%22%3E%3C/iframe%3E%3Cscript%3Ealert(document.cookie)%3C/script%3E%3Ciframe%20\"></iframe>`,
`<iframe width="560" height="320" data-type="social-embeds" ></iframe><iframe allowfullscreen=\"true\"></iframe>`,
'<iframe width="560" height="320" data-type="social-embeds" ></iframe><iframe allowfullscreen=\"true\"></iframe>',
'<iframe src=\"www.youtube.com/watch?v=Gw7EqoOYC9A\" width=\"560\" height=\"320\" data-type=\"social-embeds\" ></iframe><iframe allowfullscreen=\"true\" src=\"www.youtube.com/embed/VD6xJq8NguY\"></iframe>',
`<iframe src=\"https://www.youtube.com/embed/Gw7EqoOYC9A?si=bWdnezma6qFAePQU\" width=\"560\" height=\"320\" data-type=\"social-embeds\" ></iframe><iframe allowfullscreen=\"true\" src=\"https://www.youtube.com/embed/Gw7EqoOYC9A?si=bWdnezma6qFAePQU\"></iframe>`
],
"json":
[
Expand Down Expand Up @@ -2057,7 +2058,7 @@ export default {
"uid": "45a850acbeb949db86afe415625ad1ce",
"type": "social-embeds",
"attrs": {
"src": null,
"src": "",
"width": 560,
"height": 320
},
Expand All @@ -2077,7 +2078,7 @@ export default {
"uid": "87fed1cc68ce435caa0f71d17788c618",
"type": "embed",
"attrs": {
"src": null,
"src": "",
"redactor-attributes": {
"allowfullscreen": true
}
Expand Down Expand Up @@ -2127,6 +2128,46 @@ export default {
}
],
"_version": 1
},
{
"type": "doc",
"attrs": {},
"uid": "18396bf67f1f4b0a9da57643ac0542ca",
"children": [
{
"uid": "45a850acbeb949db86afe415625ad1ce",
"type": "social-embeds",
"attrs": {
"src": "https://www.youtube.com/embed/Gw7EqoOYC9A?si=bWdnezma6qFAePQU",
"width": 560,
"height": 320
},
"children": [
{
"text": ""
}
]
},
{
"uid": "d3c2ab78a5e547b082f95dc01123b0c1",
"type": "doc",
"_version": 11,
"attrs": {},
"children": [
{
"uid": "87fed1cc68ce435caa0f71d17788c618",
"type": "embed",
"attrs": {
"src": "https://www.youtube.com/embed/Gw7EqoOYC9A?si=bWdnezma6qFAePQU",
"redactor-attributes": {
"allowfullscreen": true
}
}
}
]
}
],
"_version": 1
}
]

Expand Down
10 changes: 8 additions & 2 deletions test/toRedactor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ describe("Testing json to html conversion", () => {
})

describe("RT-360", () =>{
it("should remove script and/or other tags from src links in HTML for social-embeds", () => {
it("should encode and not render invalid src urls", () => {
const json = expectedValue["RT-360"].json[0]
const html = toRedactor(json);
expect(html).toBe(expectedValue["RT-360"].html[0]);
Expand All @@ -262,11 +262,17 @@ describe("Testing json to html conversion", () => {
expect(html).toBe(expectedValue["RT-360"].html[1]);
})

it("should handle src without protocol",()=>{
it("should handle src urls without protocol",()=>{
const json = expectedValue["RT-360"].json[2]
const html = toRedactor(json);
expect(html).toBe(expectedValue["RT-360"].html[2]);
})

it("should work only for valid embed urls",()=>{
const json = expectedValue["RT-360"].json[3]
const html = toRedactor(json);
expect(html).toBe(expectedValue["RT-360"].html[3]);
})
})

test('should convert numeric width to string', () => {
Expand Down
Loading