From 3e3dbccf5db24e47d9ff1dc68ab8a9be584b3b82 Mon Sep 17 00:00:00 2001 From: wangziliang Date: Tue, 25 Jun 2024 08:38:07 +0000 Subject: [PATCH] fix CVE-2023-50379 --- ambari.spec | 52 +++-- ...rt-fix-Ambari-Spoofing-Vulnerability.patch | 219 ++++++++++++++++++ 2 files changed, 247 insertions(+), 24 deletions(-) create mode 100644 backport-fix-Ambari-Spoofing-Vulnerability.patch diff --git a/ambari.spec b/ambari.spec index b1e5c83..50436fc 100644 --- a/ambari.spec +++ b/ambari.spec @@ -9,7 +9,7 @@ Name: ambari Version: 2.7.6 -Release: 3 +Release: 4 Summary: Ambari Main License: Apache 2.0 Vendor: Apache Software Foundation @@ -26,6 +26,7 @@ Source8: node-v8.6.0-linux-x64.tar.gz Source9: node-v8.6.0-linux-arm64.tar.gz Patch0: 0001-python3-support.patch +Patch1: backport-fix-Ambari-Spoofing-Vulnerability.patch Group: Development Packager: Apache Software Foundation @@ -47,32 +48,32 @@ Apache Ambari is a tool for provisioning, managing, and monitoring Apache Hadoop %build cp %{SOURCE1} ./settings.xml -mkdir -p /home/abuild/rpmbuild/BUILD/apache-ambari-2.7.6/ambari-infra/ambari-infra-solr-client/target/migrate/ -mkdir -p /home/abuild/.m2/repository/com/github/eirslett/yarn/0.23.2/yarn-0.23.2./ -mkdir -p /home/abuild/.m2/repository/com/github/eirslett/npm/2.15.0/ -mkdir -p /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-admin/src/main/resources/ui/admin-web/app -mkdir -p /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-metrics/ambari-metrics-timelineservice/target/embedded/ -cp %{SOURCE4} /home/abuild/.m2/repository/com/github/eirslett/yarn/0.23.2/yarn-0.23.2./yarn-v0.23.2.tar.gz -cp %{SOURCE5} /home/abuild/.m2/repository/com/github/eirslett/npm/2.15.0/npm-2.15.0.tar.gz -cp %{SOURCE6} /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-admin/src/main/resources/ui/admin-web +mkdir -p %{_builddir}/apache-ambari-2.7.6/ambari-infra/ambari-infra-solr-client/target/migrate/ +mkdir -p ./.m2/repository/com/github/eirslett/yarn/0.23.2/yarn-0.23.2./ +mkdir -p ./.m2/repository/com/github/eirslett/npm/2.15.0/ +mkdir -p %{_builddir}/ambari-release-2.7.6/ambari-admin/src/main/resources/ui/admin-web/app +mkdir -p %{_builddir}/ambari-release-2.7.6/ambari-metrics/ambari-metrics-timelineservice/target/embedded/ +cp %{SOURCE4} ./.m2/repository/com/github/eirslett/yarn/0.23.2/yarn-0.23.2./yarn-v0.23.2.tar.gz +cp %{SOURCE5} ./.m2/repository/com/github/eirslett/npm/2.15.0/npm-2.15.0.tar.gz +cp %{SOURCE6} %{_builddir}/ambari-release-2.7.6/ambari-admin/src/main/resources/ui/admin-web mvn install:install-file -Dfile=%{SOURCE7} -s settings.xml -DgroupId=com.github.eirslett -DartifactId=yarn -Dversion=1.1.0 -Dpackaging=tar.gz -tar -zxvf /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-admin/src/main/resources/ui/admin-web/bower_source.tar.gz -C /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-admin/src/main/resources/ui/admin-web/app/ -wget https://repo.huaweicloud.com/apache/phoenix/apache-phoenix-5.0.0-HBase-2.0/bin/apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -P /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-metrics/ambari-metrics-timelineservice/target/embedded/ -mv /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-metrics/ambari-metrics-timelineservice/target/embedded/apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-metrics/ambari-metrics-timelineservice/target/embedded/phoenix.tar.gz -sed -i 's|archive.apache.org/dist|repo.huaweicloud.com/apache|g' /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-metrics/pom.xml -sed -i 's|downloads.apache.org/phoenix|repo.huaweicloud.com/apache/phoenix|g' /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-metrics/pom.xml -sed -i 's|dl.grafana.com/oss/release|repo.huaweicloud.com/grafana/6.7.4|g' /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-metrics/pom.xml -sed -i 's|registry.yarnpkg.com|repo.huaweicloud.com/repository/npm|g' /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-web/yarn.lock -sed -i 's|registry.yarnpkg.com|repo.huaweicloud.com/repository/npm|g' /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-logsearch/ambari-logsearch-web/yarn.lock -sed -i 's|repo1.maven.org/maven2|repo.huaweicloud.com/maven|g' /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-infra/ambari-infra-solr-client/pom.xml -sed -i 's|/usr/lib/python2.6|/usr/lib/python3.9|g' /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-server/src/main/python/ambari_server/serverConfiguration.py -sed -i 's|/usr/lib/python2.6|/usr/lib/python3.9|g' /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-metrics/ambari-metrics-host-monitoring/pom.xml -sed -i 's|/usr/lib/python2.6|/usr/lib/python3.9|g' /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-metrics/pom.xml +tar -zxvf %{_builddir}/ambari-release-2.7.6/ambari-admin/src/main/resources/ui/admin-web/bower_source.tar.gz -C %{_builddir}/ambari-release-2.7.6/ambari-admin/src/main/resources/ui/admin-web/app/ +wget https://repo.huaweicloud.com/apache/phoenix/apache-phoenix-5.0.0-HBase-2.0/bin/apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -P %{_builddir}/ambari-release-2.7.6/ambari-metrics/ambari-metrics-timelineservice/target/embedded/ +mv %{_builddir}/ambari-release-2.7.6/ambari-metrics/ambari-metrics-timelineservice/target/embedded/apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz %{_builddir}/ambari-release-2.7.6/ambari-metrics/ambari-metrics-timelineservice/target/embedded/phoenix.tar.gz +sed -i 's|archive.apache.org/dist|repo.huaweicloud.com/apache|g' %{_builddir}/ambari-release-2.7.6/ambari-metrics/pom.xml +sed -i 's|downloads.apache.org/phoenix|repo.huaweicloud.com/apache/phoenix|g' %{_builddir}/ambari-release-2.7.6/ambari-metrics/pom.xml +sed -i 's|dl.grafana.com/oss/release|repo.huaweicloud.com/grafana/6.7.4|g' %{_builddir}/ambari-release-2.7.6/ambari-metrics/pom.xml +sed -i 's|registry.yarnpkg.com|repo.huaweicloud.com/repository/npm|g' %{_builddir}/ambari-release-2.7.6/ambari-web/yarn.lock +sed -i 's|registry.yarnpkg.com|repo.huaweicloud.com/repository/npm|g' %{_builddir}/ambari-release-2.7.6/ambari-logsearch/ambari-logsearch-web/yarn.lock +sed -i 's|repo1.maven.org/maven2|repo.huaweicloud.com/maven|g' %{_builddir}/ambari-release-2.7.6/ambari-infra/ambari-infra-solr-client/pom.xml +sed -i 's|/usr/lib/python2.6|/usr/lib/python3.9|g' %{_builddir}/ambari-release-2.7.6/ambari-server/src/main/python/ambari_server/serverConfiguration.py +sed -i 's|/usr/lib/python2.6|/usr/lib/python3.9|g' %{_builddir}/ambari-release-2.7.6/ambari-metrics/ambari-metrics-host-monitoring/pom.xml +sed -i 's|/usr/lib/python2.6|/usr/lib/python3.9|g' %{_builddir}/ambari-release-2.7.6/ambari-metrics/pom.xml npm config set registry https://repo.huaweicloud.com/repository/npm npm cache clean -f -tar -zxvf /home/abuild/.m2/repository/com/github/eirslett/yarn/0.23.2/yarn-0.23.2./yarn-v0.23.2.tar.gz -C /home/abuild/.m2/repository/com/github/eirslett/yarn/0.23.2/ -/home/abuild/.m2/repository/com/github/eirslett/yarn/0.23.2/dist/bin/yarn config set registry https://repo.huaweicloud.com/repository/npm/ -g -rm -rf /home/abuild/rpmbuild/BUILD/ambari-release-2.7.6/ambari-common/src/main/python/ambari_commons/libs/ppc64le/_posixsubprocess.so +tar -zxvf ./.m2/repository/com/github/eirslett/yarn/0.23.2/yarn-0.23.2./yarn-v0.23.2.tar.gz -C ./.m2/repository/com/github/eirslett/yarn/0.23.2/ +./.m2/repository/com/github/eirslett/yarn/0.23.2/dist/bin/yarn config set registry https://repo.huaweicloud.com/repository/npm/ -g +rm -rf %{_builddir}/ambari-release-2.7.6/ambari-common/src/main/python/ambari_commons/libs/ppc64le/_posixsubprocess.so %install mkdir -p %{buildroot}/opt/ambari @@ -93,6 +94,9 @@ cp -rf ./ambari-agent/target/rpm/ambari-agent/RPMS/x86_64 %{buildroot}/opt/ambar /opt %changelog +* Tue Jun 25 2024 wangziliang 2.7.6-4 +- fix CVE-2023-50379 + * Thu Jul 13 2023 wenweijian 2.7.6-3 - Support python3 diff --git a/backport-fix-Ambari-Spoofing-Vulnerability.patch b/backport-fix-Ambari-Spoofing-Vulnerability.patch new file mode 100644 index 0000000..e83ee85 --- /dev/null +++ b/backport-fix-Ambari-Spoofing-Vulnerability.patch @@ -0,0 +1,219 @@ +From aa531afa81eb472265c91e7e6e99b2be379a26b0 Mon Sep 17 00:00:00 2001 +From: Brahma Reddy Battula +Date: Wed, 29 Nov 2023 19:58:33 +0530 +Subject: [PATCH] fix Ambari Spoofing Vulnerability + +--- + .../python/resource_management/core/shell.py | 27 +++++++++++-------- + ambari-server/pom.xml | 5 ++++ + .../AmbariManagementControllerImpl.java | 3 ++- + .../internal/AlertTargetResourceProvider.java | 3 ++- + .../internal/RequestResourceProvider.java | 3 ++- + .../custom_actions/scripts/check_host.py | 3 ++- + .../ui/app/components/diffTooltip.js | 6 ++--- + .../resources/ui/app/helpers/escapeAcl.js | 2 +- + 8 files changed, 33 insertions(+), 19 deletions(-) + +diff --git a/ambari-common/src/main/python/resource_management/core/shell.py b/ambari-common/src/main/python/resource_management/core/shell.py +index d815732a3b..70fae97761 100644 +--- a/ambari-common/src/main/python/resource_management/core/shell.py ++++ b/ambari-common/src/main/python/resource_management/core/shell.py +@@ -104,7 +104,7 @@ def checked_call(command, quiet=False, logoutput=None, stdout=subprocess32.PIPE, + @log_function_call + def call(command, quiet=False, logoutput=None, stdout=subprocess.PIPE,stderr=subprocess.STDOUT, + cwd=None, env=None, preexec_fn=preexec_fn, user=None, wait_for_finish=True, timeout=None, on_timeout=None, +- path=None, sudo=False, on_new_line=None, tries=1, try_sleep=0, timeout_kill_strategy=TerminateStrategy.TERMINATE_PARENT, returns=[0]): ++ path=None, sudo=False, on_new_line=None, tries=1, try_sleep=0, timeout_kill_strategy=TerminateStrategy.TERMINATE_PARENT, shell=True, returns=[0]): + """ + Execute the shell command despite failures. + @return: return_code, output +@@ -112,7 +112,7 @@ def call(command, quiet=False, logoutput=None, stdout=subprocess32.PIPE,stderr=s + return _call_wrapper(command, logoutput=logoutput, throw_on_failure=False, stdout=stdout, stderr=stderr, + cwd=cwd, env=env, preexec_fn=preexec_fn, user=user, wait_for_finish=wait_for_finish, + on_timeout=on_timeout, timeout=timeout, path=path, sudo=sudo, on_new_line=on_new_line, +- tries=tries, try_sleep=try_sleep, timeout_kill_strategy=timeout_kill_strategy, returns=returns) ++ tries=tries, try_sleep=try_sleep, timeout_kill_strategy=timeout_kill_strategy, shell=shell, returns=returns) + + @log_function_call + def non_blocking_call(command, quiet=False, stdout=None, stderr=None, +@@ -166,7 +166,7 @@ def _call_wrapper(command, **kwargs): + + def _call(command, logoutput=None, throw_on_failure=True, stdout=subprocess.PIPE,stderr=subprocess.STDOUT, + cwd=None, env=None, preexec_fn=preexec_fn, user=None, wait_for_finish=True, timeout=None, on_timeout=None, +- path=None, sudo=False, on_new_line=None, tries=1, try_sleep=0, timeout_kill_strategy=TerminateStrategy.TERMINATE_PARENT, returns=[0]): ++ path=None, sudo=False, on_new_line=None, tries=1, try_sleep=0, timeout_kill_strategy=TerminateStrategy.TERMINATE_PARENT, shell=True, returns=[0]): + """ + Execute shell command + +@@ -200,18 +200,23 @@ def _call(command, logoutput=None, throw_on_failure=True, stdout=subprocess32.PI + command = as_sudo(command, env=env) + elif user: + command = as_user(command, user, env=env) +- +- # convert to string and escape +- if isinstance(command, (list, tuple)): +- command = string_cmd_from_args_list(command) +- ++ ++ if isinstance(command, basestring): ++ subprocess32_command = [command] ++ elif shell: ++ # TODO: Remove this condition after reviewing 'shell' flag requirement where shell.call() is being used. ++ subprocess32_command = [string_cmd_from_args_list(command)] ++ else: ++ subprocess32_command = command ++ + # replace placeholder from as_sudo / as_user if present + env_str = _get_environment_str(env) + for placeholder, replacement in PLACEHOLDERS_TO_STR.items(): +- command = command.replace(placeholder, replacement.format(env_str=env_str)) ++ subprocess32_command = [cmd.replace(placeholder, replacement.format(env_str=env_str)) for cmd in subprocess32_command] + +- # --noprofile is used to preserve PATH set for ambari-agent +- subprocess_command = ["/bin/bash","--login","--noprofile","-c", command] ++ if shell: ++ # --noprofile is used to preserve PATH set for ambari-agent ++ subprocess32_command = ["/bin/bash","--login","--noprofile","-c"] + subprocess32_command + + # don't create stdout and stderr pipes, because forked process will not be able to use them if current process dies + # creating pipes may lead to the forked process silent crash +diff --git a/ambari-server/pom.xml b/ambari-server/pom.xml +index 5081451e4d..afcc387607 100644 +--- a/ambari-server/pom.xml ++++ b/ambari-server/pom.xml +@@ -1162,6 +1162,11 @@ + commons-compress + 1.5 + ++ ++ org.apache.commons ++ commons-text ++ 1.10.0 ++ + + uk.com.robust-it + cloning +diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java +index 5156746c1f..46d7cc0717 100644 +--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java ++++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java +@@ -245,6 +245,7 @@ import org.apache.commons.collections.MapUtils; + import org.apache.commons.lang.BooleanUtils; + import org.apache.commons.lang.StringUtils; + import org.apache.commons.lang.math.NumberUtils; ++import org.apache.commons.text.StringEscapeUtils; + import org.apache.http.HttpStatus; + import org.apache.http.client.utils.URIBuilder; + import org.slf4j.Logger; +@@ -4084,7 +4085,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle + String requestContext = ""; + + if (requestProperties != null) { +- requestContext = requestProperties.get(REQUEST_CONTEXT_PROPERTY); ++ requestContext = StringEscapeUtils.escapeHtml4(requestProperties.get(REQUEST_CONTEXT_PROPERTY)); + if (requestContext == null) { + // guice needs a non-null value as there is no way to mark this parameter @Nullable + requestContext = ""; +diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java +index 6489ecf8e3..2483a8fc3c 100644 +--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java ++++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AlertTargetResourceProvider.java +@@ -53,6 +53,7 @@ import org.apache.ambari.server.state.AlertState; + import org.apache.ambari.server.state.alert.AlertGroup; + import org.apache.ambari.server.state.alert.AlertTarget; + import org.apache.commons.lang.StringUtils; ++import org.apache.commons.text.StringEscapeUtils; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + +@@ -275,7 +276,7 @@ public class AlertTargetResourceProvider extends + private void createAlertTargets(Set> requestMaps, Map requestInfoProps) + throws AmbariException { + for (Map requestMap : requestMaps) { +- String name = (String) requestMap.get(ALERT_TARGET_NAME); ++ String name = StringEscapeUtils.escapeHtml4((String) requestMap.get(ALERT_TARGET_NAME)); + String description = (String) requestMap.get(ALERT_TARGET_DESCRIPTION); + String notificationType = (String) requestMap.get(ALERT_TARGET_NOTIFICATION_TYPE); + Collection alertStates = (Collection) requestMap.get(ALERT_TARGET_STATES); +diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestResourceProvider.java +index d5222d203b..6efc8d6f49 100644 +--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestResourceProvider.java ++++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RequestResourceProvider.java +@@ -75,6 +75,7 @@ import org.apache.ambari.server.topology.LogicalRequest; + import org.apache.ambari.server.topology.TopologyManager; + import org.apache.ambari.server.utils.SecretReference; + import org.apache.commons.lang.StringUtils; ++import org.apache.commons.text.StringEscapeUtils; + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + +@@ -431,7 +432,7 @@ public class RequestResourceProvider extends AbstractControllerResourceProvider + // in this case it will be mapped to HTTP 400 Bad Request + requestStatus = HostRoleStatus.valueOf(requestStatusStr); + } +- String abortReason = (String) propertyMap.get(REQUEST_ABORT_REASON_PROPERTY_ID); ++ String abortReason = StringEscapeUtils.escapeHtml4((String) propertyMap.get(REQUEST_ABORT_REASON_PROPERTY_ID)); + String removePendingHostRequests = (String) propertyMap.get(REQUEST_REMOVE_PENDING_HOST_REQUESTS_ID); + + RequestRequest requestRequest = new RequestRequest(clusterNameStr, requestId); +diff --git a/ambari-server/src/main/resources/custom_actions/scripts/check_host.py b/ambari-server/src/main/resources/custom_actions/scripts/check_host.py +index f3a72a610c..5dc3f44d84 100644 +--- a/ambari-server/src/main/resources/custom_actions/scripts/check_host.py ++++ b/ambari-server/src/main/resources/custom_actions/scripts/check_host.py +@@ -40,6 +40,7 @@ from ambari_commons.constants import AMBARI_SUDO_BINARY + from resource_management.core import shell + from resource_management.core.logger import Logger + from resource_management.core.utils import PasswordString ++from shlex import split + + + # WARNING. If you are adding a new host check that is used by cleanup, add it to BEFORE_CLEANUP_HOST_CHECKS +@@ -454,7 +455,7 @@ class CheckHost(Script): + db_connection_check_command = "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{0}{1} {2}".format(agent_cache_dir, + LIBS_PATH_IN_ARCHIVE_SQLA, db_connection_check_command) + +- code, out = shell.call(db_connection_check_command) ++ code, out = shell.call(split(db_connection_check_command, comments=True), shell=False) + + if code == 0: + db_connection_check_structured_output = {"exit_code" : 0, "message": "DB connection check completed successfully!" } +diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/diffTooltip.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/diffTooltip.js +index 1f26c2b389..7655d60df8 100644 +--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/diffTooltip.js ++++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/components/diffTooltip.js +@@ -52,8 +52,8 @@ App.DiffTooltipComponent = Em.Component.extend({ + + caption += fmtString.fmt( + [prefix,item].compact().join('.'), +- (oldV != null && '\'%@\''.fmt(oldV)) || emptyValue, +- (newV != null && '\'%@\''.fmt(newV)) || emptyValue ++ Handlebars.Utils.escapeExpression((oldV != null && '\'%@\''.fmt(oldV))) || emptyValue, ++ Handlebars.Utils.escapeExpression((newV != null && '\'%@\''.fmt(newV))) || emptyValue + ); + }, + initialLabels, +@@ -79,7 +79,7 @@ App.DiffTooltipComponent = Em.Component.extend({ + oldV = ((isAllChanged && changes._accessAllLabels.objectAt(0)) || (queue.get('accessAllLabels') && !isAllChanged))?'*':initialLabels.map(idsToNames).join(',') || emptyValue; + newV = ((isAllChanged && changes._accessAllLabels.objectAt(1)) || (queue.get('accessAllLabels') && !isAllChanged))?'*':currentLabels.map(idsToNames).join(',') || emptyValue; + +- caption += fmtString.fmt('accessible-node-labels', oldV, newV); ++ caption += fmtString.fmt('accessible-node-labels', Handlebars.Utils.escapeExpression(oldV), Handlebars.Utils.escapeExpression(newV)); + } + + queue.get('labels').forEach(function (label) { +diff --git a/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/escapeAcl.js b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/escapeAcl.js +index 125b037357..b8af22c0fa 100644 +--- a/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/escapeAcl.js ++++ b/contrib/views/capacity-scheduler/src/main/resources/ui/app/helpers/escapeAcl.js +@@ -20,7 +20,7 @@ + Ember.Handlebars.helper('escapeACL', function(value) { + var output = ''; + +- value = value || ''; ++ value = Handlebars.Utils.escapeExpression(value || ''); + + if (value.trim() == '') { + output = ' Nobody '; +-- +2.27.0 + -- Gitee