VYPR
Critical severityNVD Advisory· Published Mar 9, 2026· Updated Mar 10, 2026

Apache IoTDB: JEXL Expression Injection Vulnerability

CVE-2026-24713

Description

Improper Input Validation vulnerability in Apache IoTDB.

This issue affects Apache IoTDB: from 1.0.0 before 1.3.7, from 2.0.0 before 2.0.7.

Users are recommended to upgrade to version 1.3.7 or 2.0.7, which fixes the issue.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Apache IoTDB versions before 1.3.7 and 2.0.7 contain an improper input validation vulnerability that could allow remote code execution via crafted JEXL expressions.

Vulnerability

Overview

CVE-2026-24713 is an improper input validation vulnerability in Apache IoTDB, a time series database management system. The issue affects versions from 1.0.0 before 1.3.7 and from 2.0.0 before 2.0.7. The root cause lies in the handling of user-defined transformation functions (UDTFs) that use JEXL (Java Expression Language) expressions. The built-in JEXL function in tree mode did not properly validate or sanitize or restrict the expressions passed to it, allowing an attacker to inject arbitrary JEXL code [2].

Exploitation

An attacker with the ability to submit SQL-like queries to IoTDB can exploit this vulnerability by crafting a malicious JEXL expression within a UDTF call. No special privileges beyond query access are required, as the vulnerable function is available to any authenticated user who can execute queries. The attack surface is the IoTDB query interface, which is typically exposed on a network port [1][2].

Impact

Successful exploitation allows an attacker to execute arbitrary code on the IoTDB server. This could lead to full compromise of the database, including data exfiltration, modification, or deletion, and potentially lateral movement within the network. The vulnerability is particularly severe because IoTDB is often deployed in industrial IoT environments where it handles sensitive time series data [1][3].

Mitigation

Apache has released fixed versions 1.3.7 and 2.0.7, which remove the built-in JEXL function in tree mode [2]. Users are strongly recommended to upgrade immediately. No workarounds have been provided for older versions [3]. The vulnerability is not yet listed in CISA's Known Exploited Vulnerabilities (KEV) catalog as of publication date.

AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.apache.iotdb:iotdb-coreMaven
>= 1.0.0, < 1.3.71.3.7
org.apache.iotdb:iotdb-coreMaven
>= 2.0.0, < 2.0.72.0.7

Affected products

2
  • Apache/IoTDBllm-fuzzy
    Range: >=1.0.0, <1.3.7; >=2.0.0, <2.0.7
  • Apache Software Foundation/Apache IoTDBv5
    Range: 1.0.0

Patches

1
8fbfddc5f837

