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

wenjin272 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/flink-agents.git


The following commit(s) were added to refs/heads/main by this push:
     new 169de4a4 [runtime][java] Reject raw bytes from PyFlink inputs (#1075)
169de4a4 is described below

commit 169de4a44d72bb19d03134fc813038867a84d413
Author: Wenjin Xie <[email protected]>
AuthorDate: Mon Aug 31 15:15:22 2026 +0800

    [runtime][java] Reject raw bytes from PyFlink inputs (#1075)
    
    Validate PyFlink value type information before building the agent operator 
so only the supported pickled representation reaches Python deserialization. 
Add regression coverage for pickled, explicit, and malformed input types.
    
    Generated-by: OpenAI Codex 0.144.5 (GPT-5)
    
    Co-authored-by: Codex <[email protected]>
---
 .../apache/flink/agents/runtime/CompileUtils.java  | 39 +++++++----
 .../flink/agents/runtime/CompileUtilsTest.java     | 81 ++++++++++++++++++++--
 2 files changed, 103 insertions(+), 17 deletions(-)

diff --git 
a/runtime/src/main/java/org/apache/flink/agents/runtime/CompileUtils.java 
b/runtime/src/main/java/org/apache/flink/agents/runtime/CompileUtils.java
index 37351f2c..c1bf0805 100644
--- a/runtime/src/main/java/org/apache/flink/agents/runtime/CompileUtils.java
+++ b/runtime/src/main/java/org/apache/flink/agents/runtime/CompileUtils.java
@@ -35,10 +35,22 @@ import static 
org.apache.flink.util.Preconditions.checkArgument;
 /** A utility class that bridges Flink DataStream/SQL with the Flink Agents 
agent. */
 public class CompileUtils {
 
+    private static final int PYTHON_KEY_FIELD_INDEX = 0;
+    private static final int PYTHON_VALUE_FIELD_INDEX = 1;
+
     // ============================ invoke by python 
====================================
     public static DataStream<byte[]> connectToAgent(
             KeyedStream<Row, Row> inputDataStream, String agentPlanJson)
             throws JsonProcessingException {
+        TypeInformation<?> inputType = inputDataStream.getType();
+        checkArgument(
+                isPickledPythonFieldType(inputType, PYTHON_VALUE_FIELD_INDEX),
+                "Flink Agents only supports PyFlink input values serialized 
with "
+                        + "PickledByteArrayTypeInfo. Convert raw byte-array 
inputs with a Python "
+                        + "operator using the default pickle output type 
before connecting them "
+                        + "to Flink Agents, but got %s",
+                inputType);
+
         // deserialize agent plan json.
         AgentPlan agentPlan = new ObjectMapper().readValue(agentPlanJson, 
AgentPlan.class);
         return connectToAgent(
@@ -46,7 +58,7 @@ public class CompileUtils {
                 agentPlan,
                 TypeInformation.of(byte[].class),
                 false,
-                isPickledPythonKeyType(inputDataStream.getKeyType()));
+                isPickledPythonFieldType(inputDataStream.getKeyType(), 
PYTHON_KEY_FIELD_INDEX));
     }
 
     // ============================ invoke by java 
====================================
@@ -95,20 +107,21 @@ public class CompileUtils {
                         .setParallelism(keyedInputStream.getParallelism());
     }
 
-    /**
-     * Returns whether PyFlink's single logical key field uses its default 
pickle representation.
-     */
-    static boolean isPickledPythonKeyType(TypeInformation<?> keyType) {
+    /** Returns whether a PyFlink Row field uses its default pickle 
representation. */
+    static boolean isPickledPythonFieldType(TypeInformation<?> 
typeInformation, int fieldIndex) {
+        checkArgument(fieldIndex >= 0, "Field index must not be negative, but 
got %s", fieldIndex);
         checkArgument(
-                keyType instanceof RowTypeInfo,
-                "Expected PyFlink key type to be a single-field RowTypeInfo, 
but got %s",
-                keyType);
-        RowTypeInfo rowType = (RowTypeInfo) keyType;
+                typeInformation instanceof RowTypeInfo,
+                "Expected PyFlink type to be a RowTypeInfo, but got %s",
+                typeInformation);
+        RowTypeInfo rowType = (RowTypeInfo) typeInformation;
+        int expectedArity = fieldIndex + 1;
         checkArgument(
-                rowType.getArity() == 1,
-                "Expected PyFlink key type to contain one logical field, but 
got arity %s",
+                rowType.getArity() == expectedArity,
+                "Expected PyFlink type to contain %s fields, but got arity %s",
+                expectedArity,
                 rowType.getArity());
-        TypeInformation<?> logicalKeyType = rowType.getTypeAt(0);
-        return logicalKeyType instanceof PickledByteArrayTypeInfo;
+        TypeInformation<?> fieldType = rowType.getTypeAt(fieldIndex);
+        return fieldType instanceof PickledByteArrayTypeInfo;
     }
 }
diff --git 
a/runtime/src/test/java/org/apache/flink/agents/runtime/CompileUtilsTest.java 
b/runtime/src/test/java/org/apache/flink/agents/runtime/CompileUtilsTest.java
index 5d53c187..96a0ce8f 100644
--- 
a/runtime/src/test/java/org/apache/flink/agents/runtime/CompileUtilsTest.java
+++ 
b/runtime/src/test/java/org/apache/flink/agents/runtime/CompileUtilsTest.java
@@ -17,8 +17,10 @@
  */
 package org.apache.flink.agents.runtime;
 
+import com.fasterxml.jackson.core.JsonProcessingException;
 import org.apache.flink.agents.plan.AgentPlan;
 import org.apache.flink.agents.runtime.operator.ActionExecutionOperatorTest;
+import org.apache.flink.api.common.typeinfo.TypeInformation;
 import org.apache.flink.api.common.typeinfo.Types;
 import org.apache.flink.api.java.functions.KeySelector;
 import org.apache.flink.streaming.api.datastream.DataStream;
@@ -26,15 +28,18 @@ import 
org.apache.flink.streaming.api.datastream.DataStreamSource;
 import org.apache.flink.streaming.api.datastream.KeyedStream;
 import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
 import org.apache.flink.streaming.api.typeinfo.python.PickledByteArrayTypeInfo;
+import org.apache.flink.types.Row;
 import org.apache.flink.util.CloseableIterator;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 /** Tests for {@link CompileUtils}. */
 public class CompileUtilsTest {
@@ -101,15 +106,83 @@ public class CompileUtilsTest {
     @Test
     void detectsPickledAndExplicitByteArrayPythonKeyTypes() {
         assertThat(
-                        CompileUtils.isPickledPythonKeyType(
-                                
Types.ROW(PickledByteArrayTypeInfo.PICKLED_BYTE_ARRAY_TYPE_INFO)))
+                        CompileUtils.isPickledPythonFieldType(
+                                
Types.ROW(PickledByteArrayTypeInfo.PICKLED_BYTE_ARRAY_TYPE_INFO),
+                                0))
                 .isTrue();
         assertThat(
-                        CompileUtils.isPickledPythonKeyType(
-                                Types.ROW(Types.PRIMITIVE_ARRAY(Types.BYTE))))
+                        CompileUtils.isPickledPythonFieldType(
+                                Types.ROW(Types.PRIMITIVE_ARRAY(Types.BYTE)), 
0))
                 .isFalse();
     }
 
+    @Test
+    void detectsPickledAndNonPickledPythonValueTypes() {
+        assertThat(
+                        CompileUtils.isPickledPythonFieldType(
+                                Types.ROW(
+                                        
PickledByteArrayTypeInfo.PICKLED_BYTE_ARRAY_TYPE_INFO,
+                                        
PickledByteArrayTypeInfo.PICKLED_BYTE_ARRAY_TYPE_INFO),
+                                1))
+                .isTrue();
+        assertThat(
+                        CompileUtils.isPickledPythonFieldType(
+                                Types.ROW(
+                                        
PickledByteArrayTypeInfo.PICKLED_BYTE_ARRAY_TYPE_INFO,
+                                        Types.PRIMITIVE_ARRAY(Types.BYTE)),
+                                1))
+                .isFalse();
+        assertThat(
+                        CompileUtils.isPickledPythonFieldType(
+                                Types.ROW(
+                                        
PickledByteArrayTypeInfo.PICKLED_BYTE_ARRAY_TYPE_INFO,
+                                        Types.STRING),
+                                1))
+                .isFalse();
+    }
+
+    @Test
+    void rejectsRawByteArrayPythonInputBeforeDeserializingTheAgentPlan() {
+        KeyedStream<Row, Row> inputDataStream =
+                createPythonInputStream(Types.PRIMITIVE_ARRAY(Types.BYTE));
+
+        assertThatThrownBy(() -> CompileUtils.connectToAgent(inputDataStream, 
"not-json"))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("only supports PyFlink input values")
+                .hasMessageContaining("raw byte-array");
+    }
+
+    @Test
+    void acceptsPickledPythonInputBeforeDeserializingTheAgentPlan() {
+        KeyedStream<Row, Row> inputDataStream =
+                
createPythonInputStream(PickledByteArrayTypeInfo.PICKLED_BYTE_ARRAY_TYPE_INFO);
+
+        assertThatThrownBy(() -> CompileUtils.connectToAgent(inputDataStream, 
"not-json"))
+                .isInstanceOf(JsonProcessingException.class);
+    }
+
+    @Test
+    void rejectsMalformedPythonInputType() {
+        assertThatThrownBy(
+                        () ->
+                                CompileUtils.isPickledPythonFieldType(
+                                        Types.ROW(
+                                                PickledByteArrayTypeInfo
+                                                        
.PICKLED_BYTE_ARRAY_TYPE_INFO),
+                                        1))
+                .isInstanceOf(IllegalArgumentException.class)
+                .hasMessageContaining("contain 2 fields");
+    }
+
+    private static KeyedStream<Row, Row> 
createPythonInputStream(TypeInformation<?> valueType) {
+        StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
+        TypeInformation<Row> inputType =
+                
Types.ROW(PickledByteArrayTypeInfo.PICKLED_BYTE_ARRAY_TYPE_INFO, valueType);
+        Row input = Row.of(new byte[0], new byte[0]);
+        return env.fromData(Collections.singletonList(input), inputType)
+                .keyBy(value -> Row.of(value.getField(0)));
+    }
+
     private static List<Long> getTestSequence() {
         List<Long> testSequence = new ArrayList<>();
         for (int i = 0; i < TEST_SEQUENCE_REPEAT; i++) {

Reply via email to