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

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


The following commit(s) were added to refs/heads/master by this push:
     new da484aef26 [fix] Fixed `springboot3` template yaml has the jexl inner 
conflict keyword(#3629)
da484aef26 is described below

commit da484aef26e05fa473abd6008da00764983cebd3
Author: Duansg <siguod...@gmail.com>
AuthorDate: Mon Aug 4 23:32:02 2025 +0800

    [fix] Fixed `springboot3` template yaml has the jexl inner conflict 
keyword(#3629)
    
    Co-authored-by: tomsun28 <tomsu...@outlook.com>
---
 .../common/constants/JexlKeywordsEnum.java         | 62 +++++++++++++++
 .../manager/service/impl/AppServiceImpl.java       |  5 ++
 .../manager/service/impl/MonitorServiceImpl.java   | 22 ++++++
 .../src/main/resources/define/app-springboot3.yml  |  6 +-
 .../hertzbeat/manager/script/YamlCheckScript.java  | 91 ++++++++++++++++++++++
 .../hertzbeat/manager/service/AppServiceTest.java  | 62 +++++++++++++++
 .../manager/service/MonitorServiceTest.java        | 28 +++++++
 7 files changed, 273 insertions(+), 3 deletions(-)

diff --git 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/JexlKeywordsEnum.java
 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/JexlKeywordsEnum.java
new file mode 100644
index 0000000000..f54103ca0a
--- /dev/null
+++ 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/JexlKeywordsEnum.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.common.constants;
+
+import java.util.Arrays;
+
+/**
+ * Jexl keywords enum
+ */
+public enum JexlKeywordsEnum {
+
+    SIZE("size"),
+    EMPTY("empty"),
+    NEW("new"),
+    VAR("var"),
+    RETURN("return"),
+    IF("if"),
+    ELSE("else"),
+    ELSEIF("elseif"),
+    WHILE("while"),
+    DO("do"),
+    FOR("for"),
+    CONTINUE("continue"),
+    BREAK("break"),
+    TRUE("true"),
+    FALSE("false"),
+    NULL("null"),
+    UNDEFINED("undefined");
+
+    private final String keyword;
+
+    JexlKeywordsEnum(String keyword) {
+        this.keyword = keyword;
+    }
+
+    public String getKeyword() {
+        return keyword;
+    }
+
+    public static boolean match(String word) {
+        if (word == null || word.trim().isEmpty()) {
+            return false;
+        }
+        return Arrays.stream(values()).anyMatch(t -> t.keyword.equals(word));
+    }
+
+}
\ No newline at end of file
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AppServiceImpl.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AppServiceImpl.java
index b8f62e4235..470bfb3c45 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AppServiceImpl.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/AppServiceImpl.java
@@ -41,6 +41,7 @@ import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
 import org.apache.hertzbeat.collector.util.CollectUtil;
+import org.apache.hertzbeat.common.constants.JexlKeywordsEnum;
 import org.apache.hertzbeat.common.entity.job.Configmap;
 import org.apache.hertzbeat.common.entity.job.Job;
 import org.apache.hertzbeat.common.entity.job.Metrics;
@@ -458,6 +459,10 @@ public class AppServiceImpl implements AppService, 
InitializingBean {
                     throw new IllegalArgumentException(app.getApp() + " " + 
metrics.getName() + " " 
                             + field.getField() + " can not duplicated.");
                 }
+                if (JexlKeywordsEnum.match(field.getField())) {
+                    throw new IllegalArgumentException(app.getApp() + " " + 
metrics.getName() + " "
+                            + field.getField() + " prohibited keywords.");
+                }
                 fieldsSet.add(field.getField());
             }
         }
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
index e80c25f1c2..ab18501937 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/MonitorServiceImpl.java
@@ -27,6 +27,7 @@ import org.apache.hertzbeat.alert.dao.AlertDefineBindDao;
 import org.apache.hertzbeat.collector.dispatch.DispatchConstants;
 import org.apache.hertzbeat.common.constants.CommonConstants;
 import org.apache.hertzbeat.common.constants.ExportFileConstants;
+import org.apache.hertzbeat.common.constants.JexlKeywordsEnum;
 import org.apache.hertzbeat.common.constants.NetworkConstants;
 import org.apache.hertzbeat.common.constants.SignConstants;
 import org.apache.hertzbeat.common.entity.grafana.GrafanaDashboard;
@@ -436,6 +437,27 @@ public class MonitorServiceImpl implements MonitorService {
                 }
             }
         }
