diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5bf8e80..36afa42 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/package-lock.json b/package-lock.json
index b593d41..9d83846 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@contentstack/utils",
- "version": "1.3.8",
+ "version": "1.3.9",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@contentstack/utils",
- "version": "1.3.8",
+ "version": "1.3.9",
"license": "MIT",
"devDependencies": {
"@babel/preset-env": "^7.22.20",
diff --git a/package.json b/package.json
index aa62bbe..ae864ae 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/helper/sanitize.ts b/src/helper/sanitize.ts
new file mode 100644
index 0000000..c413ff2
--- /dev/null
+++ b/src/helper/sanitize.ts
@@ -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;
+}
+
diff --git a/src/options/default-node-options.ts b/src/options/default-node-options.ts
index 49d4def..6e10756 100644
--- a/src/options/default-node-options.ts
+++ b/src/options/default-node-options.ts
@@ -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 `
${next(node.children)}
`
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.LINK]:(node: Node, next: Next) => {
+ const sanitizedHref = sanitizeHTML(node.attrs.href || node.attrs.url);
if (node.attrs.target) {
- return `${next(node.children)}`
+ return `${sanitizeHTML(next(node.children))}`
}
- return `${next(node.children)}`
+ return `${sanitizeHTML(next(node.children))}`
},
[NodeType.IMAGE]:(node: Node, next: Next) => {
- return `
${next(node.children)}`
+ const sanitizedSrc = sanitizeHTML(node.attrs.src || node.attrs.url);
+ return `
${sanitizeHTML(next(node.children))}`
},
[NodeType.EMBED]:(node: Node, next: Next) => {
- return ``
+ const sanitizedSrc = sanitizeHTML(node.attrs.src || node.attrs.url);
+ return ``
},
[NodeType.HEADING_1]:(node: Node, next: Next) => {
- return `${next(node.children)}
`
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.HEADING_2]:(node: Node, next: Next) => {
- return `${next(node.children)}
`
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.HEADING_3]:(node: Node, next: Next) => {
- return `${next(node.children)}
`
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.HEADING_4]:(node: Node, next: Next) => {
- return `${next(node.children)}
`
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.HEADING_5]:(node: Node, next: Next) => {
- return `${next(node.children)}
`
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.HEADING_6]:(node: Node, next: Next) => {
- return `${next(node.children)}
`
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.ORDER_LIST]:(node: Node, next: Next) => {
- return `${next(node.children)}
`
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.FRAGMENT]:(node: Node, next: Next) => {
- return `${next(node.children)}`
+ return `${sanitizeHTML(next(node.children))}`
},
[NodeType.UNORDER_LIST]:(node: Node, next: Next) => {
- return ``
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.LIST_ITEM]:(node: Node, next: Next) => {
- return `${next(node.children)}`
+ return `${sanitizeHTML(next(node.children))}`
},
[NodeType.HR]:(node: Node, next: Next) => {
return `
`
},
[NodeType.TABLE]:(node: Node, next: Next) => {
- return ``
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.TABLE_HEADER]:(node: Node, next: Next) => {
- return `${next(node.children)}`
+ return `${sanitizeHTML(next(node.children))}`
},
[NodeType.TABLE_BODY]:(node: Node, next: Next) => {
- return `${next(node.children)}`
+ return `${sanitizeHTML(next(node.children))}`
},
[NodeType.TABLE_FOOTER]:(node: Node, next: Next) => {
- return `${next(node.children)}`
+ return `${sanitizeHTML(next(node.children))}`
},
[NodeType.TABLE_ROW]:(node: Node, next: Next) => {
- return `${next(node.children)}
`
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.TABLE_HEAD]:(node: Node, next: Next) => {
if (node.attrs.void) return '';
@@ -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))}` +
``
},
[NodeType.TABLE_DATA]:(node: Node, next: Next) => {
@@ -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))}` +
``
},
[NodeType.BLOCK_QUOTE]:(node: Node, next: Next) => {
- return `${next(node.children)}
`
+ return `${sanitizeHTML(next(node.children))}
`
},
[NodeType.CODE]:(node: Node, next: Next) => {
- return `${next(node.children)}`
+ return `${sanitizeHTML(next(node.children))}`
},
['reference']:(node: Node, next: Next) => {
if (node.attrs.type === 'asset') {
- return `
`
+ return `
`
}
return ``
},
['default']:(node: Node, next: Next) => {
- return next(node.children)
+ return sanitizeHTML(next(node.children))
},
[MarkType.BOLD]:(text: string) => {
- return `${text}`
+ return `${sanitizeHTML(text)}`
},
[MarkType.ITALIC]:(text: string) => {
- return `${text}`
+ return `${sanitizeHTML(text)}`
},
[MarkType.UNDERLINE]:(text: string) => {
- return `${text}`
+ return `${sanitizeHTML(text)}`
},
[MarkType.STRIKE_THROUGH]:(text: string) => {
- return `${text}`
+ return `${sanitizeHTML(text)}`
},
[MarkType.INLINE_CODE]:(text: string) => {
- return `${text}`
+ return `${sanitizeHTML(text)}`
},
[MarkType.SUBSCRIPT]:(text: string) => {
- return `${text}`
+ return `${sanitizeHTML(text)}`
},
[MarkType.SUPERSCRIPT]:(text: string) => {
- return `${text}`
+ return `${sanitizeHTML(text)}`
},
[MarkType.BREAK]:(text: string) => {
- return `
${text}`
+ return `
${sanitizeHTML(text)}`
},
[MarkType.CLASSNAME_OR_ID]:(text: string, classname: string, id:string) => {
- return `${text}`
+ return `${sanitizeHTML(text)}`
}
}
diff --git a/src/options/default-options.ts b/src/options/default-options.ts
index 0c6e221..aac5309 100644
--- a/src/options/default-options.ts
+++ b/src/options/default-options.ts
@@ -3,13 +3,32 @@ import { RenderOption } from '.';
import { Metadata } from '../Models/metadata-model';
import { EmbeddedItem } from '../Models/embedded-object';
import { EntryNode } from '../Models/json-rte-model';
+import { sanitizeHTML } from '../helper/sanitize'
export const defaultOptions: RenderOption = {
- [StyleType.BLOCK]: (item: EmbeddedItem | EntryNode) =>
- `${item.title || item.uid}
Content type: ${item._content_type_uid || (item.system ? item.system.content_type_uid : '')}
`,
- [StyleType.INLINE]: (item: EmbeddedItem | EntryNode) => `${item.title || item.uid}`,
- [StyleType.LINK]: (item: EmbeddedItem | EntryNode, metadata: Metadata) => `${metadata.text || item.title || item.uid || (item.system ? item.system.uid : '')}`,
- [StyleType.DISPLAY]: (item: EmbeddedItem | EntryNode, metadata: Metadata) => `
`,
- [StyleType.DOWNLOAD]: (item: EmbeddedItem | EntryNode, metadata: Metadata) => `${metadata.text || item.title || item.uid || (item.system ? item.system.content_type_uid : '')}`,
+ [StyleType.BLOCK]: (item: EmbeddedItem | EntryNode) => {
+ const title = sanitizeHTML(item.title || item.uid);
+ const content_type_uid = sanitizeHTML(item._content_type_uid || (item.system ? item.system.content_type_uid : ''));
+ return `${title}
Content type: ${content_type_uid}
`;
+ },
+ [StyleType.INLINE]: (item: EmbeddedItem | EntryNode) => {
+ const title = sanitizeHTML(item.title || item.uid);
+ return `${title}`;
+ },
+ [StyleType.LINK]: (item: EmbeddedItem | EntryNode, metadata: Metadata) => {
+ const url = sanitizeHTML(item.url || 'undefined');
+ const text = sanitizeHTML(metadata.text || item.title || item.uid || (item.system ? item.system.uid : ''));
+ return `${text}`;
+ },
+ [StyleType.DISPLAY]: (item: EmbeddedItem | EntryNode, metadata: Metadata) => {
+ const url = sanitizeHTML(item.url || 'undefined');
+ const alt = sanitizeHTML(metadata.attributes.alt || item.title || item.filename || item.uid
+ || (item.system ? item.system.uid : ''));
+ return `
`;
+ },
+ [StyleType.DOWNLOAD]: (item: EmbeddedItem | EntryNode, metadata: Metadata) => {
+ const href = sanitizeHTML(item.url || 'undefined');
+ const text = sanitizeHTML(metadata.text || item.title || item.uid || (item.system ? item.system.content_type_uid : ''));
+ return `${text}`;
+ },
};