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

gnodet pushed a commit to branch swift-school
in repository https://gitbox.apache.org/repos/asf/camel-spring-boot.git

commit 27c4054b365c99bab49d2e2f1a6e3b002a5f3938
Author: Guillaume Nodet <[email protected]>
AuthorDate: Sat Apr 25 21:16:04 2026 +0200

    CAMEL-23250: Add Spring Boot auto-configuration for security policy 
properties
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../CamelSecurityPolicyAutoConfiguration.java      | 151 +++++++++++++++++++
 ...CamelSecurityPolicyConfigurationProperties.java | 119 +++++++++++++++
 ...rk.boot.autoconfigure.AutoConfiguration.imports |   1 +
 .../CamelSecurityPolicyAutoConfigurationTest.java  | 163 +++++++++++++++++++++
 4 files changed, 434 insertions(+)

diff --git 
a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfiguration.java
 
b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfiguration.java
new file mode 100644
index 00000000000..c0c18a84a9f
--- /dev/null
+++ 
b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfiguration.java
@@ -0,0 +1,151 @@
+/*
+ * 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.spring.boot.security;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.main.SecurityConfigurationProperties;
+import org.apache.camel.main.SecurityPolicyResult;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.apache.camel.support.CamelContextHelper;
+import org.apache.camel.util.SecurityUtils;
+import org.apache.camel.util.SecurityViolation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import 
org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.EnumerablePropertySource;
+import org.springframework.core.env.Environment;
+
+@AutoConfiguration(after = CamelAutoConfiguration.class)
+@ConditionalOnBean(CamelAutoConfiguration.class)
+@EnableConfigurationProperties(CamelSecurityPolicyConfigurationProperties.class)
+public class CamelSecurityPolicyAutoConfiguration {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(CamelSecurityPolicyAutoConfiguration.class);
+
+    @Bean
+    SecurityPolicyResult camelSecurityPolicyResult(
+            CamelContext camelContext,
+            CamelSecurityPolicyConfigurationProperties config,
+            Environment environment) {
+
+        SecurityConfigurationProperties securityConfig = 
applySecurityProperties(camelContext, config);
+
+        Map<String, Object> camelProperties = 
extractCamelProperties(environment);
+
+        List<SecurityViolation> violations = SecurityUtils.detectViolations(
+                camelProperties,
+                (k, v) -> containsSensitive(camelContext, k, v),
+                securityConfig::resolvePolicy,
+                securityConfig.getAllowedPropertySet());
+
+        SecurityPolicyResult result = new SecurityPolicyResult(violations);
+        
camelContext.getCamelContextExtension().addContextPlugin(SecurityPolicyResult.class,
 result);
+
+        enforceViolations(violations);
+
+        return result;
+    }
+
+    private SecurityConfigurationProperties applySecurityProperties(
+            CamelContext camelContext,
+            CamelSecurityPolicyConfigurationProperties config) {
+
+        // get the security config from camel-main's 
MainConfigurationProperties
+        // which is already bound by CamelAutoConfiguration via 
CamelConfigurationProperties
+        SecurityConfigurationProperties securityConfig
+                = new SecurityConfigurationProperties(null);
+
+        securityConfig.setPolicy(config.getPolicy());
+        if (config.getSecretPolicy() != null) {
+            securityConfig.setSecretPolicy(config.getSecretPolicy());
+        }
+        if (config.getInsecureSslPolicy() != null) {
+            securityConfig.setInsecureSslPolicy(config.getInsecureSslPolicy());
+        }
+        if (config.getInsecureSerializationPolicy() != null) {
+            
securityConfig.setInsecureSerializationPolicy(config.getInsecureSerializationPolicy());
+        }
+        if (config.getInsecureDevPolicy() != null) {
+            securityConfig.setInsecureDevPolicy(config.getInsecureDevPolicy());
+        }
+        if (config.getAllowedProperties() != null) {
+            securityConfig.setAllowedProperties(config.getAllowedProperties());
+        }
+
+        return securityConfig;
+    }
+
+    private static Map<String, Object> extractCamelProperties(Environment 
environment) {
+        Map<String, Object> properties = new LinkedHashMap<>();
+
+        if (environment instanceof ConfigurableEnvironment ce) {
+            ce.getPropertySources().forEach(ps -> {
+                if (ps instanceof EnumerablePropertySource<?> eps) {
+                    for (String name : eps.getPropertyNames()) {
+                        if (name.startsWith("camel.") && 
!name.startsWith("camel.security.")) {
+                            Object value = environment.getProperty(name);
+                            if (value != null) {
+                                properties.putIfAbsent(name, value);
+                            }
+                        }
+                    }
+                }
+            });
+        }
+
+        return properties;
+    }
+
+    private static boolean containsSensitive(CamelContext camelContext, String 
key, Object value) {
+        boolean answer = CamelContextHelper.containsSensitive(camelContext, 
key);
+        if (!answer && value != null) {
+            String v = value.toString();
+            answer = v.startsWith("RAW(");
+        }
+        return answer;
+    }
+
+    private static void enforceViolations(List<SecurityViolation> violations) {
+        List<String> failures = new ArrayList<>();
+        for (SecurityViolation v : violations) {
+            if ("fail".equals(v.policy())) {
+                failures.add(v.toString());
+            } else {
+                LOG.warn("SECURITY WARNING: {}", v);
+            }
+        }
+
+        if (!failures.isEmpty()) {
+            throw new RuntimeCamelException(
+                    "Security policy violations detected (policy=fail):\n - " 
+ String.join("\n - ", failures)
+                                            + "\nTo allow specific properties, 
add them to camel.security.allowed-properties"
+                                            + " or change the policy to 'warn' 
or 'allow'.");
+        }
+    }
+
+}
diff --git 
a/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyConfigurationProperties.java
 
b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyConfigurationProperties.java
new file mode 100644
index 00000000000..d358776e600
--- /dev/null
+++ 
b/core/camel-spring-boot/src/main/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyConfigurationProperties.java
@@ -0,0 +1,119 @@
+/*
+ * 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.spring.boot.security;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * Security policy configuration for Camel Spring Boot applications.
+ * <p>
+ * Controls how Camel reacts to insecure configuration at startup. Policies 
can be set globally or per security
+ * category:
+ * <ul>
+ * <li>{@code allow} — no warnings, allow the configuration</li>
+ * <li>{@code warn} — log a warning at startup (default)</li>
+ * <li>{@code fail} — throw an exception and prevent startup</li>
+ * </ul>
+ */
+@ConfigurationProperties(prefix = "camel.security")
+public class CamelSecurityPolicyConfigurationProperties {
+
+    /**
+     * Global security policy applied to all categories unless overridden. 
Controls how Camel reacts when insecure
+     * configuration is detected at startup.
+     */
+    private String policy = "warn";
+
+    /**
+     * Security policy for plain-text secrets. When set, overrides the global 
policy for properties that contain
+     * sensitive values configured as plain text.
+     */
+    private String secretPolicy;
+
+    /**
+     * Security policy for insecure SSL/TLS configuration. When set, overrides 
the global policy for options that
+     * disable certificate validation or hostname verification.
+     */
+    private String insecureSslPolicy;
+
+    /**
+     * Security policy for insecure deserialization configuration. When set, 
overrides the global policy for options that
+     * enable dangerous deserialization of untrusted data.
+     */
+    private String insecureSerializationPolicy;
+
+    /**
+     * Security policy for development-only features. When set, overrides the 
global policy for options intended only for
+     * development environments.
+     */
+    private String insecureDevPolicy;
+
+    /**
+     * Comma-separated list of property keys to exclude from security policy 
checks. Use full property paths (e.g.,
+     * camel.component.aws2-s3.trustAllCertificates) to allow specific 
properties regardless of the configured policy.
+     */
+    private String allowedProperties;
+
+    public String getPolicy() {
+        return policy;
+    }
+
+    public void setPolicy(String policy) {
+        this.policy = policy;
+    }
+
+    public String getSecretPolicy() {
+        return secretPolicy;
+    }
+
+    public void setSecretPolicy(String secretPolicy) {
+        this.secretPolicy = secretPolicy;
+    }
+
+    public String getInsecureSslPolicy() {
+        return insecureSslPolicy;
+    }
+
+    public void setInsecureSslPolicy(String insecureSslPolicy) {
+        this.insecureSslPolicy = insecureSslPolicy;
+    }
+
+    public String getInsecureSerializationPolicy() {
+        return insecureSerializationPolicy;
+    }
+
+    public void setInsecureSerializationPolicy(String 
insecureSerializationPolicy) {
+        this.insecureSerializationPolicy = insecureSerializationPolicy;
+    }
+
+    public String getInsecureDevPolicy() {
+        return insecureDevPolicy;
+    }
+
+    public void setInsecureDevPolicy(String insecureDevPolicy) {
+        this.insecureDevPolicy = insecureDevPolicy;
+    }
+
+    public String getAllowedProperties() {
+        return allowedProperties;
+    }
+
+    public void setAllowedProperties(String allowedProperties) {
+        this.allowedProperties = allowedProperties;
+    }
+
+}
diff --git 
a/core/camel-spring-boot/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
 
