Skip to content
Merged

Next #80

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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## [1.3.9](https://github.com/contentstack/contentstack-utils-javascript/tree/v1.3.9) (2024-07-08)
- Fix: SRE vulnerabilities fixed

## [1.3.8](https://github.com/contentstack/contentstack-utils-javascript/tree/v1.3.8) (2024-06-24)
- Feat: Support for Image type asset in JsonToHtml

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/utils",
"version": "1.3.8",
"version": "1.3.9",
"description": "Contentstack utilities for Javascript",
"main": "dist/index.es.js",
"types": "dist/types/index.d.ts",
Expand Down
19 changes: 19 additions & 0 deletions src/helper/sanitize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

type AllowedTags = 'p' | 'a' | 'strong' | 'em' | 'ul' | 'ol' | 'li' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'sub' | 'u' | 'table' | 'thead' | 'tbody' | 'tr' | 'th' | 'td' | 'span'|'fragment'|'strike'|'sup'|'br';
type AllowedAttributes = 'href' | 'title' | 'target' | 'alt' | 'src' | 'class' | 'id' | 'style';

export function sanitizeHTML(input: string, allowedTags: AllowedTags[] = ['p', 'a', 'strong', 'em', 'ul', 'ol', 'li', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'sub', 'u', 'table', 'thead', 'tbody', 'tr', 'th', 'td', 'span','fragment','sup','strike','br'], allowedAttributes: AllowedAttributes[] = ['href', 'title', 'target', 'alt', 'src', 'class', 'id', 'style']): string {
// Regular expression to find and remove all HTML tags except the allowed ones
const sanitized = input.replace(/<\/?([a-z][a-z0-9]*)\b[^<>]*>/gi, (match, tag) => {
return allowedTags.includes(tag.toLowerCase()) ? match : '';
});

// Regular expression to remove all attributes except the allowed ones
const cleaned = sanitized.replace(/\s([a-z:]+)=['"][^'"]*['"]/gi
, (match, attribute) => {
return allowedAttributes.includes(attribute.toLowerCase()) ? match : '';
});

return cleaned;
}

74 changes: 39 additions & 35 deletions src/options/default-node-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,73 +2,77 @@ import { Next, RenderOption } from ".";
import MarkType from "../nodes/mark-type";
import Node from "../nodes/node";
import NodeType from "../nodes/node-type";
import { sanitizeHTML } from "../helper/sanitize";

