From 38aafbce6cf6cd63e38a949ed31dde5e15b2a5e4 Mon Sep 17 00:00:00 2001
From: Vikas Awaghade <67629551+vikas-cldcvr@users.noreply.github.com>
Date: Tue, 19 Dec 2023 17:22:46 +0530
Subject: [PATCH] =?UTF-8?q?FLOW-1037=20lineage=20node=20template=20renderi?=
=?UTF-8?q?ng=20logic=20updated=20to=20fix=20html=20i=E2=80=A6=20(#214)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* FLOW-1037 lineage node template rendering logic updated to fix html injection
* FLOW-1037 null svg element check added
* FLOW-1037 rendering error updated
---
packages/flow-lineage/CHANGELOG.md | 6 +
packages/flow-lineage/package.json | 2 +-
.../components/f-lineage/draw/draw-nodes.ts | 10 +-
.../f-lineage/draw/hot-reload-proxies.ts | 16 +-
.../src/components/f-lineage/f-lineage.ts | 71 ++--
.../htmlinjection-test.stories.ts | 349 ++++++++++++++++++
stories/flow-lineage/lineage-properties.mdx | 30 +-
7 files changed, 424 insertions(+), 60 deletions(-)
create mode 100644 stories/flow-lineage/htmlinjection-test.stories.ts
diff --git a/packages/flow-lineage/CHANGELOG.md b/packages/flow-lineage/CHANGELOG.md
index ca14cc787..c53864578 100644
--- a/packages/flow-lineage/CHANGELOG.md
+++ b/packages/flow-lineage/CHANGELOG.md
@@ -2,6 +2,12 @@
# Change Log
+## [3.1.1] - 2023-12-19
+
+### Patch Changes
+
+- 🔄 Updated the template rendering logic of d3's `foreignObject` for enhanced security against HTML injection. 🛡️
+
## [3.1.0] - 2023-11-27
### Minor Changes
diff --git a/packages/flow-lineage/package.json b/packages/flow-lineage/package.json
index 2397405c5..91c807eb3 100644
--- a/packages/flow-lineage/package.json
+++ b/packages/flow-lineage/package.json
@@ -1,6 +1,6 @@
{
"name": "@cldcvr/flow-lineage",
- "version": "3.1.0",
+ "version": "3.1.1",
"description": "Lineage dependency for flow design system",
"module": "dist/flow-lineage.es.js",
"main": "dist/flow-lineage.cjs.js",
diff --git a/packages/flow-lineage/src/components/f-lineage/draw/draw-nodes.ts b/packages/flow-lineage/src/components/f-lineage/draw/draw-nodes.ts
index 8d7fb325f..4ed017325 100644
--- a/packages/flow-lineage/src/components/f-lineage/draw/draw-nodes.ts
+++ b/packages/flow-lineage/src/components/f-lineage/draw/draw-nodes.ts
@@ -164,8 +164,8 @@ export default function drawNodes(params: DrawLineageParams) {
element.reDrawChunk(+pageNo, d.level);
}
})
- .html(node => {
- return element.doTemplateHotUpdate(node);
+ .each(function (d) {
+ element.doTemplateHotUpdate(d, this as unknown as HTMLElement);
});
/**
@@ -355,10 +355,8 @@ export default function drawNodes(params: DrawLineageParams) {
d.fRightClick(event, d);
}
})
- /* eslint-disable @typescript-eslint/ban-ts-comment */
- //@ts-ignore
- .html(node => {
- return element.doTemplateHotUpdate(node, true);
+ .each(function (d) {
+ element.doTemplateHotUpdate(d, this as unknown as HTMLElement, true);
});
drawLinks({
diff --git a/packages/flow-lineage/src/components/f-lineage/draw/hot-reload-proxies.ts b/packages/flow-lineage/src/components/f-lineage/draw/hot-reload-proxies.ts
index b591f3d5f..18b31a25b 100644
--- a/packages/flow-lineage/src/components/f-lineage/draw/hot-reload-proxies.ts
+++ b/packages/flow-lineage/src/components/f-lineage/draw/hot-reload-proxies.ts
@@ -23,8 +23,12 @@ export default function getProxies(element: FLineage) {
if (nodeElement) {
d3.select(element.svg)
.select(`#${target.__id__}-foreign-object`)
- .html(() => {
- return element.doTemplateHotUpdate(nodeElement, nodeElement.isChildren);
+ .call(function (d) {
+ element.doTemplateHotUpdate(
+ nodeElement,
+ d.node() as unknown as HTMLElement,
+ nodeElement.isChildren
+ );
});
}
}
@@ -60,8 +64,12 @@ export default function getProxies(element: FLineage) {
nodeElement.fData = target[key];
d3.select(element.svg)
.select(`#${target.__id__}-foreign-object`)
- .html(() => {
- return element.doTemplateHotUpdate(nodeElement, nodeElement.isChildren);
+ .call(function (d) {
+ element.doTemplateHotUpdate(
+ nodeElement,
+ d.node() as unknown as HTMLElement,
+ nodeElement.isChildren
+ );
});
}
}
diff --git a/packages/flow-lineage/src/components/f-lineage/f-lineage.ts b/packages/flow-lineage/src/components/f-lineage/f-lineage.ts
index 2a95475c9..0182de51e 100644
--- a/packages/flow-lineage/src/components/f-lineage/f-lineage.ts
+++ b/packages/flow-lineage/src/components/f-lineage/f-lineage.ts
@@ -1,4 +1,4 @@
-import { html, PropertyValues, unsafeCSS } from "lit";
+import { html, PropertyValues, render, unsafeCSS } from "lit";
import { customElement, property, query, queryAssignedElements } from "lit/decorators.js";
import eleStyle from "./f-lineage.scss?inline";
import globalStyle from "./f-lineage-global.scss?inline";
@@ -20,7 +20,7 @@ import lowlightPath from "./highlight/lowlight-path";
import createHierarchy from "./create/create-hierarchy";
import { FButton, FDiv, FIcon, FIconButton, FPopover, FText } from "@cldcvr/flow-core";
import { FRoot } from "@cldcvr/flow-core";
-import { debounce, getComputedHTML } from "../../utils";
+import { debounce } from "../../utils";
import getProxies from "./draw/hot-reload-proxies";
import { ref, createRef, Ref } from "lit/directives/ref.js";
import { injectCss } from "@cldcvr/flow-core-config";
@@ -613,26 +613,22 @@ export class FLineage extends FRoot {
this.progressElement.setAttribute("width", "500px");
this.progressElement.innerHTML = "No data to display";
}
-
- // console.timeEnd("Total duration");
- // console.groupEnd();
}
isSafari() {
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
}
- /* eslint-disable @typescript-eslint/no-unused-vars */
- /* eslint-disable @typescript-eslint/ban-ts-comment */
- // @ts-ignore
- doTemplateHotUpdate(node: LineageNodeElement, isChildNode = false) {
+ doTemplateHotUpdate(
+ node: LineageNodeElement,
+ nodeSVGElement: HTMLElement | null,
+ isChildNode = false
+ ): void {
try {
if (isChildNode) {
- if (node.fNodeTemplate) {
- return getComputedHTML(node.fNodeTemplate(node));
- } else {
- return this["children-node-template"]
- ? getComputedHTML(this["children-node-template"](node))
- : ``;
+ if (node.fNodeTemplate && nodeSVGElement) {
+ render(node.fNodeTemplate(node), nodeSVGElement);
+ } else if (this["children-node-template"] && nodeSVGElement) {
+ render(this["children-node-template"](node), nodeSVGElement);
}
} else {
if (node.fChildren) {
@@ -641,28 +637,35 @@ export class FLineage extends FRoot {
} else {
node.childrenToggle = html``;
}
- if (node.fNodeTemplate) {
- return getComputedHTML(node.fNodeTemplate(node));
- } else {
- return this["node-template"] ? getComputedHTML(this["node-template"](node)) : ``;
+ if (node.fNodeTemplate && nodeSVGElement) {
+ render(node.fNodeTemplate(node), nodeSVGElement);
+ } else if (this["node-template"] && nodeSVGElement) {
+ render(this["node-template"](node), nodeSVGElement);
}
}
} catch (error: unknown) {
- console.error(`Error reading node ${node.id}.fData`);
- return `
{`tony: { //Unique node ID
fData: {
@@ -524,13 +524,13 @@ Each node is identified by a unique ID.
Nodes are represented through templates, you can pass custom markup to create a custom nodes.
-
+
- Node example
+ Node example
- Node data example
+ Node data example
{`tony: {
fNodeTemplate:\`
@@ -552,13 +552,13 @@ The data required by each node needs to be present in the node schema.
###### Note: Use “\$\{node.data.key}” to access fData in your node template.
-
+
- Node example
+ Node example
- Node data example
+ Node data example
{`tony: {
fData: { //Data goes here
@@ -577,13 +577,13 @@ The data required by each node needs to be present in the node schema.
Each node can have fChildren.
-
+
- Node example
+ Node example
- Node data example
+ Node data example
{`tony: { //Unique node ID
@@ -613,12 +613,12 @@ Boolean that defines whether node children will be visible on load or not.
###### Note: On load, all node children are collpased/hidden. Clicking on a node will reveal its children
-
+
- Node example
+ Node example
- Node data example
+ Node data example
add code here