+        checkJobFields(monitorDto.getMonitor().getApp());
+    }
+
+    private void checkJobFields(String app) {
+        if (null == app || app.trim().isEmpty()) {
+            return;
+        }
+        Job job = appService.getAppDefine(app);
+        if (null != job && !CollectionUtils.isEmpty(job.getMetrics())) {
+            for (Metrics metrics : job.getMetrics()) {
+                if (null == metrics.getFields() || 
metrics.getFields().isEmpty()) {
+                    continue;
+                }
+                for (Metrics.Field field : metrics.getFields()) {
+                    if (JexlKeywordsEnum.match(field.getField())) {
+                        throw new IllegalArgumentException(job.getApp() + " " 
+ metrics.getName() + " "
+                                + field.getField() + " prohibited keywords, 
please modify the template information.");
+                    }
+                }
+            }
+        }
     }
 
     @Override
diff --git a/hertzbeat-manager/src/main/resources/define/app-springboot3.yml 
b/hertzbeat-manager/src/main/resources/define/app-springboot3.yml
index 90ffe25f42..09d6424cf7 100644
--- a/hertzbeat-manager/src/main/resources/define/app-springboot3.yml
+++ b/hertzbeat-manager/src/main/resources/define/app-springboot3.yml
@@ -184,16 +184,16 @@ metrics:
           en-US: state
         type: 1
         label: true
-      - field: size
+      - field: Size
         i18n:
           zh-CN: 大小
-          en-US: size
+          en-US: Size
         type: 0
     aliasFields:
       - $.measurements[?(@.statistic == "VALUE")].value
     calculates:
       - state='^o^state^o^'
-      - size=$.measurements[?(@.statistic == "VALUE")].value
+      - Size=$.measurements[?(@.statistic == "VALUE")].value
     protocol: http
     http:
       host: ^_^host^_^
diff --git 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/script/YamlCheckScript.java
 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/script/YamlCheckScript.java
new file mode 100644
index 0000000000..54f98b3c6d
--- /dev/null
+++ 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/script/YamlCheckScript.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.manager.script;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.hertzbeat.common.constants.JexlKeywordsEnum;
+import org.apache.hertzbeat.common.entity.job.Job;
+import org.apache.hertzbeat.common.entity.job.Metrics;
+import org.junit.jupiter.api.Test;
+import org.yaml.snakeyaml.Yaml;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.stream.Stream;
+
+/**
+ * Yaml check script
+ */
+public class YamlCheckScript {
+
+    static final String YML_PATH = "src/main/resources/define";
+
+    @Test
+    public void checkYaml() throws IOException {
+        Path definePath = Paths.get(YML_PATH);
+        if (!Files.exists(definePath)) {
+            throw new IllegalStateException("Define directory not found: " + 
YML_PATH);
+        }
+        try (Stream<Path> paths = Files.walk(definePath)) {
+            paths.filter(Files::isRegularFile)
+                    .filter(path -> path.toString().endsWith(".yml"))
+                    .forEach(this::validateYmlFile);
+        }
+    }
+
+    private void validateYmlFile(Path filePath) {
+        var yaml = new Yaml();
+        Job app;
+        try {
+            app = yaml.loadAs(Files.readString(filePath), Job.class);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("parse yml error in file " + 
filePath.getFileName() + ": " + e.getMessage());
+        }
+        if (app == null) {
+            throw new IllegalArgumentException("Failed to load Job from file: 
" + filePath.getFileName());
+        }
+        try {
+            validateJexlKeywords(app.getMetrics());
+        } catch (Exception e) {
+            System.out.printf("file: %s , msg: %s%n", filePath.getFileName(), 
e.getMessage());
+        }
+    }
+
+    private void validateJexlKeywords(List<Metrics> metrics) {
+        if (null == metrics || metrics.isEmpty()) {
+            return;
+        }
+        for (Metrics metric : metrics) {
+            if (null == metric.getFields() || metric.getFields().isEmpty()) {
+                continue;
+            }
+            for (Metrics.Field field : metric.getFields()) {
+                if (null == field || StringUtils.isBlank(field.getField())) {
+                    continue;
+                }
+                if (JexlKeywordsEnum.match(field.getField())) {
+                    throw new IllegalArgumentException("check jexl keywords 
failed. field:" + field.getField());
+                }
+            }
+        }
+
+    }
+}
\ No newline at end of file
diff --git 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/AppServiceTest.java
 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/AppServiceTest.java
index be9b0746f1..a54d59cef1 100644
--- 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/AppServiceTest.java
+++ 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/AppServiceTest.java
@@ -17,7 +17,10 @@
 
 package org.apache.hertzbeat.manager.service;
 
+import org.apache.hertzbeat.common.entity.job.Job;
+import org.apache.hertzbeat.common.entity.job.Metrics;
 import org.apache.hertzbeat.common.entity.manager.Monitor;
+import org.apache.hertzbeat.common.entity.manager.ParamDefine;
 import org.apache.hertzbeat.manager.dao.DefineDao;
 import org.apache.hertzbeat.manager.dao.MonitorDao;
 import org.apache.hertzbeat.manager.service.impl.AppServiceImpl;
@@ -30,11 +33,16 @@ import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
+import java.util.Map;
 
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.when;
@@ -94,4 +102,58 @@ class AppServiceTest {
         
when(warehouseService.queryMonitorMetricsData(anyLong())).thenReturn(Collections.emptyList());
         assertDoesNotThrow(() -> appService.getAllAppHierarchy("en-US"));
     }
+
+    @Test
+    void appDefineJexl() throws NoSuchMethodException {
+        Job job = new Job();
+        job.setApp("test-app");
+        job.setCategory("service");
+        job.setName(Map.of("k", "v"));
+
+        List<ParamDefine> params = new ArrayList<>();
+        ParamDefine hostParam = new ParamDefine();
+        hostParam.setField("host");
+        hostParam.setType("host");
+        hostParam.setRequired(true);
+        params.add(hostParam);
+
+        ParamDefine portParam = new ParamDefine();
+        portParam.setField("port");
+        portParam.setType("number");
+        portParam.setRequired(true);
+        portParam.setDefaultValue("8080");
+        params.add(portParam);
+
+        job.setParams(params);
+
+        List<Metrics> metrics = new ArrayList<>();
+
+        Metrics otherMetrics = new Metrics();
+        otherMetrics.setName("details");
+        otherMetrics.setPriority((byte) 0);
+        otherMetrics.setProtocol("http");
+
+        List<Metrics.Field> fields = new ArrayList<>();
+        fields.add(Metrics.Field.builder().field("size").build());
+        otherMetrics.setFields(fields);
+
+        metrics.add(otherMetrics);
+        job.setMetrics(metrics);
+
+        Method verifyMethod = 
AppServiceImpl.class.getDeclaredMethod("verifyDefineAppContent", Job.class, 
boolean.class);
+        verifyMethod.setAccessible(true);
+        IllegalArgumentException exception = 
assertThrows(IllegalArgumentException.class, () -> {
+            try {
+                verifyMethod.invoke(appService, job, false);
+            } catch (InvocationTargetException e) {
+                if (e.getCause() instanceof RuntimeException) {
+                    throw e.getCause();
+                }
+                throw new RuntimeException(e.getCause());
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        });
+        assertTrue(exception.getMessage().contains("prohibited keywords"));
+    }
 }
diff --git 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/MonitorServiceTest.java
 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/MonitorServiceTest.java
index 711ddfb03f..7db847a451 100644
--- 
a/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/MonitorServiceTest.java
+++ 
b/hertzbeat-manager/src/test/java/org/apache/hertzbeat/manager/service/MonitorServiceTest.java
@@ -34,6 +34,7 @@ import java.util.Set;
 import org.apache.hertzbeat.alert.dao.AlertDefineBindDao;
 import org.apache.hertzbeat.common.constants.CommonConstants;
 import org.apache.hertzbeat.common.entity.job.Job;
+import org.apache.hertzbeat.common.entity.job.Metrics;
 import org.apache.hertzbeat.common.entity.manager.Monitor;
 import org.apache.hertzbeat.common.entity.manager.Param;
 import org.apache.hertzbeat.common.entity.manager.ParamDefine;
@@ -774,4 +775,31 @@ class MonitorServiceTest {
         // Test the exportAll method
         assertDoesNotThrow(() -> monitorService.exportAll("JSON", 
mockResponse));
     }
+
+    @Test
+    void jexlKeyword() {
+
+        List<Metrics.Field> fields = new ArrayList<>();
+        fields.add(Metrics.Field.builder().field("size").build());
+
+        List<Metrics> metrics = new ArrayList<>();
+        
metrics.add(Metrics.builder().name("metricsName").fields(fields).build());
+
+        Job job = new Job();
+        job.setApp("testJob");
+        job.setMetrics(metrics);
+        Monitor monitor = 
Monitor.builder().jobId(1L).intervals(1).app(job.getApp()).name(job.getApp()).host("host").build();
+
+
+        List<Param> params = new ArrayList<>();
+        params.add(Param.builder().field("field").paramValue("value").build());
+
+        MonitorDto dto = new MonitorDto();
+        dto.setMonitor(monitor);
+        dto.setParams(params);
+
+        when(appService.getAppDefine(monitor.getApp())).thenReturn(job);
+        IllegalArgumentException exception = 
assertThrows(IllegalArgumentException.class, () -> monitorService.validate(dto, 
null));
+        assertEquals("testJob metricsName size prohibited keywords, please 
modify the template information.", exception.getMessage());
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscr...@hertzbeat.apache.org
For additional commands, e-mail: notifications-h...@hertzbeat.apache.org

Reply via email to