atiaomar1978-hub commented on code in PR #26026:
URL: https://github.com/apache/camel/pull/26026#discussion_r3915845150


##########
components/camel-avro-rpc/camel-avro-rpc-component/src/main/java/org/apache/camel/component/avro/AvroEndpoint.java:
##########
@@ -114,4 +118,21 @@ private void validateConfiguration(AvroConfiguration 
config) throws Exception {
             }
         }
     }
+
+    private void configureClassSecurity(AvroConfiguration config) {
+        AvroClassSecuritySupport.ensureAvroIpcPackagesTrusted();
+        
AvroClassSecuritySupport.trustPackages(config.getSerializablePackages());
+        AvroClassSecuritySupport.trustClassName(config.getProtocolClassName());
+        if (config.getProtocol() != null) {
+            
AvroClassSecuritySupport.trustPackages(config.getProtocol().getNamespace());
+            for (Schema type : config.getProtocol().getTypes()) {
+                if (type.getNamespace() != null) {

Review Comment:
   Fixed in 69f78c23: `trustProtocol()` skips error schemas and 
`isSystemPackage()` blocks `java.*`/`javax.*`/`jdk.*`/`sun.*` namespaces from 
entering the allowlist.
   
   _AI-generated reply on behalf of @atiaomar1978-hub_



##########
components/camel-avro/src/main/java/org/apache/camel/dataformat/avro/AvroDataFormat.java:
##########
@@ -135,6 +158,9 @@ protected Schema loadSchema(String className) throws 
CamelException, ClassNotFou
 
     @Override
     public void marshal(Exchange exchange, Object graph, OutputStream 
outputStream) throws Exception {
+        if (actualSchema == null) {
+            
AvroClassSecuritySupport.trustClassName(graph.getClass().getName());

Review Comment:
   Fixed in 69f78c23: marshal calls `loadSchema()` before any trust; uses 
`trustClassNameOnly()` (exact class, no package) and short-circuits when 
already trusted.
   
   _AI-generated reply on behalf of @atiaomar1978-hub_



##########
components/camel-avro/src/main/java/org/apache/camel/avro/support/AvroClassSecuritySupport.java:
##########
@@ -0,0 +1,172 @@
+/*
+ * 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.camel.avro.support;
+
+import java.util.Arrays;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.avro.util.ClassSecurityValidator;
+import org.apache.avro.util.ClassSecurityValidator.ClassSecurityPredicate;
+
+/**
+ * Configures Apache Avro {@link ClassSecurityValidator} with Camel trusted 
packages.
+ * <p>
+ * Avro 1.12+ validates classes resolved from schemas. Camel automatically 
trusts packages derived from configured
+ * protocol or schema classes. Additional packages can be configured through 
the {@code serializablePackages} endpoint
+ * option.
+ * <p>
+ * Trusted packages are stored in a JVM-wide registry shared by all Camel 
contexts in the process. Trust is cumulative
+ * and cannot be revoked in production.
+ */
+public final class AvroClassSecuritySupport {
+
+    private static final Set<String> TRUSTED_PACKAGES = 
ConcurrentHashMap.newKeySet();
+
+    private static final Set<String> TRUSTED_CLASSES = 
ConcurrentHashMap.newKeySet();
+
+    private static final Object LOCK = new Object();
+
+    private static final ClassSecurityPredicate CAMEL_TRUSTED = 
AvroClassSecuritySupport::isCamelTrusted;
+
+    private static volatile ClassSecurityPredicate baseValidator = 
ClassSecurityValidator.DEFAULT;
+
+    private static volatile ClassSecurityPredicate installedGlobal;
+
+    private static volatile NavigableSet<String> normalizedPackagePrefixes = 
new TreeSet<>();
+
+    private AvroClassSecuritySupport() {
+    }
+
+    /**
+     * Trusts Avro IPC classes required for camel-avro-rpc handshake.
+     */
+    public static void ensureAvroIpcPackagesTrusted() {
+        trustPackages("org.apache.avro.ipc");
+    }
+
+    /**
+     * Trusts the exact class name and its package for schema resolution.
+     */
+    public static void trustClassName(String className) {
+        if (className == null || className.isBlank()) {
+            return;
+        }
+        synchronized (LOCK) {
+            TRUSTED_CLASSES.add(className);
+            int lastDot = className.lastIndexOf('.');
+            if (lastDot > 0) {
+                TRUSTED_PACKAGES.add(normalizePackage(className.substring(0, 
lastDot)));
+            }
+            rebuildNormalizedPackagePrefixes();
+            refreshGlobal();
+        }
+    }
+
+    /**
+     * Trusts the comma-separated list of packages.
+     */
+    public static void trustPackages(String packages) {
+        if (packages == null || packages.isBlank()) {
+            return;
+        }
+        trustPackages(parsePackages(packages).toArray(String[]::new));
+    }
+
+    /**
+     * Trusts the given packages.
+     */
+    public static void trustPackages(String... packages) {
+        if (packages == null || packages.length == 0) {
+            return;
+        }
+        synchronized (LOCK) {
+            for (String pkg : packages) {
+                if (pkg != null && !pkg.isBlank()) {
+                    TRUSTED_PACKAGES.add(normalizePackage(pkg));
+                }
+            }
+            rebuildNormalizedPackagePrefixes();
+            refreshGlobal();
+        }
+    }
+
+    /**
+     * Clears Camel-managed trusted classes and packages. Intended for tests.
+     */
+    public static void resetForTesting() {
+        synchronized (LOCK) {
+            TRUSTED_PACKAGES.clear();
+            TRUSTED_CLASSES.clear();
+            normalizedPackagePrefixes = new TreeSet<>();
+            baseValidator = ClassSecurityValidator.DEFAULT;
+            installedGlobal = null;
+            ClassSecurityValidator.setGlobal(ClassSecurityValidator.DEFAULT);
+        }
+    }
+
+    private static void refreshGlobal() {
+        if (installedGlobal == null) {

Review Comment:
   Fixed in 69f78c23: `refreshGlobal()` re-reads `getGlobal()` on every trust 
change; if it differs from `installedGlobal`, it is adopted as the new base 
before composing Camel trust.
   
   _AI-generated reply on behalf of @atiaomar1978-hub_



##########
components/camel-avro/src/main/java/org/apache/camel/dataformat/avro/AvroDataFormat.java:
##########
@@ -91,6 +97,11 @@ protected void doInit() throws Exception {
         } else if (instanceClassName != null) {
             actualSchema = loadSchema(instanceClassName);
         }
+
+        if (actualSchema != null) {
+            
AvroClassSecuritySupport.trustPackages(actualSchema.getNamespace());

Review Comment:
   Fixed in 69f78c23: `trustSchema()` walks the graph; non-named roots 
(ARRAY/UNION/MAP) no longer call `getNamespace()` directly.
   
   _AI-generated reply on behalf of @atiaomar1978-hub_



##########
components/camel-avro/src/main/java/org/apache/camel/avro/support/AvroClassSecuritySupport.java:
##########
@@ -0,0 +1,172 @@
+/*
+ * 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.camel.avro.support;
+
+import java.util.Arrays;
+import java.util.NavigableSet;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.avro.util.ClassSecurityValidator;
+import org.apache.avro.util.ClassSecurityValidator.ClassSecurityPredicate;
+
+/**
+ * Configures Apache Avro {@link ClassSecurityValidator} with Camel trusted 
packages.
+ * <p>
+ * Avro 1.12+ validates classes resolved from schemas. Camel automatically 
trusts packages derived from configured
+ * protocol or schema classes. Additional packages can be configured through 
the {@code serializablePackages} endpoint
+ * option.
+ * <p>
+ * Trusted packages are stored in a JVM-wide registry shared by all Camel 
contexts in the process. Trust is cumulative
+ * and cannot be revoked in production.
+ */
+public final class AvroClassSecuritySupport {
+
+    private static final Set<String> TRUSTED_PACKAGES = 
ConcurrentHashMap.newKeySet();
+
+    private static final Set<String> TRUSTED_CLASSES = 
ConcurrentHashMap.newKeySet();
+
+    private static final Object LOCK = new Object();
+
+    private static final ClassSecurityPredicate CAMEL_TRUSTED = 
AvroClassSecuritySupport::isCamelTrusted;
+
+    private static volatile ClassSecurityPredicate baseValidator = 
ClassSecurityValidator.DEFAULT;
+
+    private static volatile ClassSecurityPredicate installedGlobal;
+
+    private static volatile NavigableSet<String> normalizedPackagePrefixes = 
new TreeSet<>();
+
+    private AvroClassSecuritySupport() {
+    }
+
+    /**
+     * Trusts Avro IPC classes required for camel-avro-rpc handshake.
+     */
+    public static void ensureAvroIpcPackagesTrusted() {
+        trustPackages("org.apache.avro.ipc");
+    }
+
+    /**
+     * Trusts the exact class name and its package for schema resolution.
+     */
+    public static void trustClassName(String className) {
+        if (className == null || className.isBlank()) {
+            return;
+        }
+        synchronized (LOCK) {
+            TRUSTED_CLASSES.add(className);
+            int lastDot = className.lastIndexOf('.');
+            if (lastDot > 0) {
+                TRUSTED_PACKAGES.add(normalizePackage(className.substring(0, 
lastDot)));
+            }
+            rebuildNormalizedPackagePrefixes();
+            refreshGlobal();
+        }
+    }
+
+    /**
+     * Trusts the comma-separated list of packages.
+     */
+    public static void trustPackages(String packages) {
+        if (packages == null || packages.isBlank()) {
+            return;
+        }
+        trustPackages(parsePackages(packages).toArray(String[]::new));
+    }
+
+    /**
+     * Trusts the given packages.
+     */
+    public static void trustPackages(String... packages) {
+        if (packages == null || packages.length == 0) {
+            return;
+        }
+        synchronized (LOCK) {
+            for (String pkg : packages) {
+                if (pkg != null && !pkg.isBlank()) {
+                    TRUSTED_PACKAGES.add(normalizePackage(pkg));
+                }
+            }
+            rebuildNormalizedPackagePrefixes();
+            refreshGlobal();
+        }
+    }
+
+    /**
+     * Clears Camel-managed trusted classes and packages. Intended for tests.
+     */
+    public static void resetForTesting() {
+        synchronized (LOCK) {
+            TRUSTED_PACKAGES.clear();
+            TRUSTED_CLASSES.clear();
+            normalizedPackagePrefixes = new TreeSet<>();
+            baseValidator = ClassSecurityValidator.DEFAULT;
+            installedGlobal = null;
+            ClassSecurityValidator.setGlobal(ClassSecurityValidator.DEFAULT);
+        }
+    }
+
+    private static void refreshGlobal() {
+        if (installedGlobal == null) {
+            ClassSecurityPredicate current = 
ClassSecurityValidator.getGlobal();
+            if (current != null && current != ClassSecurityValidator.DEFAULT) {
+                baseValidator = current;
+            }
+        }
+        installedGlobal = ClassSecurityValidator.composite(baseValidator, 
CAMEL_TRUSTED);
+        ClassSecurityValidator.setGlobal(installedGlobal);
+    }
+
+    private static boolean isCamelTrusted(Class<?> clazz) {
+        String className = clazz.getName();
+        if (TRUSTED_CLASSES.contains(className)) {
+            return true;
+        }
+        NavigableSet<String> packages = normalizedPackagePrefixes;
+        String lower = packages.lower(className);

Review Comment:
   Fixed in 69f78c23: prefix check iterates all normalized prefixes so parent 
packages are not shadowed by longer child prefixes.
   
   _AI-generated reply on behalf of @atiaomar1978-hub_



##########
components/camel-avro/src/main/java/org/apache/camel/dataformat/avro/AvroDataFormat.java:
##########
@@ -91,6 +97,11 @@ protected void doInit() throws Exception {
         } else if (instanceClassName != null) {
             actualSchema = loadSchema(instanceClassName);
         }
+
+        if (actualSchema != null) {
+            
AvroClassSecuritySupport.trustPackages(actualSchema.getNamespace());
+            
AvroClassSecuritySupport.trustClassName(actualSchema.getFullName());

Review Comment:
   Fixed in 69f78c23: `trustSchema()` / `trustProtocol()` recursively collect 
all named nested types from fields, arrays, maps, and unions.
   
   _AI-generated reply on behalf of @atiaomar1978-hub_



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to