export const defaultNodeOption: RenderOption = {
[NodeType.DOCUMENT]:(node: Node) => {
return ``
},
[NodeType.PARAGRAPH]:(node: Node, next: Next) => {
return `<p${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</p>`
return `<p${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</p>`
},
[NodeType.LINK]:(node: Node, next: Next) => {
const sanitizedHref = sanitizeHTML(node.attrs.href || node.attrs.url);
if (node.attrs.target) {
return `<a${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} href="${node.attrs.href || node.attrs.url}" target="${node.attrs.target}">${next(node.children)}</a>`
return `<a${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} href="${sanitizedHref}" target="${node.attrs.target}">${sanitizeHTML(next(node.children))}</a>`
}
return `<a${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} href="${node.attrs.href || node.attrs.url}">${next(node.children)}</a>`
return `<a${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} href="${sanitizedHref}">${sanitizeHTML(next(node.children))}</a>`
},
[NodeType.IMAGE]:(node: Node, next: Next) => {
return `<img${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} src="${node.attrs.src || node.attrs.url}" />${next(node.children)}`
const sanitizedSrc = sanitizeHTML(node.attrs.src || node.attrs.url);
return `<img${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} src="${sanitizedSrc}" />${sanitizeHTML(next(node.children))}`
},
[NodeType.EMBED]:(node: Node, next: Next) => {
return `<iframe${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} src="${node.attrs.src || node.attrs.url}">${next(node.children)}</iframe>`
const sanitizedSrc = sanitizeHTML(node.attrs.src || node.attrs.url);
return `<iframe${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} src="${sanitizedSrc}">${sanitizeHTML(next(node.children))}</iframe>`
},
[NodeType.HEADING_1]:(node: Node, next: Next) => {
return `<h1${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</h1>`
return `<h1${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h1>`
},
[NodeType.HEADING_2]:(node: Node, next: Next) => {
return `<h2${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</h2>`
return `<h2${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h2>`
},
[NodeType.HEADING_3]:(node: Node, next: Next) => {
return `<h3${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</h3>`
return `<h3${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h3>`
},
[NodeType.HEADING_4]:(node: Node, next: Next) => {
return `<h4${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</h4>`
return `<h4${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h4>`
},
[NodeType.HEADING_5]:(node: Node, next: Next) => {
return `<h5${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</h5>`
return `<h5${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h5>`
},
[NodeType.HEADING_6]:(node: Node, next: Next) => {
return `<h6${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</h6>`
return `<h6${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</h6>`
},
[NodeType.ORDER_LIST]:(node: Node, next: Next) => {
return `<ol${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</ol>`
return `<ol${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</ol>`
},
[NodeType.FRAGMENT]:(node: Node, next: Next) => {
return `<fragment>${next(node.children)}</fragment>`
return `<fragment>${sanitizeHTML(next(node.children))}</fragment>`
},
[NodeType.UNORDER_LIST]:(node: Node, next: Next) => {
return `<ul${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</ul>`
return `<ul${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</ul>`
},
[NodeType.LIST_ITEM]:(node: Node, next: Next) => {
return `<li${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</li>`
return `<li${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</li>`
},
[NodeType.HR]:(node: Node, next: Next) => {
return `<hr>`
},
[NodeType.TABLE]:(node: Node, next: Next) => {
return `<table${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</table>`
return `<table${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</table>`
},
[NodeType.TABLE_HEADER]:(node: Node, next: Next) => {
return `<thead${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</thead>`
return `<thead${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</thead>`
},
[NodeType.TABLE_BODY]:(node: Node, next: Next) => {
return `<tbody${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</tbody>`
return `<tbody${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</tbody>`
},
[NodeType.TABLE_FOOTER]:(node: Node, next: Next) => {
return `<tfoot${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</tfoot>`
return `<tfoot${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</tfoot>`
},
[NodeType.TABLE_ROW]:(node: Node, next: Next) => {
return `<tr${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</tr>`
return `<tr${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</tr>`
},
[NodeType.TABLE_HEAD]:(node: Node, next: Next) => {
if (node.attrs.void) return '';
Expand All @@ -78,7 +82,7 @@ export const defaultNodeOption: RenderOption = {
`${node.attrs.colSpan ? ` colspan="${node.attrs.colSpan}"` : ``}` +
`${node.attrs.style ? ` style="${node.attrs.style}"` : ``}`+
`${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}`+
`${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}` +
`${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}` +
`</th>`
},
[NodeType.TABLE_DATA]:(node: Node, next: Next) => {
Expand All @@ -89,52 +93,52 @@ export const defaultNodeOption: RenderOption = {
`${node.attrs.colSpan ? ` colspan="${node.attrs.colSpan}"` : ``}` +
`${node.attrs.style ? ` style="${node.attrs.style}"` : ``}`+
`${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}`+
`${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}` +
`${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}` +
`</td>`
},
[NodeType.BLOCK_QUOTE]:(node: Node, next: Next) => {
return `<blockquote${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</blockquote>`
return `<blockquote${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</blockquote>`
},
[NodeType.CODE]:(node: Node, next: Next) => {
return `<code${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${next(node.children)}</code>`
return `<code${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``}>${sanitizeHTML(next(node.children))}</code>`
},

['reference']:(node: Node, next: Next) => {
if (node.attrs.type === 'asset') {
return `<img${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${node.attrs['class-name']}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} src="${node.attrs['asset-link']}" />`
return `<img${node.attrs.style ? ` style="${node.attrs.style}"` : ``}${node.attrs['class-name'] ? ` class="${sanitizeHTML(node.attrs['class-name'])}"` : ``}${node.attrs.id ? ` id="${node.attrs.id}"` : ``} src="${sanitizeHTML(node.attrs['asset-link'])}" />`
}
return ``
},
['default']:(node: Node, next: Next) => {
return next(node.children)
return sanitizeHTML(next(node.children))
},

[MarkType.BOLD]:(text: string) => {
return `<strong>${text}</strong>`
return `<strong>${sanitizeHTML(text)}</strong>`
},
[MarkType.ITALIC]:(text: string) => {
return `<em>${text}</em>`
return `<em>${sanitizeHTML(text)}</em>`
},
[MarkType.UNDERLINE]:(text: string) => {
return `<u>${text}</u>`
return `<u>${sanitizeHTML(text)}</u>`
},
[MarkType.STRIKE_THROUGH]:(text: string) => {
return `<strike>${text}</strike>`
return `<strike>${sanitizeHTML(text)}</strike>`
},
[MarkType.INLINE_CODE]:(text: string) => {
return `<span>${text}</span>`
return `<span>${sanitizeHTML(text)}</span>`
},
[MarkType.SUBSCRIPT]:(text: string) => {
return `<sub>${text}</sub>`
return `<sub>${sanitizeHTML(text)}</sub>`
},
[MarkType.SUPERSCRIPT]:(text: string) => {
return `<sup>${text}</sup>`
return `<sup>${sanitizeHTML(text)}</sup>`
},
[MarkType.BREAK]:(text: string) => {
return `<br />${text}`
return `<br />${sanitizeHTML(text)}`
},
[MarkType.CLASSNAME_OR_ID]:(text: string, classname: string, id:string) => {
return `<span${classname ? ` class="${classname}"` : ``}${id ? ` id="${id}"` : ``}>${text}</span>`
return `<span${classname ? ` class="${classname}"` : ``}${id ? ` id="${id}"` : ``}>${sanitizeHTML(text)}</span>`
}

}
Loading