Skip to content
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
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023-2024 Contentstack
Copyright (c) 2024-2025 Contentstack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
32 changes: 32 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 @@ -356,12 +357,43 @@ 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.

### Convert JSON to HTML

#### HTML Attribute Name and Value Sanitization


This project ensures that HTML attributes are properly validated and sanitized according to the W3C HTML specification. It validates attribute names based on the HTML standards and sanitizes attribute values to ensure correct rendering and security, particularly against cross-site scripting (XSS) vulnerabilities.

#### Attribute Name Guidelines

All HTML attribute names must conform to the [W3C HTML specification](https://www.w3.org/TR/2012/WD-html-markup-20120329/syntax.html#attribute-name). These guidelines specify the following rules:

- **Printable ASCII Characters:** Attribute names must consist only of printable ASCII characters.
- **Case-Insensitive:** Attribute names are case-insensitive, but lowercase is preferred for consistency.
- **No Special Characters:** Attribute names cannot contain spaces or special characters such as `=`, `>`, `<`, `"`, etc.
- **Allowed Attributes:** Attributes such as `xmlns`, `aria-*`, `data-*`, and others defined by HTML5 standards are allowed and must follow specific rules.

##### Important Note:
If an attribute name does not conform to these rules, the attribute will be **dropped** from the element.

#### Attribute Value Guidelines

The values of HTML attributes are sanitized to ensure proper rendering and to mitigate security risks, such as Cross-Site Scripting (XSS). This sanitization process involves replacing HTML entities (like `&lt;`, `&gt;`, `&amp;`, etc.) with their corresponding characters and removing any invalid or unsafe characters.

Here are some common HTML entities and their replacements:

- `&lt;` → `<`
- `&gt;` → `>`
- `&amp;` → `&`


<hr>
You can pass the `allowNonStandardTags: true` parameter within the `jsonToHtml` method to allow the JSON RTE Serializer tool to recognize standard HTML tags or element types and convert them into JSON format.

You can use the following customized JSON RTE Serializer code to convert your JSON RTE field data into HTML format.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

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.0.12",
"version": "2.1.0",
"description": "This Package converts Html Document to Json and vice-versa.",
"main": "lib/index.js",
"module": "lib/index.mjs",
Expand Down
17 changes: 14 additions & 3 deletions src/toRedactor.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import kebbab from 'lodash.kebabcase'
import isEmpty from 'lodash.isempty'

import {IJsonToHtmlElementTags, IJsonToHtmlOptions, IJsonToHtmlTextTags} from './types'
import isPlainObject from 'lodash.isplainobject'
import {replaceHtmlEntities, forbiddenAttrChars } from './utils'

const ELEMENT_TYPES: IJsonToHtmlElementTags = {
'blockquote': (attrs: string, child: string) => {
Expand Down Expand Up @@ -379,6 +379,9 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
}
if (attrsJson['width']) {
let width = attrsJson['width']
if(typeof width === 'number'){
width = width.toString()
}
if (width.slice(width.length - 1) === '%') {
style = `width: ${allattrs['width'] + '%'}; height: ${attrsJson['height'] ? attrsJson['height'] : 'auto'};`
} else {
Expand Down Expand Up @@ -494,12 +497,20 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
}
figureStyles.fieldsEdited.push(figureStyles.caption)
}

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']
}
delete attrsJson['redactor-attributes']
Object.entries(attrsJson).forEach((key) => {
return key[1] ? (key[1] !== '' ? (attrs += `${key[0]}="${key[1]}" `) : '') : ''
if (forbiddenAttrChars.some(char => key[0].includes(char))) {
return;
}
return key[1] ? (key[1] !== '' ? (attrs += `${key[0]}="${replaceHtmlEntities(key[1])}" `) : '') : ''
})
attrs = (attrs.trim() ? ' ' : '') + attrs.trim()
}
Expand Down Expand Up @@ -556,7 +567,7 @@ export const toRedactor = (jsonValue: any,options?:IJsonToHtmlOptions) : string
if(['td','th'].includes(jsonValue['type'])){
if(jsonValue?.['attrs']?.['void']) return ''
}

attrs = (attrs.trim() ? ' ' : '') + attrs.trim()

return ELEMENT_TYPES[orgType || jsonValue['type']](attrs, children,jsonValue, figureStyles)
Expand Down
9 changes: 9 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function replaceHtmlEntities(str: string): string {
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}

export const forbiddenAttrChars = ['"', "'", '>','<', '/', '='];
205 changes: 205 additions & 0 deletions test/expectedJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1999,6 +1999,211 @@ 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%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 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>`,
`<iframe src="null" width="560" height="320" title=" This is for &lt;/p&gt;testing &lt;/p&gt; purpose 'only' " data-type="social-embeds" ></iframe>`,
`<iframe 123="456" src="https://www.youtube.com/embed/Gw7EqoOYC9A?si=bWdnezma6qFAePQU" width="560" height="320" status="Active" data-type="social-embeds" ></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": "",
"width": 560,
"height": 320
},
"children": [
{
"text": ""
}
]
},
{
"uid": "d3c2ab78a5e547b082f95dc01123b0c1",
"type": "doc",
"_version": 11,
"attrs": {},
"children": [
{
"uid": "87fed1cc68ce435caa0f71d17788c618",
"type": "embed",
"attrs": {
"src": "",
"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
},
{
"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
},
{
uid: "45a850acbeb949db86afe415625ad1ce",
type: "social-embeds",
attrs: {
src: "null",
width: 560,
height: 320,
title: " This is for </p>testing </p> purpose 'only' "
},
children: [{ text: "" }],
},
{
"uid": "45a850acbeb949db86afe415625ad1ce",
"type": "social-embeds",
"attrs": {
"123": "456",
"src": "https://www.youtube.com/embed/Gw7EqoOYC9A?si=bWdnezma6qFAePQU",
"width": 560,
"height": 320,
"<p>ding": 234,
"status": "Active",
"emptyKey<": "12",
"country/": "USA"
},
"children": [
{
"text": ""
}
]
},

]

}

}
Loading
Loading