Remove built-in jexl function in tree mode (#17092)

https://github.com/apache/iotdbJackie TienJan 28, 2026via ghsa
12 files changed · +36 627
  • dependencies.json+0 2 modified
    @@ -52,7 +52,6 @@
         "io.netty:netty-codec",
         "io.netty:netty-codec-dns",
         "io.netty:netty-codec-http",
    -    "io.netty:netty-codec-http",
         "io.netty:netty-codec-http2",
         "io.netty:netty-codec-mqtt",
         "io.netty:netty-codec-socks",
    @@ -97,7 +96,6 @@
         "org.antlr:antlr4-runtime",
         "org.apache.commons:commons-collections4",
         "org.apache.commons:commons-csv",
    -    "org.apache.commons:commons-jexl3",
         "org.apache.commons:commons-lang3",
         "org.apache.commons:commons-math3",
         "org.apache.commons:commons-pool2",
    
  • example/pipe-count-point-processor/pom.xml+1 1 modified
    @@ -59,7 +59,7 @@
                     <groupId>org.apache.maven.plugins</groupId>
                     <artifactId>maven-jar-plugin</artifactId>
                     <version>3.2.2</version>
    -                <configuration />
    +                <configuration/>
                 </plugin>
             </plugins>
         </build>
    
  • integration-test/pom.xml+5 5 modified
    @@ -29,16 +29,16 @@
         <artifactId>integration-test</artifactId>
         <name>IoTDB: Integration-Test</name>
         <properties>
    -        <integrationTest.excludedGroups />
    +        <integrationTest.excludedGroups/>
             <integrationTest.forkCount>1</integrationTest.forkCount>
    -        <integrationTest.includedGroups />
    +        <integrationTest.includedGroups/>
             <integrationTest.launchNodeInSameJVM>true</integrationTest.launchNodeInSameJVM>
             <integrationTest.nodeMaxHeapSize>200m</integrationTest.nodeMaxHeapSize>
             <integrationTest.nodeNewHeapSize>200m</integrationTest.nodeNewHeapSize>
             <integrationTest.randomSelectWriteNode>true</integrationTest.randomSelectWriteNode>
             <integrationTest.readAndVerifyWithMultiNode>true</integrationTest.readAndVerifyWithMultiNode>
             <integrationTest.dataRegionPerDataNode>0</integrationTest.dataRegionPerDataNode>
    -        <integrationTest.testEnv />
    +        <integrationTest.testEnv/>
             <lightWeightStandaloneMode.configNodeConsensus>Simple</lightWeightStandaloneMode.configNodeConsensus>
             <!-- -DClusterConfigurations values configured here -->
             <lightWeightStandaloneMode.configNodeNumber>1</lightWeightStandaloneMode.configNodeNumber>
    @@ -672,7 +672,7 @@
                     <activeByDefault>false</activeByDefault>
                 </activation>
                 <properties>
    -                <integrationTest.excludedGroups />
    +                <integrationTest.excludedGroups/>
                     <integrationTest.includedGroups>org.apache.iotdb.itbase.category.DailyIT</integrationTest.includedGroups>
                     <integrationTest.launchNodeInSameJVM>false</integrationTest.launchNodeInSameJVM>
                     <integrationTest.randomSelectWriteNode>true</integrationTest.randomSelectWriteNode>
    @@ -714,7 +714,7 @@
                     <activeByDefault>false</activeByDefault>
                 </activation>
                 <properties>
    -                <integrationTest.excludedGroups />
    +                <integrationTest.excludedGroups/>
                     <integrationTest.includedGroups>org.apache.iotdb.itbase.category.ManualIT</integrationTest.includedGroups>
                     <integrationTest.launchNodeInSameJVM>false</integrationTest.launchNodeInSameJVM>
                     <integrationTest.randomSelectWriteNode>true</integrationTest.randomSelectWriteNode>
    
  • integration-test/src/main/java/org/apache/iotdb/itbase/constant/BuiltinTimeSeriesGeneratingFunctionEnum.java+0 1 modified
    @@ -72,7 +72,6 @@ public enum BuiltinTimeSeriesGeneratingFunctionEnum {
       EQUAL_SIZE_BUCKET_AGG_SAMPLE("EQUAL_SIZE_BUCKET_AGG_SAMPLE"),
       EQUAL_SIZE_BUCKET_M4_SAMPLE("EQUAL_SIZE_BUCKET_M4_SAMPLE"),
       EQUAL_SIZE_BUCKET_OUTLIER_SAMPLE("EQUAL_SIZE_BUCKET_OUTLIER_SAMPLE"),
    -  JEXL("JEXL"),
       MASTER_REPAIR("MASTER_REPAIR"),
       FORECAST("FORECAST"),
       M4("M4");
    
  • integration-test/src/test/java/org/apache/iotdb/db/it/udf/IoTDBUDTFBuiltinFunctionIT.java+0 235 modified
    @@ -1490,241 +1490,6 @@ private void test_M4_constantTimeSeries() {
         }
       }
     
    -  @Test
    -  public void testUDTFJexl() {
    -    try (Connection connection = EnvFactory.getEnv().getConnection();
    -        Statement statement = connection.createStatement()) {
    -      statement.execute("CREATE TIMESERIES root.sg.d7.s1 with datatype=INT32,encoding=PLAIN");
    -      statement.execute("CREATE TIMESERIES root.sg.d7.s2 with datatype=FLOAT,encoding=PLAIN");
    -      statement.execute("CREATE TIMESERIES root.sg.d7.s3 with datatype=DOUBLE,encoding=PLAIN");
    -      statement.execute("CREATE TIMESERIES root.sg.d7.s4 with datatype=TEXT,encoding=PLAIN");
    -      statement.execute("CREATE TIMESERIES root.sg.d7.s5 with datatype=BOOLEAN,encoding=PLAIN");
    -      statement.execute("CREATE TIMESERIES root.sg.d7.s6 with datatype=INT64,encoding=PLAIN");
    -      statement.execute("CREATE TIMESERIES root.sg.d7.s7 with datatype=INT64,encoding=PLAIN");
    -      statement.execute("CREATE TIMESERIES root.sg.d7.s8 with datatype=FLOAT,encoding=PLAIN");
    -      statement.execute("CREATE TIMESERIES root.sg.d7.s9 with datatype=TEXT,encoding=PLAIN");
    -    } catch (SQLException throwable) {
    -      fail(throwable.getMessage());
    -    }
    -    String[] SQL_FOR_SAMPLE_1 = new String[6];
    -    String[] SQL_FOR_SAMPLE_2 = new String[6];
    -    String[] SQL_FOR_SAMPLE_3 = new String[6];
    -    String[] SQL_FOR_SAMPLE_4 = new String[6];
    -    String[] SQL_FOR_SAMPLE_5 = new String[6];
    -    String[] SQL_FOR_SAMPLE_6 = new String[6];
    -    String[] SQL_FOR_SAMPLE_7 = new String[6];
    -    String[] SQL_FOR_SAMPLE_8 = new String[6];
    -    String[] SQL_FOR_SAMPLE_9 = new String[6];
    -    for (int i = 0; i < 5; i++) {
    -      SQL_FOR_SAMPLE_1[i] =
    -          String.format(
    -              Locale.ENGLISH, "insert into root.sg.d7(time, s1) values (%d, %d)", i, i + 1);
    -      SQL_FOR_SAMPLE_2[i] =
    -          String.format(
    -              Locale.ENGLISH, "insert into root.sg.d7(time, s2) values (%d, %f)", i, i + 1.0);
    -      SQL_FOR_SAMPLE_3[i] =
    -          String.format(
    -              Locale.ENGLISH, "insert into root.sg.d7(time, s3) values (%d, %f)", i, i + 1.0);
    -      SQL_FOR_SAMPLE_4[i] =
    -          String.format(
    -              Locale.ENGLISH, "insert into root.sg.d7(time, s4) values (%d, '%s')", i, "string");
    -      SQL_FOR_SAMPLE_5[i] = String.format("insert into root.sg.d7(time, s5) values (%d, true)", i);
    -      SQL_FOR_SAMPLE_6[i] =
    -          String.format(
    -              Locale.ENGLISH, "insert into root.sg.d7(time, s6) values (%d, %d)", i, i + 8);
    -      SQL_FOR_SAMPLE_7[i] =
    -          String.format(
    -              Locale.ENGLISH, "insert into root.sg.d7(time, s7) values (%d, %d)", i, i + 1);
    -      SQL_FOR_SAMPLE_8[i] =
    -          String.format(
    -              Locale.ENGLISH, "insert into root.sg.d7(time, s8) values (%d, %f)", i, i + 1.0);
    -      SQL_FOR_SAMPLE_9[i] =
    -          String.format(
    -              Locale.ENGLISH, "insert into root.sg.d7(time, s9) values (%d, '%s')", i, "string");
    -    }
    -    SQL_FOR_SAMPLE_1[5] =
    -        String.format(
    -            Locale.ENGLISH,
    -            "insert into root.sg.d7(time, s1) values (%d, %d)",
    -            10000000000L,
    -            5 + 1);
    -    SQL_FOR_SAMPLE_2[5] =
    -        String.format(
    -            Locale.ENGLISH,
    -            "insert into root.sg.d7(time, s2) values (%d, %f)",
    -            10000000000L,
    -            5 + 1.0);
    -    SQL_FOR_SAMPLE_3[5] =
    -        String.format(
    -            Locale.ENGLISH,
    -            "insert into root.sg.d7(time, s3) values (%d, %f)",
    -            10000000000L,
    -            5 + 1.0);
    -    SQL_FOR_SAMPLE_4[5] =
    -        String.format(
    -            Locale.ENGLISH,
    -            "insert into root.sg.d7(time, s4) values (%d, '%s')",
    -            10000000000L,
    -            "string");
    -    SQL_FOR_SAMPLE_5[5] = String.format("insert into root.sg.d7(time, s5) values (%d, true)", 5);
    -    SQL_FOR_SAMPLE_6[5] =
    -        String.format(
    -            Locale.ENGLISH,
    -            "insert into root.sg.d7(time, s6) values (%d, %d)",
    -            10000000000L,
    -            5 + 8);
    -    SQL_FOR_SAMPLE_7[5] =
    -        String.format(
    -            Locale.ENGLISH,
    -            "insert into root.sg.d7(time, s7) values (%d, %d)",
    -            10000000000L,
    -            5 + 1);
    -    SQL_FOR_SAMPLE_8[5] =
    -        String.format(
    -            Locale.ENGLISH,
    -            "insert into root.sg.d7(time, s8) values (%d, %f)",
    -            10000000000L,
    -            5 + 1.0);
    -    SQL_FOR_SAMPLE_9[5] =
    -        String.format(
    -            Locale.ENGLISH,
    -            "insert into root.sg.d7(time, s9) values (%d, '%s')",
    -            10000000000L,
    -            "string");
    -    double[] ANSWER1 = new double[] {2, 4, 6, 8, 10, 12};
    -    double[] ANSWER2 = new double[] {2, 4, 6, 8, 10, 12};
    -    double[] ANSWER3 = new double[] {4, 7, 10, 13, 16, 19};
    -    String[] ANSWER4 =
    -        new String[] {"string2", "string2", "string2", "string2", "string2", "string2"};
    -    double[] ANSWER7 = new double[] {1, 4, 9, 16, 25, 36};
    -    String[] ANSWER8 =
    -        new String[] {"string1", "string4", "string9", "string16", "string25", "string36"};
    -    double[] ANSWER9 = new double[] {2, 9, 28, 65, 126, 469};
    -    try (Connection connection = EnvFactory.getEnv().getConnection();
    -        Statement statement = connection.createStatement()) {
    -      for (int i = 0; i < 5; i++) {
    -        statement.execute(SQL_FOR_SAMPLE_1[i]);
    -        statement.execute(SQL_FOR_SAMPLE_2[i]);
    -        statement.execute(SQL_FOR_SAMPLE_3[i]);
    -        statement.execute(SQL_FOR_SAMPLE_4[i]);
    -        statement.execute(SQL_FOR_SAMPLE_5[i]);
    -        statement.execute(SQL_FOR_SAMPLE_6[i]);
    -        statement.execute(SQL_FOR_SAMPLE_7[i]);
    -        statement.execute(SQL_FOR_SAMPLE_8[i]);
    -        statement.execute(SQL_FOR_SAMPLE_9[i]);
    -      }
    -    } catch (SQLException throwable) {
    -      fail(throwable.getMessage());
    -    }
    -
    -    try (Connection connection = EnvFactory.getEnv().getConnection();
    -        Statement statement = connection.createStatement()) {
    -      String functionName = "JEXL";
    -      String expr1 = "x -> {2 * x}";
    -      String expr2 = "x -> {x + x}";
    -      String expr3 = "x -> {x * 3 + 1}";
    -      String expr4 = "x -> {x + 2}";
    -      String expr5 = "x -> {x == true}";
    -      String expr6 = "x -> {x == x}";
    -      String expr7 = "(x, y) -> {x * y}";
    -      String expr8 = "(x, y, z) -> {x + y * z}";
    -      String expr9 = "(x, y, z, a) -> {x * y * z + (a ? 1 : -1)}";
    -      ResultSet resultSet =
    -          statement.executeQuery(
    -              String.format(
    -                  "select %s(s1, 'expr'='%s'), "
    -                      + "%s(s2, 'expr'='%s'), "
    -                      + "%s(s3, 'expr'='%s'), "
    -                      + "%s(s4, 'expr'='%s'), "
    -                      + "%s(s5, 'expr'='%s'), "
    -                      + "%s(s6, 'expr'='%s'), "
    -                      + "%s(s7, s8, 'expr'='%s'), "
    -                      + "%s(s4, s7, s1, 'expr'='%s'), "
    -                      + "%s(s1, s7, s8, s5, 'expr'='%s') "
    -                      + "from root.sg.d7",
    -                  functionName,
    -                  expr1,
    -                  functionName,
    -                  expr2,
    -                  functionName,
    -                  expr3,
    -                  functionName,
    -                  expr4,
    -                  functionName,
    -                  expr5,
    -                  functionName,
    -                  expr6,
    -                  functionName,
    -                  expr7,
    -                  functionName,
    -                  expr8,
    -                  functionName,
    -                  expr9));
    -      int columnCount = resultSet.getMetaData().getColumnCount();
    -      assertEquals(1 + 9, columnCount);
    -      for (int i = 0; i < 5; i++) {
    -        resultSet.next();
    -        assertEquals(ANSWER1[i], resultSet.getDouble(2), 0.01);
    -        assertEquals(ANSWER2[i], resultSet.getDouble(3), 0.01);
    -        assertEquals(ANSWER3[i], resultSet.getDouble(4), 0.01);
    -        assertEquals(ANSWER4[i], resultSet.getString(5));
    -        assertTrue(resultSet.getBoolean(6));
    -        assertTrue(resultSet.getBoolean(7));
    -        assertEquals(ANSWER7[i], resultSet.getDouble(8), 0.01);
    -        assertEquals(ANSWER8[i], resultSet.getString(9));
    -        assertEquals(ANSWER9[i], resultSet.getDouble(10), 0.01);
    -      }
    -
    -      resultSet =
    -          statement.executeQuery(
    -              String.format(
    -                  "select %s(s1, 'expr'='%s'), "
    -                      + "%s(s2, 'expr'='%s'), "
    -                      + "%s(s3, 'expr'='%s'), "
    -                      + "%s(s4, 'expr'='%s'), "
    -                      + "%s(s5, 'expr'='%s'), "
    -                      + "%s(s6, 'expr'='%s'), "
    -                      + "%s(s7, s8, 'expr'='%s'), "
    -                      + "%s(s4, s7, s1, 'expr'='%s'), "
    -                      + "%s(s1, s7, s8, s5, 'expr'='%s') "
    -                      + "from root.sg.d7 align by device",
    -                  functionName,
    -                  expr1,
    -                  functionName,
    -                  expr2,
    -                  functionName,
    -                  expr3,
    -                  functionName,
    -                  expr4,
    -                  functionName,
    -                  expr5,
    -                  functionName,
    -                  expr6,
    -                  functionName,
    -                  expr7,
    -                  functionName,
    -                  expr8,
    -                  functionName,
    -                  expr9));
    -      columnCount = resultSet.getMetaData().getColumnCount();
    -      assertEquals(2 + 9, columnCount);
    -      for (int i = 0; i < 5; i++) {
    -        resultSet.next();
    -        assertEquals(ANSWER1[i], resultSet.getDouble(2 + 1), 0.01);
    -        assertEquals(ANSWER2[i], resultSet.getDouble(3 + 1), 0.01);
    -        assertEquals(ANSWER3[i], resultSet.getDouble(4 + 1), 0.01);
    -        assertEquals(ANSWER4[i], resultSet.getString(5 + 1));
    -        assertTrue(resultSet.getBoolean(6 + 1));
    -        assertTrue(resultSet.getBoolean(7 + 1));
    -        assertEquals(ANSWER7[i], resultSet.getDouble(8 + 1), 0.01);
    -        assertEquals(ANSWER8[i], resultSet.getString(9 + 1));
    -        assertEquals(ANSWER9[i], resultSet.getDouble(10 + 1), 0.01);
    -      }
    -    } catch (Exception e) {
    -      e.printStackTrace();
    -    }
    -  }
    -
       @Test
       public void testStringFunctions() {
         String[] createSQLs =
    
  • iotdb-client/jdbc/pom.xml+1 1 modified
    @@ -266,7 +266,7 @@
                                                     </goals>
                                                 </pluginExecutionFilter>
                                                 <action>
    -                                                <ignore />
    +                                                <ignore/>
                                                 </action>
                                             </pluginExecution>
                                         </pluginExecutions>
    
  • iotdb-core/ainode/iotdb/conf/git.properties+3 0 added
    @@ -0,0 +1,3 @@
    +#Generated by Git-Commit-Id-Plugin
    +git.commit.id.abbrev=370df98
    +git.dirty=false
    
  • iotdb-core/ainode/iotdb/conf/pom.properties+21 0 added
    @@ -0,0 +1,21 @@
    +#
    +# 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.
    +
    +artifactId=iotdb-ainode
    +groupId=org.apache.iotdb
    +version=1.3.7-SNAPSHOT
    \ No newline at end of file
    
  • iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/udf/BuiltinTimeSeriesGeneratingFunction.java+0 2 modified
    @@ -51,7 +51,6 @@
     import org.apache.iotdb.commons.udf.builtin.UDTFExp;
     import org.apache.iotdb.commons.udf.builtin.UDTFFloor;
     import org.apache.iotdb.commons.udf.builtin.UDTFInRange;
    -import org.apache.iotdb.commons.udf.builtin.UDTFJexl;
     import org.apache.iotdb.commons.udf.builtin.UDTFLog;
     import org.apache.iotdb.commons.udf.builtin.UDTFLog10;
     import org.apache.iotdb.commons.udf.builtin.UDTFM4;
    @@ -136,7 +135,6 @@ public enum BuiltinTimeSeriesGeneratingFunction {
       EQUAL_SIZE_BUCKET_M4_SAMPLE("EQUAL_SIZE_BUCKET_M4_SAMPLE", UDTFEqualSizeBucketM4Sample.class),
       EQUAL_SIZE_BUCKET_OUTLIER_SAMPLE(
           "EQUAL_SIZE_BUCKET_OUTLIER_SAMPLE", UDTFEqualSizeBucketOutlierSample.class),
    -  JEXL("JEXL", UDTFJexl.class),
       MASTER_REPAIR("MASTER_REPAIR", UDTFMasterRepair.class),
       M4("M4", UDTFM4.class),
       FORECAST("FORECAST", UDTFForecast.class),
    
  • iotdb-core/node-commons/pom.xml+0 4 modified
    @@ -159,10 +159,6 @@
                 <groupId>com.github.ben-manes.caffeine</groupId>
                 <artifactId>caffeine</artifactId>
             </dependency>
    -        <dependency>
    -            <groupId>org.apache.commons</groupId>
    -            <artifactId>commons-jexl3</artifactId>
    -        </dependency>
             <dependency>
                 <groupId>com.github.luben</groupId>
                 <artifactId>zstd-jni</artifactId>
    
  • iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/UDTFJexl.java+0 365 removed
    @@ -1,365 +0,0 @@
    -/*
    - * 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.iotdb.commons.udf.builtin;
    -
    -import org.apache.iotdb.commons.exception.MetadataException;
    -import org.apache.iotdb.commons.udf.utils.UDFDataTypeTransformer;
    -import org.apache.iotdb.udf.api.UDTF;
    -import org.apache.iotdb.udf.api.access.Row;
    -import org.apache.iotdb.udf.api.collector.PointCollector;
    -import org.apache.iotdb.udf.api.customizer.config.UDTFConfigurations;
    -import org.apache.iotdb.udf.api.customizer.parameter.UDFParameterValidator;
    -import org.apache.iotdb.udf.api.customizer.parameter.UDFParameters;
    -import org.apache.iotdb.udf.api.customizer.strategy.RowByRowAccessStrategy;
    -import org.apache.iotdb.udf.api.exception.UDFException;
    -import org.apache.iotdb.udf.api.exception.UDFInputSeriesDataTypeNotValidException;
    -import org.apache.iotdb.udf.api.exception.UDFOutputSeriesDataTypeNotValidException;
    -import org.apache.iotdb.udf.api.type.Type;
    -
    -import org.apache.commons.jexl3.JexlBuilder;
    -import org.apache.commons.jexl3.JexlEngine;
    -import org.apache.commons.jexl3.JexlScript;
    -import org.apache.tsfile.enums.TSDataType;
    -
    -import java.io.IOException;
    -import java.util.HashMap;
    -
    -public class UDTFJexl implements UDTF {
    -
    -  private int inputSeriesNumber;
    -  private TSDataType[] inputDataType;
    -  private TSDataType outputDataType;
    -  private JexlScript script;
    -  private Evaluator evaluator;
    -
    -  @Override
    -  public void validate(UDFParameterValidator validator) throws UDFException {
    -    inputSeriesNumber = validator.getParameters().getChildExpressionsSize();
    -    for (int i = 0; i < inputSeriesNumber; i++) {
    -      validator.validateInputSeriesDataType(
    -          i, Type.INT32, Type.INT64, Type.FLOAT, Type.DOUBLE, Type.TEXT, Type.BOOLEAN);
    -    }
    -    validator.validateRequiredAttribute("expr");
    -  }
    -
    -  @Override
    -  public void beforeStart(UDFParameters parameters, UDTFConfigurations configurations)
    -      throws UDFInputSeriesDataTypeNotValidException,
    -          UDFOutputSeriesDataTypeNotValidException,
    -          MetadataException {
    -    String expr = parameters.getString("expr");
    -    JexlEngine jexl = new JexlBuilder().create();
    -    script = jexl.createScript(expr);
    -
    -    inputDataType = new TSDataType[inputSeriesNumber];
    -    for (int i = 0; i < inputSeriesNumber; i++) {
    -      inputDataType[i] = UDFDataTypeTransformer.transformToTsDataType(parameters.getDataType(i));
    -    }
    -    outputDataType = probeOutputDataType();
    -
    -    if (inputSeriesNumber == 1) {
    -      switch (inputDataType[0]) {
    -        case INT32:
    -          evaluator = new EvaluatorIntInput();
    -          break;
    -        case INT64:
    -          evaluator = new EvaluatorLongInput();
    -          break;
    -        case FLOAT:
    -          evaluator = new EvaluatorFloatInput();
    -          break;
    -        case DOUBLE:
    -          evaluator = new EvaluatorDoubleInput();
    -          break;
    -        case TEXT:
    -          evaluator = new EvaluatorStringInput();
    -          break;
    -        case BOOLEAN:
    -          evaluator = new EvaluatorBooleanInput();
    -          break;
    -        case STRING:
    -        case TIMESTAMP:
    -        case DATE:
    -        case BLOB:
    -        default:
    -          throw new UDFInputSeriesDataTypeNotValidException(
    -              0,
    -              UDFDataTypeTransformer.transformToUDFDataType(inputDataType[0]),
    -              Type.INT32,
    -              Type.INT64,
    -              Type.FLOAT,
    -              Type.DOUBLE,
    -              Type.TEXT,
    -              Type.BOOLEAN);
    -      }
    -    } else {
    -      evaluator = new EvaluatorMulInput();
    -    }
    -
    -    configurations
    -        .setAccessStrategy(new RowByRowAccessStrategy())
    -        .setOutputDataType(UDFDataTypeTransformer.transformToUDFDataType(outputDataType));
    -  }
    -
    -  // 23, 23L, 23f, 23d, "string", true are hard codes for probing
    -  private HashMap<TSDataType, Object> initialMap() {
    -    HashMap<TSDataType, Object> map = new HashMap<>();
    -    map.put(TSDataType.INT32, 23);
    -    map.put(TSDataType.INT64, 23L);
    -    map.put(TSDataType.FLOAT, 23f);
    -    map.put(TSDataType.DOUBLE, 23d);
    -    map.put(TSDataType.TEXT, "string");
    -    map.put(TSDataType.BOOLEAN, true);
    -    return map;
    -  }
    -
    -  private TSDataType probeOutputDataType() throws UDFOutputSeriesDataTypeNotValidException {
    -    // initial inputHardCodes to probe OutputDataType
    -    HashMap<TSDataType, Object> map = initialMap();
    -    Object[] inputHardCodes = new Object[inputSeriesNumber];
    -    for (int i = 0; i < inputSeriesNumber; i++) {
    -      inputHardCodes[i] = map.get(inputDataType[i]);
    -    }
    -
    -    Object o = script.execute(null, inputHardCodes);
    -
    -    if (o instanceof Number) {
    -      return TSDataType.DOUBLE;
    -    } else if (o instanceof String) {
    -      return TSDataType.TEXT;
    -    } else if (o instanceof Boolean) {
    -      return TSDataType.BOOLEAN;
    -    } else {
    -      throw new UDFOutputSeriesDataTypeNotValidException(0, "[Number, String, Boolean]");
    -    }
    -  }
    -
    -  @Override
    -  public void transform(Row row, PointCollector collector)
    -      throws IOException,
    -          UDFOutputSeriesDataTypeNotValidException,
    -          UDFInputSeriesDataTypeNotValidException {
    -    switch (outputDataType) {
    -      case DOUBLE:
    -        evaluator.evaluateDouble(row, collector);
    -        break;
    -      case TEXT:
    -        evaluator.evaluateText(row, collector);
    -        break;
    -      case BOOLEAN:
    -        evaluator.evaluateBoolean(row, collector);
    -        break;
    -      case TIMESTAMP:
    -      case DATE:
    -      case STRING:
    -      case BLOB:
    -      case INT64:
    -      case INT32:
    -      case FLOAT:
    -      default:
    -        // This will not happen.
    -        throw new UDFOutputSeriesDataTypeNotValidException(0, "[Number, String, Boolean]");
    -    }
    -  }
    -
    -  private interface Evaluator {
    -    void evaluateDouble(Row row, PointCollector collector)
    -        throws IOException, UDFInputSeriesDataTypeNotValidException;
    -
    -    void evaluateText(Row row, PointCollector collector)
    -        throws IOException, UDFInputSeriesDataTypeNotValidException;
    -
    -    void evaluateBoolean(Row row, PointCollector collector)
    -        throws IOException, UDFInputSeriesDataTypeNotValidException;
    -  }
    -
    -  private class EvaluatorIntInput implements Evaluator {
    -    @Override
    -    public void evaluateDouble(Row row, PointCollector collector) throws IOException {
    -      collector.putDouble(
    -          row.getTime(), ((Number) script.execute(null, row.getInt(0))).doubleValue());
    -    }
    -
    -    @Override
    -    public void evaluateText(Row row, PointCollector collector) throws IOException {
    -      collector.putString(row.getTime(), (String) script.execute(null, row.getInt(0)));
    -    }
    -
    -    @Override
    -    public void evaluateBoolean(Row row, PointCollector collector) throws IOException {
    -      collector.putBoolean(row.getTime(), (Boolean) script.execute(null, row.getInt(0)));
    -    }
    -  }
    -
    -  private class EvaluatorLongInput implements Evaluator {
    -    @Override
    -    public void evaluateDouble(Row row, PointCollector collector) throws IOException {
    -      collector.putDouble(
    -          row.getTime(), ((Number) script.execute(null, row.getLong(0))).doubleValue());
    -    }
    -
    -    @Override
    -    public void evaluateText(Row row, PointCollector collector) throws IOException {
    -      collector.putString(row.getTime(), (String) script.execute(null, row.getLong(0)));
    -    }
    -
    -    @Override
    -    public void evaluateBoolean(Row row, PointCollector collector) throws IOException {
    -      collector.putBoolean(row.getTime(), (Boolean) script.execute(null, row.getLong(0)));
    -    }
    -  }
    -
    -  private class EvaluatorFloatInput implements Evaluator {
    -    @Override
    -    public void evaluateDouble(Row row, PointCollector collector) throws IOException {
    -      collector.putDouble(
    -          row.getTime(), ((Number) script.execute(null, row.getFloat(0))).doubleValue());
    -    }
    -
    -    @Override
    -    public void evaluateText(Row row, PointCollector collector) throws IOException {
    -      collector.putString(row.getTime(), (String) script.execute(null, row.getFloat(0)));
    -    }
    -
    -    @Override
    -    public void evaluateBoolean(Row row, PointCollector collector) throws IOException {
    -      collector.putBoolean(row.getTime(), (Boolean) script.execute(null, row.getFloat(0)));
    -    }
    -  }
    -
    -  private class EvaluatorDoubleInput implements Evaluator {
    -    @Override
    -    public void evaluateDouble(Row row, PointCollector collector) throws IOException {
    -      collector.putDouble(
    -          row.getTime(), ((Number) script.execute(null, row.getDouble(0))).doubleValue());
    -    }
    -
    -    @Override
    -    public void evaluateText(Row row, PointCollector collector) throws IOException {
    -      collector.putString(row.getTime(), (String) script.execute(null, row.getDouble(0)));
    -    }
    -
    -    @Override
    -    public void evaluateBoolean(Row row, PointCollector collector) throws IOException {
    -      collector.putBoolean(row.getTime(), (Boolean) script.execute(null, row.getDouble(0)));
    -    }
    -  }
    -
    -  private class EvaluatorStringInput implements Evaluator {
    -    @Override
    -    public void evaluateDouble(Row row, PointCollector collector) throws IOException {
    -      collector.putDouble(
    -          row.getTime(), ((Number) script.execute(null, row.getString(0))).doubleValue());
    -    }
    -
    -    @Override
    -    public void evaluateText(Row row, PointCollector collector) throws IOException {
    -      collector.putString(row.getTime(), (String) script.execute(null, row.getString(0)));
    -    }
    -
    -    @Override
    -    public void evaluateBoolean(Row row, PointCollector collector) throws IOException {
    -      collector.putBoolean(row.getTime(), (Boolean) script.execute(null, row.getString(0)));
    -    }
    -  }
    -
    -  private class EvaluatorBooleanInput implements Evaluator {
    -    @Override
    -    public void evaluateDouble(Row row, PointCollector collector) throws IOException {
    -      collector.putDouble(
    -          row.getTime(), ((Number) script.execute(null, row.getBoolean(0))).doubleValue());
    -    }
    -
    -    @Override
    -    public void evaluateText(Row row, PointCollector collector) throws IOException {
    -      collector.putString(row.getTime(), (String) script.execute(null, row.getBoolean(0)));
    -    }
    -
    -    @Override
    -    public void evaluateBoolean(Row row, PointCollector collector) throws IOException {
    -      collector.putBoolean(row.getTime(), (Boolean) script.execute(null, row.getBoolean(0)));
    -    }
    -  }
    -
    -  private class EvaluatorMulInput implements Evaluator {
    -
    -    Object[] values = new Object[inputSeriesNumber];
    -
    -    @Override
    -    public void evaluateDouble(Row row, PointCollector collector)
    -        throws IOException, UDFInputSeriesDataTypeNotValidException {
    -      getValues(row);
    -      collector.putDouble(row.getTime(), ((Number) script.execute(null, values)).doubleValue());
    -    }
    -
    -    @Override
    -    public void evaluateText(Row row, PointCollector collector)
    -        throws IOException, UDFInputSeriesDataTypeNotValidException {
    -      getValues(row);
    -      collector.putString(row.getTime(), (String) script.execute(null, values));
    -    }
    -
    -    @Override
    -    public void evaluateBoolean(Row row, PointCollector collector)
    -        throws IOException, UDFInputSeriesDataTypeNotValidException {
    -      getValues(row);
    -      collector.putBoolean(row.getTime(), (Boolean) script.execute(null, values));
    -    }
    -
    -    public void getValues(Row row) throws IOException, UDFInputSeriesDataTypeNotValidException {
    -      for (int i = 0; i < inputSeriesNumber; i++) {
    -        switch (inputDataType[i]) {
    -          case INT32:
    -            values[i] = row.getInt(i);
    -            break;
    -          case INT64:
    -            values[i] = row.getLong(i);
    -            break;
    -          case FLOAT:
    -            values[i] = row.getFloat(i);
    -            break;
    -          case DOUBLE:
    -            values[i] = row.getDouble(i);
    -            break;
    -          case TEXT:
    -            values[i] = row.getString(i);
    -            break;
    -          case BOOLEAN:
    -            values[i] = row.getBoolean(i);
    -            break;
    -          case STRING:
    -          case BLOB:
    -          case DATE:
    -          case TIMESTAMP:
    -          default:
    -            throw new UDFInputSeriesDataTypeNotValidException(
    -                i,
    -                UDFDataTypeTransformer.transformToUDFDataType(inputDataType[i]),
    -                Type.INT32,
    -                Type.INT64,
    -                Type.FLOAT,
    -                Type.DOUBLE,
    -                Type.TEXT,
    -                Type.BOOLEAN);
    -        }
    -      }
    -    }
    -  }
    -}
    
  • pom.xml+5 11 modified
    @@ -57,9 +57,9 @@
             <!-- This was the last version to support Java 8 -->
             <antlr4.version>4.9.3</antlr4.version>
             <!-- By default, the argLine is empty-->
    -        <argLine />
    +        <argLine/>
             <awaitility.version>4.2.0</awaitility.version>
    -        <boost.include.dir />
    +        <boost.include.dir/>
             <!-- This was the last version to support Java 8 -->
             <caffeine.version>2.9.3</caffeine.version>
             <cglib.version>3.3.0</cglib.version>
    @@ -69,7 +69,6 @@
             <commons-codec.version>1.16.1</commons-codec.version>
             <commons-csv.version>1.10.0</commons-csv.version>
             <commons-io.version>2.14.0</commons-io.version>
    -        <commons-jexl3.version>3.3</commons-jexl3.version>
             <commons-lang3.version>3.18.0</commons-lang3.version>
             <commons-math3.version>3.6.1</commons-math3.version>
             <commons-pool2.version>2.11.1</commons-pool2.version>
    @@ -165,7 +164,7 @@
             <!-- This was the last version to support Java 8 -->
             <swagger.version>1.6.14</swagger.version>
             <thrift.exec-cmd.executable>chmod</thrift.exec-cmd.executable>
    -        <thrift.exec.absolute.path />
    +        <thrift.exec.absolute.path/>
             <!--
           Thrift 0.17.0 was the last version that could be used in Java 8 applications,
           However Thrift 0.17.0 has an invalid entry in the META-INF/MANIFEST.mf file.
    @@ -510,11 +509,6 @@
                     <artifactId>cglib</artifactId>
                     <version>${cglib.version}</version>
                 </dependency>
    -            <dependency>
    -                <groupId>org.apache.commons</groupId>
    -                <artifactId>commons-jexl3</artifactId>
    -                <version>${commons-jexl3.version}</version>
    -            </dependency>
                 <dependency>
                     <groupId>com.github.luben</groupId>
                     <artifactId>zstd-jni</artifactId>
    @@ -888,7 +882,7 @@
                                 <importOrder>
                                     <order>org.apache.iotdb,,javax,java,\#</order>
                                 </importOrder>
    -                            <removeUnusedImports />
    +                            <removeUnusedImports/>
                             </java>
                             <lineEndings>UNIX</lineEndings>
                         </configuration>
    @@ -1126,7 +1120,7 @@
                             <phase>validate</phase>
                             <configuration>
                                 <rules>
    -                                <dependencyConvergence />
    +                                <dependencyConvergence/>
                                 </rules>
                             </configuration>
                         </execution>
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

9

News mentions

0

No linked articles in our index yet.