Skip to content
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

Rt 360 #67

Merged
merged 4 commits into from
Jan 10, 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
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ On the other hand, the `customTextWrapper` parser function provides the followin
- `child`: The HTML string that specifies the child element
- `value`: The value passed against the child element


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

```javascript
Expand Down Expand Up @@ -230,6 +231,60 @@ 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
33 changes: 31 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
},
"dependencies": {
"array-flat-polyfill": "^1.0.1",
"dompurify": "^3.2.3",
"lodash": "^4.17.21",
"lodash.clonedeep": "^4.5.0",
"lodash.flatten": "^4.4.0",
Expand Down
15 changes: 14 additions & 1 deletion src/toRedactor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import kebbab from 'lodash.kebabcase'
import isEmpty from 'lodash.isempty'

import DOMPurify from 'dompurify'
import {IJsonToHtmlElementTags, IJsonToHtmlOptions, IJsonToHtmlTextTags} from './types'
import isPlainObject from 'lodash.isplainobject'

Expand Down Expand Up @@ -494,6 +494,19 @@ 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(!(options?.customElementTypes && !isEmpty(options.customElementTypes) && options.customElementTypes[jsonValue['type']])) {
delete attrsJson['url']
}
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ export interface IJsonToHtmlOptions {
customElementTypes?: IJsonToHtmlElementTags,
customTextWrapper?: IJsonToHtmlTextTags,
allowNonStandardTypes?: boolean,
skipURLSanitization?:boolean
}
131 changes: 131 additions & 0 deletions test/expectedJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1999,6 +1999,137 @@ export default {
}
],
"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\"/>"
},
"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 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>',
],
"json":
[
{
"type": "doc",
"attrs": {},
"uid": "18396bf67f1f4b0a9da57643ac0542ca",
"children": [
{
"uid": "45a850acbeb949db86afe415625ad1ce",
"type": "social-embeds",
"attrs": {
"src": "https://www.youtube.com/watch?v=Gw7EqoOYC9A\"></iframe><script>alert(document.cookie)</script><iframe ",
"width": 560,
"height": 320
},
"children": [
{
"text": ""
}
]
},
{
"uid": "d3c2ab78a5e547b082f95dc01123b0c1",
"type": "doc",
"_version": 11,
"attrs": {},
"children": [
{
"uid": "87fed1cc68ce435caa0f71d17788c618",
"type": "embed",
"attrs": {
"src": "https://www.youtube.com/watch?v=Gw7EqoOYC9A\"></iframe><script>alert(document.cookie)</script><iframe ",
"redactor-attributes": {
"allowfullscreen": true
}
}
}
]
}
],
"_version": 1
},
{
"type": "doc",
"attrs": {},
"uid": "18396bf67f1f4b0a9da57643ac0542ca",
"children": [
{
"uid": "45a850acbeb949db86afe415625ad1ce",
"type": "social-embeds",
"attrs": {
"src": null,
"width": 560,
"height": 320
},
"children": [
{
"text": ""
}
]
},
{
"uid": "d3c2ab78a5e547b082f95dc01123b0c1",
"type": "doc",
"_version": 11,
"attrs": {},
"children": [
{
"uid": "87fed1cc68ce435caa0f71d17788c618",
"type": "embed",
"attrs": {
"src": null,
"redactor-attributes": {
"allowfullscreen": true
}
}
}
]
}
],
"_version": 1
},
{
"type": "doc",
"attrs": {},
"uid": "18396bf67f1f4b0a9da57643ac0542ca",
"children": [
{
"uid": "45a850acbeb949db86afe415625ad1ce",
"type": "social-embeds",
"attrs": {
"src": "www.youtube.com/watch?v=Gw7EqoOYC9A",
"width": 560,
"height": 320
},
"children": [
{
"text": ""
}
]
},
{
"uid": "d3c2ab78a5e547b082f95dc01123b0c1",
"type": "doc",
"_version": 11,
"attrs": {},
"children": [
{
"uid": "87fed1cc68ce435caa0f71d17788c618",
"type": "embed",
"attrs": {
"src": "www.youtube.com/embed/VD6xJq8NguY",
"redactor-attributes": {
"allowfullscreen": true
}
}
}
]
}
],
"_version": 1
}
]

}

}
20 changes: 20 additions & 0 deletions test/toRedactor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,5 +248,25 @@ describe("Testing json to html conversion", () => {
const html = toRedactor(json);
expect(html).toBe(`<iframe src="https://www.***REMOVED***.com/embed/3V-Sq7_uHXQ" width="560" height="320" data-type="social-embeds" ></iframe>`);
})

describe("RT-360", () =>{
it("should remove script and/or other tags from src links in HTML for social-embeds", () => {
const json = expectedValue["RT-360"].json[0]
const html = toRedactor(json);
expect(html).toBe(expectedValue["RT-360"].html[0]);
})

it("should handle undefined or null cases",()=>{
const json = expectedValue["RT-360"].json[1]
const html = toRedactor(json);
expect(html).toBe(expectedValue["RT-360"].html[1]);
})

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

Loading