You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by pp...@apache.org on 2023/10/31 12:57:27 UTC

(atlas) branch master updated: ATLAS-4805: (UI)Text editor improvements and code clean up

This is an automated email from the ASF dual-hosted git repository.

ppawar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git


The following commit(s) were added to refs/heads/master by this push:
     new 35ce839f0 ATLAS-4805: (UI)Text editor improvements and code clean up
35ce839f0 is described below

commit 35ce839f0a24f8080ca5d0f6841e261c8becb347
Author: Farhan Khan <fa...@freestoneinfotech.com>
AuthorDate: Tue Oct 31 16:26:54 2023 +0530

    ATLAS-4805: (UI)Text editor improvements and code clean up
    
    Signed-off-by: Prasad Pawar <pr...@cloudera.com>
---
 dashboardv2/public/css/scss/trumbowyg.scss                  |  2 ++
 dashboardv2/public/js/utils/Helper.js                       |  5 +++++
 dashboardv2/public/js/utils/Utils.js                        | 13 ++++++++++---
 .../business_metadata/BusinessMetadataDetailLayoutView.js   |  3 ++-
 .../business_metadata/BusinessMetadataTableLayoutView.js    |  2 +-
 .../public/js/views/tag/TagAttributeDetailLayoutView.js     |  6 ++++--
 dashboardv3/public/css/scss/trumbowyg.scss                  |  2 ++
 dashboardv3/public/js/utils/Helper.js                       |  5 +++++
 dashboardv3/public/js/utils/Utils.js                        | 13 ++++++++++---
 .../business_metadata/BusinessMetadataDetailLayoutView.js   |  3 ++-
 .../business_metadata/BusinessMetadataTableLayoutView.js    |  2 +-
 .../public/js/views/tag/TagAttributeDetailLayoutView.js     |  6 ++++--
 12 files changed, 48 insertions(+), 14 deletions(-)

diff --git a/dashboardv2/public/css/scss/trumbowyg.scss b/dashboardv2/public/css/scss/trumbowyg.scss
index a5463eadd..88a7a9775 100644
--- a/dashboardv2/public/css/scss/trumbowyg.scss
+++ b/dashboardv2/public/css/scss/trumbowyg.scss
@@ -289,6 +289,8 @@ $slow-transition-duration: 300ms !default;
 }
 
 .trumbowyg-dropdown {
+    top: auto !important;
+    bottom: 0px;
     max-width: 300px;
     max-height: 250px;
     overflow-y: auto;
diff --git a/dashboardv2/public/js/utils/Helper.js b/dashboardv2/public/js/utils/Helper.js
index 8efeeb9c5..86a7aac49 100644
--- a/dashboardv2/public/js/utils/Helper.js
+++ b/dashboardv2/public/js/utils/Helper.js
@@ -134,6 +134,11 @@ define(['require',
             $('.trumbowyg').css('border', '1px solid #8fa5b1');
         }
     });