b/core/camel-spring-boot/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index e2dd68b6d89..31f55659dc8 100644
--- 
a/core/camel-spring-boot/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ 
b/core/camel-spring-boot/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -25,6 +25,7 @@ 
org.apache.camel.spring.boot.actuate.health.CamelAvailabilityCheckAutoConfigurat
 org.apache.camel.spring.boot.actuate.info.CamelInfoAutoConfiguration
 org.apache.camel.spring.boot.cluster.ClusteredRouteControllerAutoConfiguration
 
org.apache.camel.spring.boot.routecontroller.SupervisingRouteControllerAutoConfiguration
+org.apache.camel.spring.boot.security.CamelSecurityPolicyAutoConfiguration
 org.apache.camel.spring.boot.security.CamelSSLAutoConfiguration
 org.apache.camel.spring.boot.threadpool.CamelThreadPoolAutoConfiguration
 org.apache.camel.spring.boot.trace.CamelTraceAutoConfiguration
diff --git 
a/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfigurationTest.java
 
b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfigurationTest.java
new file mode 100644
index 00000000000..31d1585b95b
--- /dev/null
+++ 
b/core/camel-spring-boot/src/test/java/org/apache/camel/spring/boot/security/CamelSecurityPolicyAutoConfigurationTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.spring.boot.security;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.main.SecurityPolicyResult;
+import org.apache.camel.spring.boot.CamelAutoConfiguration;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+
+public class CamelSecurityPolicyAutoConfigurationTest {
+
+    private final ApplicationContextRunner runner = new 
ApplicationContextRunner()
+            .withConfiguration(AutoConfigurations.of(
+                    CamelAutoConfiguration.class,
+                    CamelSecurityPolicyAutoConfiguration.class));
+
+    @Test
+    public void noSecurityPropertiesShouldStartNormally() {
+        runner.run(context -> {
+            assertThat(context).hasNotFailed();
+            SecurityPolicyResult result = 
context.getBean(SecurityPolicyResult.class);
+            assertThat(result).isNotNull();
+            assertThat(result.hasViolations()).isFalse();
+        });
+    }
+
+    @Test
+    public void policyAllowShouldIgnoreInsecureConfig() {
+        runner.withPropertyValues(
+                "camel.security.policy=allow",
+                "camel.component.http.trustAllCertificates=true")
+                .run(context -> {
+                    assertThat(context).hasNotFailed();
+                    SecurityPolicyResult result = 
context.getBean(SecurityPolicyResult.class);
+                    assertThat(result.hasViolations()).isFalse();
+                });
+    }
+
+    @Test
+    public void policyWarnShouldStartWithViolations() {
+        runner.withPropertyValues(
+                "camel.security.policy=warn",
+                "camel.component.http.trustAllCertificates=true")
+                .run(context -> {
+                    assertThat(context).hasNotFailed();
+                    SecurityPolicyResult result = 
context.getBean(SecurityPolicyResult.class);
+                    assertThat(result.hasViolations()).isTrue();
+                    assertThat(result.getViolations()).anyMatch(
+                            v -> 
v.propertyKey().contains("trustAllCertificates") && "warn".equals(v.policy()));
+                });
+    }
+
+    @Test
+    public void policyFailShouldPreventStartup() {
+        runner.withPropertyValues(
+                "camel.security.policy=fail",
+                "camel.component.http.trustAllCertificates=true")
+                .run(context -> {
+                    assertThat(context).hasFailed();
+                    assertThat(context.getStartupFailure())
+                            .rootCause()
+                            .isInstanceOf(RuntimeCamelException.class)
+                            .hasMessageContaining("Security policy violations 
detected");
+                });
+    }
+
+    @Test
+    public void categoryOverrideShouldTakePrecedence() {
+        runner.withPropertyValues(
+                "camel.security.policy=fail",
+                "camel.security.insecure-ssl-policy=allow",
+                "camel.component.http.trustAllCertificates=true")
+                .run(context -> {
+                    assertThat(context).hasNotFailed();
+                    SecurityPolicyResult result = 
context.getBean(SecurityPolicyResult.class);
+                    assertThat(result.hasViolations()).isFalse();
+                });
+    }
+
+    @Test
+    public void categoryOverrideWarnWhileGlobalFail() {
+        runner.withPropertyValues(
+                "camel.security.policy=fail",
+                "camel.security.insecure-ssl-policy=warn",
+                "camel.component.http.trustAllCertificates=true")
+                .run(context -> {
+                    assertThat(context).hasNotFailed();
+                    SecurityPolicyResult result = 
context.getBean(SecurityPolicyResult.class);
+                    assertThat(result.hasViolations()).isTrue();
+                    assertThat(result.getViolations()).anyMatch(
+                            v -> "warn".equals(v.policy()));
+                });
+    }
+
+    @Test
+    public void allowedPropertiesShouldExcludeFromChecks() {
+        runner.withPropertyValues(
+                "camel.security.policy=fail",
+                
"camel.security.allowed-properties=camel.component.http.trustAllCertificates",
+                "camel.component.http.trustAllCertificates=true")
+                .run(context -> {
+                    assertThat(context).hasNotFailed();
+                    SecurityPolicyResult result = 
context.getBean(SecurityPolicyResult.class);
+                    assertThat(result.hasViolations()).isFalse();
+                });
+    }
+
+    @Test
+    public void multipleViolationsDetected() {
+        runner.withPropertyValues(
+                "camel.security.policy=warn",
+                "camel.component.http.trustAllCertificates=true",
+                "camel.component.netty.allowJavaSerializedObject=true")
+                .run(context -> {
+                    assertThat(context).hasNotFailed();
+                    SecurityPolicyResult result = 
context.getBean(SecurityPolicyResult.class);
+                    
assertThat(result.getViolationCount()).isGreaterThanOrEqualTo(2);
+                });
+    }
+
+    @Test
+    public void insecureDevPolicyOverride() {
+        runner.withPropertyValues(
+                "camel.security.policy=fail",
+                "camel.security.insecure-dev-policy=allow",
+                "camel.main.devConsoleEnabled=true")
+                .run(context -> {
+                    assertThat(context).hasNotFailed();
+                });
+    }
+
+    @Test
+    public void insecureSerializationPolicyOverride() {
+        runner.withPropertyValues(
+                "camel.security.policy=fail",
+                "camel.security.insecure-serialization-policy=warn",
+                "camel.component.netty.allowJavaSerializedObject=true")
+                .run(context -> {
+                    assertThat(context).hasNotFailed();
+                    SecurityPolicyResult result = 
context.getBean(SecurityPolicyResult.class);
+                    assertThat(result.hasViolations()).isTrue();
+                });
+    }
+
+}

Reply via email to