+    $('body').on('change', '.trumbowyg-modal-box input[name="url"], .trumbowyg-modal-box input[name="text"]', function(e) {
+        var inputValue = e.target.value;
+        var sanitizedValue = Utils.sanitizeHtmlContent({ data: inputValue });
+        e.target.value = sanitizedValue;
+    });
     $('body').on('keyup input', '.modal-body', function(e) {
         var target = e.target,
             isGlossary = (e.target.dataset.id === "searchTerm" || e.target.dataset.id === "searchCategory") ? true : false; // assign term/category modal
diff --git a/dashboardv2/public/js/utils/Utils.js b/dashboardv2/public/js/utils/Utils.js
index 25ce3190e..b4e02971e 100644
--- a/dashboardv2/public/js/utils/Utils.js
+++ b/dashboardv2/public/js/utils/Utils.js
@@ -1296,18 +1296,25 @@ define(['require', 'utils/Globals', 'pnotify', 'utils/Messages', 'utils/Enums',
             $parent.css('border', '1px solid #8fa5b1');
         }).on('tbwchange', function(e) {
             options.callback ? options.callback(e) : null;
+            e.target.value = Utils.sanitizeHtmlContent({ data: e.target.value });
         }).on('tbwmodalopen', function(e) {
             $('input[name="title"], input[name="target"]').parent().css('display', 'none');
         });
     }
 
     Utils.sanitizeHtmlContent = function(options) {
-        var editorContent, cleanedContent;
+        var editorContent, cleanedContent,
+            config = {
+                ALLOWED_TAGS: ['b', 'em', 'strong', 'u', 'a', 'ul', 'ol', 'li', 'p', 'strike', 'h1', 'h2', 'h3', 'h4'],
+                ALLOWED_ATTR: ['href'],
+                FORBID_TAGS: ['script', 'img', 'iframe', 'svg', 'title'],
+                FORBID_ATTR: ['onmouseover', 'onload', 'onclick', 'onerror']
+            };
         editorContent = options.selector ? $(options.selector).trumbowyg('html') : options.data;
         if (options && editorContent) {
-            cleanedContent = DOMPurify.sanitize(editorContent, { FORBID_TAGS: ['img', 'script', 'iframe', 'embed', 'svg', 'meta'], ALLOWED_ATTR: ['target', 'href'] });
+            cleanedContent = DOMPurify.sanitize(editorContent, config);
         }
-        return cleanedContent;
+        return cleanedContent || "";
     }
     //-----------------------------------------END---------------------//
 
diff --git a/dashboardv2/public/js/views/business_metadata/BusinessMetadataDetailLayoutView.js b/dashboardv2/public/js/views/business_metadata/BusinessMetadataDetailLayoutView.js
index b3d1ff337..f08fb3424 100644
--- a/dashboardv2/public/js/views/business_metadata/BusinessMetadataDetailLayoutView.js
+++ b/dashboardv2/public/js/views/business_metadata/BusinessMetadataDetailLayoutView.js
@@ -58,8 +58,9 @@ define(['require',
             },
             renderDetail: function() {
                 this.ui.title.html('<span>' + this.model.get('name') + '</span>');
+                var sanitizedDescription = Utils.sanitizeHtmlContent({data: this.model.get("description")});
                 if (this.model.get('description')) {
-                    this.isTextTypeChecked ? this.ui.description.text(this.model.get('description')) : this.ui.description.html(this.model.get('description'));
+                    this.isTextTypeChecked ? this.ui.description.text(sanitizedDescription) : this.ui.description.html(sanitizedDescription);
                 }
             },
             onDestroy: function() {
diff --git a/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js b/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
index ccff1fdaf..c02236843 100644
--- a/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
+++ b/dashboardv2/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
@@ -301,7 +301,7 @@ define(['require',
                                 if (description.length > 50) {
                                     description = description.substr(0, 50) + "...";
                                 }
-                                return description;
+                                return Utils.sanitizeHtmlContent({data: description});
                             }
                         })
                     },
diff --git a/dashboardv2/public/js/views/tag/TagAttributeDetailLayoutView.js b/dashboardv2/public/js/views/tag/TagAttributeDetailLayoutView.js
index 3b26f0e0e..14f1b8c3e 100644
--- a/dashboardv2/public/js/views/tag/TagAttributeDetailLayoutView.js
+++ b/dashboardv2/public/js/views/tag/TagAttributeDetailLayoutView.js
@@ -57,7 +57,7 @@ define(['require',
                 events["click " + this.ui.editButton] = 'onEditButton';
                 events["change " + this.ui.textType] = function(e) {
                     this.isTextTypeChecked = !this.isTextTypeChecked;
-                    this.isTextTypeChecked ? this.ui.description.text(this.model.get("description")) : this.ui.description.html(this.model.get("description"));
+                    this.isTextTypeChecked ? this.ui.description.text(this.sanitizedDescription) : this.ui.description.html(this.sanitizedDescription);
                 };
                 return events;
             },
@@ -68,6 +68,7 @@ define(['require',
             initialize: function(options) {
                 _.extend(this, _.pick(options, 'tag', 'collection', 'enumDefCollection'));
                 this.isTextTypeChecked = false;
+                this.sanitizedDescription = "";
             },
             bindEvents: function() {
                 this.listenTo(this.collection, 'reset', function() {
@@ -127,7 +128,8 @@ define(['require',
                     }
                 this.ui.title.html('<span>' + (Utils.getName(this.model.toJSON())) + '</span>');
                 if (this.model.get("description")) {
-                    this.isTextTypeChecked ? this.ui.description.text(this.model.get("description")) : this.ui.description.html(this.model.get("description"));
+                    this.sanitizedDescription = Utils.sanitizeHtmlContent({data: this.model.get("description")});
+                    this.isTextTypeChecked ? this.ui.description.text(this.sanitizedDescription) : this.ui.description.html(this.sanitizedDescription);
                 }
                 if (attributeDefs) {
                     if (!_.isArray(attributeDefs)) {
diff --git a/dashboardv3/public/css/scss/trumbowyg.scss b/dashboardv3/public/css/scss/trumbowyg.scss
index a5463eadd..88a7a9775 100644
--- a/dashboardv3/public/css/scss/trumbowyg.scss
+++ b/dashboardv3/public/css/scss/trumbowyg.scss
@@ -289,6 +289,8 @@ $slow-transition-duration: 300ms !default;
 }
 
 .trumbowyg-dropdown {
+    top: auto !important;
+    bottom: 0px;
     max-width: 300px;
     max-height: 250px;
     overflow-y: auto;
diff --git a/dashboardv3/public/js/utils/Helper.js b/dashboardv3/public/js/utils/Helper.js
index 71101a74f..c942ac534 100644
--- a/dashboardv3/public/js/utils/Helper.js
+++ b/dashboardv3/public/js/utils/Helper.js
@@ -140,6 +140,11 @@ define(['require',
             $('.trumbowyg').css('border', '1px solid #8fa5b1');
         }
     });
+    $('body').on('change', '.trumbowyg-modal-box input[name="url"], .trumbowyg-modal-box input[name="text"]', function(e) {
+        var inputValue = e.target.value;
+        var sanitizedValue = Utils.sanitizeHtmlContent({ data: inputValue });
+        e.target.value = sanitizedValue;
+    });
     $('body').on('keyup input', '.modal-body', function(e) {
         var target = e.target,
             isGlossary = (e.target.dataset.id === "searchTerm" || e.target.dataset.id === "searchCategory") ? true : false; // assign term/category modal
diff --git a/dashboardv3/public/js/utils/Utils.js b/dashboardv3/public/js/utils/Utils.js
index f60d4be5b..dcd737c01 100644
--- a/dashboardv3/public/js/utils/Utils.js
+++ b/dashboardv3/public/js/utils/Utils.js
@@ -1311,18 +1311,25 @@ define(['require', 'utils/Globals', 'pnotify', 'utils/Messages', 'utils/Enums',
             $parent.css('border', '1px solid #8fa5b1');
         }).on('tbwchange', function(e) {
             options.callback ? options.callback(e) : null;
+            e.target.value = Utils.sanitizeHtmlContent({ data: e.target.value });
         }).on('tbwmodalopen', function(e) {
             $('input[name="title"], input[name="target"]').parent().css('display', 'none');
         });
     }
 
     Utils.sanitizeHtmlContent = function(options) {
-        var editorContent, cleanedContent;
+        var editorContent, cleanedContent,
+            config = {
+                ALLOWED_TAGS: ['b', 'em', 'strong', 'u', 'a', 'ul', 'ol', 'li', 'p', 'strike', 'h1', 'h2', 'h3', 'h4'],
+                ALLOWED_ATTR: ['href'],
+                FORBID_TAGS: ['script', 'img', 'iframe', 'svg', 'title'],
+                FORBID_ATTR: ['onmouseover', 'onload', 'onclick', 'onerror', 'src']
+            };
         editorContent = options.selector ? $(options.selector).trumbowyg('html') : options.data;
         if (options && editorContent) {
-            cleanedContent = DOMPurify.sanitize(editorContent, { FORBID_TAGS: ['img', 'script', 'iframe', 'embed', 'svg', 'meta'], ALLOWED_ATTR: ['target', 'href'] });
+            cleanedContent = DOMPurify.sanitize(editorContent, config);
         }
-        return cleanedContent;
+        return cleanedContent || "";
     }
     //-----------------------------------------END---------------------//
 
diff --git a/dashboardv3/public/js/views/business_metadata/BusinessMetadataDetailLayoutView.js b/dashboardv3/public/js/views/business_metadata/BusinessMetadataDetailLayoutView.js
index 0921d4371..b7d12d665 100644
--- a/dashboardv3/public/js/views/business_metadata/BusinessMetadataDetailLayoutView.js
+++ b/dashboardv3/public/js/views/business_metadata/BusinessMetadataDetailLayoutView.js
@@ -61,8 +61,9 @@ define(['require',
             },
             renderDetail: function() {
                 this.ui.title.html('<span>' + this.model.get('name') + '</span>');
+                var sanitizedDescription = Utils.sanitizeHtmlContent({data: this.model.get("description")});
                 if (this.model.get('description')) {
-                    this.isTextTypeChecked ? this.ui.description.text(this.model.get('description')) : this.ui.description.html(this.model.get('description'));
+                    this.isTextTypeChecked ? this.ui.description.text(sanitizedDescription) : this.ui.description.html(sanitizedDescription);
                 }
             }
         });
diff --git a/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js b/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
index 2e8d7c10c..274630656 100644
--- a/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
+++ b/dashboardv3/public/js/views/business_metadata/BusinessMetadataTableLayoutView.js
@@ -301,7 +301,7 @@ define(['require',
                                 if (description.length > 50) {
                                     description = description.substr(0, 50) + "...";
                                 }
-                                return description;
+                                return Utils.sanitizeHtmlContent({data: description});
                             }
                         })
                     },
diff --git a/dashboardv3/public/js/views/tag/TagAttributeDetailLayoutView.js b/dashboardv3/public/js/views/tag/TagAttributeDetailLayoutView.js
index 2a22d3212..95d706281 100644
--- a/dashboardv3/public/js/views/tag/TagAttributeDetailLayoutView.js
+++ b/dashboardv3/public/js/views/tag/TagAttributeDetailLayoutView.js
@@ -61,7 +61,7 @@ define(['require',
                 };
                 events["change " + this.ui.textType] = function(e) {
                     this.isTextTypeChecked = !this.isTextTypeChecked;
-                    this.isTextTypeChecked ? this.ui.description.text(this.model.get("description")) : this.ui.description.html(this.model.get("description"));
+                    this.isTextTypeChecked ? this.ui.description.text(this.sanitizedDescription) : this.ui.description.html(this.sanitizedDescription);
                 };
                 return events;
             },
@@ -72,6 +72,7 @@ define(['require',
             initialize: function(options) {
                 _.extend(this, _.pick(options, 'tag', 'collection', 'enumDefCollection'));
                 this.isTextTypeChecked = false;
+                this.sanitizedDescription = "";
             },
             bindEvents: function() {
                 this.listenTo(this.collection, 'reset', function() {
@@ -131,7 +132,8 @@ define(['require',
                     }
                 this.ui.title.html('<span>' + (Utils.getName(this.model.toJSON())) + '</span>');
                 if (this.model.get("description")) {
-                    this.isTextTypeChecked ? this.ui.description.text(this.model.get("description")) : this.ui.description.html(this.model.get("description"));
+                    this.sanitizedDescription = Utils.sanitizeHtmlContent({data: this.model.get("description")});
+                    this.isTextTypeChecked ? this.ui.description.text(this.sanitizedDescription) : this.ui.description.html(this.sanitizedDescription);
                 }
                 if (attributeDefs) {
                     if (!_.isArray(attributeDefs)) {