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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 92c77eaf8808 CAMEL-25134: camel-main - Security policy check matches 
component options by component (#27070)
92c77eaf8808 is described below

commit 92c77eaf880829407bbfdd3e814a489e3d6f6bd5
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Sep 29 14:01:18 2026 +0200

    CAMEL-25134: camel-main - Security policy check matches component options 
by component (#27070)
    
    * CAMEL-25134: camel-main - Security policy check matches component options 
by component
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
 .../apache/camel/main/MainSecurityPolicyTest.java  |  38 +++-
 .../java/org/apache/camel/util/SecurityUtils.java  | 221 ++++++++++++++++++++-
 .../org/apache/camel/util/SecurityUtilsTest.java   |  63 +++++-
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    |  23 ++-
 .../modules/ROOT/pages/security-policy.adoc        |  11 +-
 .../maven/packaging/UpdateSensitizeHelper.java     |  76 ++++++-
 6 files changed, 409 insertions(+), 23 deletions(-)

diff --git 
a/core/camel-main/src/test/java/org/apache/camel/main/MainSecurityPolicyTest.java
 
b/core/camel-main/src/test/java/org/apache/camel/main/MainSecurityPolicyTest.java
index 1bc71551886d..da9cda420b24 100644
--- 
a/core/camel-main/src/test/java/org/apache/camel/main/MainSecurityPolicyTest.java
+++ 
b/core/camel-main/src/test/java/org/apache/camel/main/MainSecurityPolicyTest.java
@@ -22,7 +22,9 @@ import java.util.Map;
 import java.util.Set;
 import java.util.stream.Stream;
 
+import org.apache.camel.Endpoint;
 import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.support.DefaultComponent;
 import org.apache.camel.util.SecurityUtils;
 import org.apache.camel.util.SecurityViolation;
 import org.junit.jupiter.api.Test;
@@ -267,7 +269,7 @@ public class MainSecurityPolicyTest {
     public void testInsecureSerializationViaDetectViolations() {
         // test insecure:serialization detection via 
SecurityUtils.detectViolations()
         Map<String, Object> properties = new LinkedHashMap<>();
-        properties.put("camel.component.jms.allowJavaSerializedObject", 
"true");
+        properties.put("camel.component.jms.transferException", "true");
 
         List<SecurityViolation> violations = SecurityUtils.detectViolations(
                 properties,
@@ -277,7 +279,7 @@ public class MainSecurityPolicyTest {
 
         assertEquals(1, violations.size());
         assertEquals("insecure:serialization", violations.get(0).category());
-        
assertTrue(violations.get(0).propertyKey().contains("allowJavaSerializedObject"));
+        
assertTrue(violations.get(0).propertyKey().contains("transferException"));
     }
 
     @Test
@@ -552,4 +554,36 @@ public class MainSecurityPolicyTest {
             main.stop();
         }
     }
+
+    @Test
+    public void testOptionWithSameNameAsInsecureOptionOfAnotherComponent() {
+        // tls is an insecure:ssl option of camel-pinecone only, so tls=false 
on another component is not flagged
+        Main main = new Main();
+        main.bind("mytls", new MyTlsComponent());
+        main.addInitialProperty("camel.security.insecureSslPolicy", "fail");
+        main.addInitialProperty("camel.component.mytls.tls", "false");
+
+        assertDoesNotThrow(() -> {
+            main.start();
+            main.stop();
+        });
+    }
+
+    public static class MyTlsComponent extends DefaultComponent {
+
+        private boolean tls = true;
+
+        public boolean isTls() {
+            return tls;
+        }
+
+        public void setTls(boolean tls) {
+            this.tls = tls;
+        }
+
+        @Override
+        protected Endpoint createEndpoint(String uri, String remaining, 
Map<String, Object> parameters) {
+            throw new UnsupportedOperationException();
+        }
+    }
 }
diff --git 
a/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java 
b/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
index ed7178491d1b..5074ee221df6 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
@@ -101,6 +101,179 @@ public final class SecurityUtils {
         SECURITY_OPTIONS = Collections.unmodifiableMap(map);
     }
 
+    // the components, data formats and languages that declare each security 
option (such as component:netty),
+    // so a configuration key that identifies one of them is only matched 
against its own options
+    private static final Map<String, Set<String>> SECURITY_OPTION_OWNERS;
+
+    static {
+        Map<String, Set<String>> owners = new HashMap<>();
+        // Generated by camel build tools - do NOT edit this map!
+        // @formatter:off
+        // SECURITY-OPTION-OWNERS: START
+        owners.put("allowcontrolheaders", Set.of(
+                "component:exec"));
+        owners.put("allowexternalentities", Set.of(
+                "component:smooks"));
+        owners.put("allowfilepathsource", Set.of(
+                "component:docling"));
+        owners.put("allowjavaserializedobject", Set.of(
+                "component:atmospherewebsocket",
+                "component:http",
+                "component:https",
+                "component:jetty",
+                "component:servlet",
+                "component:vertxhttp"));
+        owners.put("allowlocalwebhookurls", Set.of(
+                "component:a2a"));
+        owners.put("allowoperationheader", Set.of(
+                "component:spiffe"));
+        owners.put("allowpredicatefrommessage", Set.of(
+                "component:dynamicroutercontrol"));
+        owners.put("allowqueryfromheader", Set.of(
+                "component:sql"));
+        owners.put("allowserializedheaders", Set.of(
+                "component:activemq",
+                "component:activemq6",
+                "component:amqp",
+                "component:jms",
+                "component:netty",
+                "component:nettyhttp"));
+        owners.put("allowtemplatefromheader", Set.of(
+                "component:sqlstored"));
+        owners.put("allowurlbody", Set.of(
+                "component:ldif"));
+        owners.put("allowurlsource", Set.of(
+                "component:docling"));
+        owners.put("failonunknownhost", Set.of(
+                "component:ssh"));
+        owners.put("failopen", Set.of(
+                "component:opa"));
+        owners.put("hostnameverification", Set.of(
+                "component:netty",
+                "component:nettyhttp"));
+        owners.put("httpshostnameverificationenabled", Set.of(
+                "component:paho",
+                "component:pahomqtt5"));
+        owners.put("ignoresslverification", Set.of(
+                "component:hwclouddms",
+                "component:hwcloudfrs",
+                "component:hwcloudfunctiongraph",
+                "component:hwcloudiam",
+                "component:hwcloudimagerecognition",
+                "component:hwcloudobs",
+                "component:hwcloudsmn"));
+        owners.put("ignoresslwarnings", Set.of(
+                "component:oaipmh"));
+        owners.put("knownhostsresource", Set.of(
+                "component:ssh"));
+        owners.put("objectcodecpattern", Set.of(
+                "component:mina"));
+        owners.put("objectmessageenabled", Set.of(
+                "component:activemq",
+                "component:activemq6",
+                "component:amqp",
+                "component:jms",
+                "component:sjms",
+                "component:sjms2"));
+        owners.put("serializablepackages", Set.of(
+                "component:avro",
+                "dataformat:avro"));
+        owners.put("skiptlsverify", Set.of(
+                "component:splunkhec"));
+        owners.put("ssl", Set.of(
+                "component:hivemq"));
+        owners.put("sslendpointalgorithm", Set.of(
+                "component:kafka",
+                "component:llm",
+                "component:openai"));
+        owners.put("stricthostkeychecking", Set.of(
+                "component:minasftp",
+                "component:scp",
+                "component:sftp"));
+        owners.put("tls", Set.of(
+                "component:pinecone"));
+        owners.put("transferexception", Set.of(
+                "component:activemq",
+                "component:activemq6",
+                "component:amqp",
+                "component:atmospherewebsocket",
+                "component:jetty",
+                "component:jms",
+                "component:nettyhttp",
+                "component:servlet",
+                "component:sjms",
+                "component:sjms2",
+                "component:undertow",
+                "component:vertxhttp"));
+        owners.put("transferexchange", Set.of(
+                "component:activemq",
+                "component:activemq6",
+                "component:amqp",
+                "component:hazelcastseda",
+                "component:jms",
+                "component:mina",
+                "component:netty",
+                "component:nettyhttp"));
+        owners.put("trustallcertificates", Set.of(
+                "component:aws2athena",
+                "component:aws2comprehend",
+                "component:aws2cw",
+                "component:aws2ddb",
+                "component:aws2ddbstream",
+                "component:aws2ec2",
+                "component:aws2ecs",
+                "component:aws2eks",
+                "component:aws2eventbridge",
+                "component:aws2iam",
+                "component:aws2kinesis",
+                "component:aws2kinesisfirehose",
+                "component:aws2kms",
+                "component:aws2lambda",
+                "component:aws2mq",
+                "component:aws2msk",
+                "component:aws2polly",
+                "component:aws2redshiftdata",
+                "component:aws2rekognition",
+                "component:aws2s3",
+                "component:aws2s3vectors",
+                "component:aws2ses",
+                "component:aws2sns",
+                "component:aws2sqs",
+                "component:aws2stepfunctions",
+                "component:aws2sts",
+                "component:aws2textract",
+                "component:aws2timestream",
+                "component:aws2transcribe",
+                "component:aws2translate",
+                "component:awsbedrock",
+                "component:awsbedrockagent",
+                "component:awsbedrockagentruntime",
+                "component:awscloudtrail",
+                "component:awsconfig",
+                "component:awssecretsmanager",
+                "component:awssecurityhub"));
+        owners.put("trustallpackages", Set.of(
+                "component:activemq",
+                "component:activemq6"));
+        owners.put("usejavamailsessionpropertiesfromheaders", Set.of(
+                "component:imap",
+                "component:imaps",
+                "component:pop3",
+                "component:pop3s",
+                "component:smtp",
+                "component:smtps"));
+        owners.put("validateauth", Set.of(
+                "component:a2a"));
+        owners.put("verifyssl", Set.of(
+                "component:ibmwatsonxai"));
+        owners.put("x509hostnameverifier", Set.of(
+                "component:http",
+                "component:https"));
+        // SECURITY-OPTION-OWNERS: END
+        // @formatter:on
+        SECURITY_OPTION_OWNERS = Collections.unmodifiableMap(owners);
+    }
+
     private SecurityUtils() {
     }
 
@@ -113,18 +286,52 @@ public final class SecurityUtils {
 
     /**
      * Get security information for a configuration property.
+     * <p>
+     * A key that identifies a component, data format or language (such as 
{@code camel.component.netty.ssl}) only
+     * matches a security option that this component, data format or language 
declares. Any other key (such as a
+     * camel-main option, or an option name without a prefix) matches by the 
option name.
      *
-     * @param  text the configuration property key (e.g., 
"camel.component.http.trustAllCertificates")
+     * @param  text the configuration property key (e.g., 
"camel.component.aws2-s3.trustAllCertificates")
      * @return      the security option info, or null if the property has no 
security category
      */
     public static SecurityOption getSecurityOption(String text) {
-        int lastPeriod = text.lastIndexOf('.');
-        if (lastPeriod >= 0) {
-            text = text.substring(lastPeriod + 1);
+        String name = normalizeOptionName(text);
+        SecurityOption answer = SECURITY_OPTIONS.get(name);
+        if (answer != null) {
+            String owner = ownerOf(text);
+            Set<String> owners = SECURITY_OPTION_OWNERS.get(name);
+            if (owner != null && owners != null && !owners.contains(owner)) {
+                // the option has the same name as a security option of 
another component, data format or language
+                return null;
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * The owner of a configuration property key that identifies a component, 
data format or language, such as
+     * {@code component:nettyhttp} for {@code camel.component.netty-http.ssl}, 
or null for any other key.
+     */
+    private static String ownerOf(String text) {
+        String kind;
+        String remainder;
+        if (text.startsWith("camel.component.")) {
+            kind = "component:";
+            remainder = text.substring(16);
+        } else if (text.startsWith("camel.dataformat.")) {
+            kind = "dataformat:";
+            remainder = text.substring(17);
+        } else if (text.startsWith("camel.language.")) {
+            kind = "language:";
+            remainder = text.substring(15);
+        } else {
+            return null;
+        }
+        int dot = remainder.indexOf('.');
+        if (dot <= 0) {
+            return null;
         }
-        text = text.toLowerCase(Locale.ENGLISH);
-        text = text.replace("-", "");
-        return SECURITY_OPTIONS.get(text);
+        return kind + remainder.substring(0, 
dot).toLowerCase(Locale.ENGLISH).replace("-", "");
     }
 
     /**
diff --git 
a/core/camel-util/src/test/java/org/apache/camel/util/SecurityUtilsTest.java 
b/core/camel-util/src/test/java/org/apache/camel/util/SecurityUtilsTest.java
index 1f98d8618556..29e3606f25c3 100644
--- a/core/camel-util/src/test/java/org/apache/camel/util/SecurityUtilsTest.java
+++ b/core/camel-util/src/test/java/org/apache/camel/util/SecurityUtilsTest.java
@@ -78,8 +78,8 @@ class SecurityUtilsTest {
 
     @Test
     void testGetSecurityOption() {
-        // full property key — should extract last segment
-        SecurityUtils.SecurityOption opt = 
SecurityUtils.getSecurityOption("camel.component.http.trustAllCertificates");
+        // full property key of a component that has this security option
+        SecurityUtils.SecurityOption opt = 
SecurityUtils.getSecurityOption("camel.component.aws2-s3.trustAllCertificates");
         assertNotNull(opt);
         assertEquals("insecure:ssl", opt.category());
         assertEquals("true", opt.insecureValue());
@@ -225,4 +225,63 @@ class SecurityUtilsTest {
 
         assertEquals(0, violations.size());
     }
+
+    @Test
+    void testComponentOptionMatchesOnlyItsOwnSecurityOption() {
+        // ssl is a security option of camel-hivemq only, and tls of 
camel-pinecone only
+        assertTrue(SecurityUtils.isInsecureValue("camel.component.hivemq.ssl", 
"false"));
+        
assertTrue(SecurityUtils.isInsecureValue("camel.component.pinecone.tls", 
"false"));
+
+        // other components with an option of the same name are not flagged
+        
assertNull(SecurityUtils.getSecurityOption("camel.component.netty.ssl"));
+        
assertNull(SecurityUtils.getSecurityOption("camel.component.netty-http.ssl"));
+        
assertNull(SecurityUtils.getSecurityOption("camel.component.clickhouse.ssl"));
+        
assertNull(SecurityUtils.getSecurityOption("camel.component.oaipmh.ssl"));
+        
assertNull(SecurityUtils.getSecurityOption("camel.component.kafka.tls"));
+        assertFalse(SecurityUtils.isInsecureValue("camel.component.netty.ssl", 
"false"));
+    }
+
+    @Test
+    void testComponentNameMatching() {
+        // the component name in the key may use dashes and any case
+        
assertNotNull(SecurityUtils.getSecurityOption("camel.component.atmosphere-websocket.allowJavaSerializedObject"));
+        
assertNotNull(SecurityUtils.getSecurityOption("camel.component.HiveMQ.ssl"));
+        
assertNotNull(SecurityUtils.getSecurityOption("camel.component.hivemq.configuration.ssl"));
+        // an alternative scheme of the component matches as well
+        
assertNotNull(SecurityUtils.getSecurityOption("camel.component.llm.sslEndpointAlgorithm"));
+        
assertNotNull(SecurityUtils.getSecurityOption("camel.component.openai.sslEndpointAlgorithm"));
+    }
+
+    @Test
+    void testDataFormatOptionMatchesOnlyItsOwnSecurityOption() {
+        
assertNotNull(SecurityUtils.getSecurityOption("camel.dataformat.avro.serializablePackages"));
+        
assertNull(SecurityUtils.getSecurityOption("camel.dataformat.jackson.serializablePackages"));
+    }
+
+    @Test
+    void testKeysWithoutComponentMatchByName() {
+        // keys that do not identify a component, data format or language 
match by the option name
+        assertNotNull(SecurityUtils.getSecurityOption("ssl"));
+        
assertNotNull(SecurityUtils.getSecurityOption("camel.ssl.trustAllCertificates"));
+        
assertNotNull(SecurityUtils.getSecurityOption("camel.beans.myClient.trustAllCertificates"));
+        // camel-main options have no owning component
+        
assertNotNull(SecurityUtils.getSecurityOption("camel.main.devConsoleEnabled"));
+    }
+
+    @Test
+    void testDetectViolationsOnlyForTheOwningComponent() {
+        Map<String, Object> properties = new LinkedHashMap<>();
+        properties.put("camel.component.netty.ssl", "false");
+        properties.put("camel.component.kafka.tls", "false");
+        properties.put("camel.component.hivemq.ssl", "false");
+
+        List<SecurityViolation> violations = SecurityUtils.detectViolations(
+                properties,
+                (k, v) -> false,
+                category -> "fail",
+                Set.of());
+
+        assertEquals(1, violations.size());
+        assertEquals("camel.component.hivemq.ssl", 
violations.get(0).propertyKey());
+    }
 }
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index e6748d3de541..e3b446578ccf 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -1295,11 +1295,24 @@ The `ssl` option is now marked `insecure:ssl`, so 
setting it to `false` in the c
 by the xref:security-policy.adoc[security policy] check: a warning by default, 
and a startup failure with
 the `prod` profile or `camel.security.insecureSslPolicy = fail`.
 
-The check matches a configuration property by its option name, not by its 
component. It therefore also
-applies when `ssl=false` is set on the other components that have an `ssl` 
option: `camel-clickhouse`,
-`camel-netty`, `camel-netty-http` and `camel-oaipmh`, for example 
`camel.component.netty.ssl = false` in
-`application.properties`. The `tls` option of `camel-pinecone` already worked 
this way for `tls=false`. To
-keep such a setting under a `fail` policy, list it in 
`camel.security.allowedProperties`.
+This applies only to `camel-hivemq`: setting `ssl=false` on the other 
components that have an `ssl` option
+(such as `camel.component.netty.ssl = false`) is not reported, see the 
`camel-main` section about the security
+policy check below. To keep `camel.component.hivemq.ssl = false` under a 
`fail` policy, list it in
+`camel.security.allowedProperties`.
+
+=== camel-main - security policy check matches component options by component
+
+The xref:security-policy.adoc[security policy] check matched an insecure 
option by its name only, so an
+option of one component that is marked insecure was also reported for any 
other component, data format or
+language with an option of the same name. For example `tls=false` was reported 
for every component because of
+the `tls` option of `camel-pinecone`.
+
+A property that configures a component, data format or language 
(`camel.component.<name>.<option>`,
+`camel.dataformat.<name>.<option>` and `camel.language.<name>.<option>`) is 
now only checked against the options
+of that component, data format or language. This means fewer properties are 
reported: a setting that was
+reported only because another component has an insecure option of the same 
name no longer triggers a warning,
+or a startup failure with the `prod` profile. Other properties, such as 
`camel.ssl.trustAllCertificates`, are
+still checked by the option name.
 
 === camel-hazelcast
 
diff --git a/docs/user-manual/modules/ROOT/pages/security-policy.adoc 
b/docs/user-manual/modules/ROOT/pages/security-policy.adoc
index f782fdc6d4c8..0b8de56f4fba 100644
--- a/docs/user-manual/modules/ROOT/pages/security-policy.adoc
+++ b/docs/user-manual/modules/ROOT/pages/security-policy.adoc
@@ -30,6 +30,13 @@ The framework checks four categories of security concerns:
 | `devConsoleEnabled=true`, `uploadEnabled=true`
 |===
 
+The insecure options are the component, data format and language options that 
are marked with a security
+category in their metadata. A property that configures a component, data 
format or language (such as
+`camel.component.netty.ssl`) is only checked against the options of that 
component, data format or language, so
+an option that merely has the same name as an insecure option of another 
component (such as the `ssl` option of
+camel-hivemq) is not flagged. Any other property (such as 
`camel.ssl.trustAllCertificates`) is checked by the
+option name.
+
 == Policy levels
 
 Each category can be set to one of three enforcement levels:
@@ -64,7 +71,7 @@ camel.security.insecureSerializationPolicy = fail
 camel.security.insecureDevPolicy = allow
 
 # Exempt specific properties from all checks
-camel.security.allowedProperties = camel.component.http.trustAllCertificates
+camel.security.allowedProperties = camel.component.aws2-s3.trustAllCertificates
 ----
 
 [cols="2,4,1"]
@@ -143,7 +150,7 @@ camel.main.profile = prod
 # Implicit: camel.security.policy = fail
 
 # Allow one specific exception where self-signed certs are needed
-camel.security.allowedProperties = camel.component.https.trustAllCertificates
+camel.security.allowedProperties = camel.component.aws2-s3.trustAllCertificates
 ----
 
 With this configuration, the application will refuse to start if any 
plain-text secret, insecure SSL
diff --git 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateSensitizeHelper.java
 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateSensitizeHelper.java
index c8074a0fd34d..a6d9679d26b4 100644
--- 
a/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateSensitizeHelper.java
+++ 
b/tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/UpdateSensitizeHelper.java
@@ -60,6 +60,8 @@ public class UpdateSensitizeHelper extends 
AbstractGeneratorMojo {
     private static final String PATTERN_END_TOKEN = "// SENSITIVE-PATTERN: 
END";
     private static final String SECURITY_START_TOKEN = "// SECURITY-OPTIONS: 
START";
     private static final String SECURITY_END_TOKEN = "// SECURITY-OPTIONS: 
END";
+    private static final String OWNERS_START_TOKEN = "// 
SECURITY-OPTION-OWNERS: START";
+    private static final String OWNERS_END_TOKEN = "// SECURITY-OPTION-OWNERS: 
END";
 
     private static final String SECRET = "secret";
     private static final String INSECURE_DEV = "insecure:dev";
@@ -129,6 +131,8 @@ public class UpdateSensitizeHelper extends 
AbstractGeneratorMojo {
         Set<String> secrets = new TreeSet<>();
         // key -> [category, insecureValue]
         Map<String, String[]> securityOptions = new TreeMap<>();
+        // key -> the components, data formats and languages that declare the 
option (such as component:netty)
+        Map<String, Set<String>> securityOptionOwners = new TreeMap<>();
 
         for (Path file : jsonFiles) {
             final String name = PackageHelper.asName(file);
@@ -154,22 +158,26 @@ public class UpdateSensitizeHelper extends 
AbstractGeneratorMojo {
 
                 if (isComponent) {
                     ComponentModel cm = 
JsonMapper.generateComponentModel(json);
+                    Set<String> owners = owners("component:", cm.getScheme(), 
cm.getAlternativeSchemes());
                     cm.getComponentOptions().forEach(o -> {
                         collectSecretOption(o, secrets);
-                        collectSecurityOption(o, securityOptions);
+                        collectSecurityOption(o, securityOptions, 
securityOptionOwners, owners);
                     });
-                    cm.getEndpointOptions().forEach(o -> 
collectSecurityOption(o, securityOptions));
+                    cm.getEndpointOptions()
+                            .forEach(o -> collectSecurityOption(o, 
securityOptions, securityOptionOwners, owners));
                 } else if (isDataFormat) {
                     DataFormatModel dm = 
JsonMapper.generateDataFormatModel(json);
+                    Set<String> owners = owners("dataformat:", dm.getName(), 
null);
                     dm.getOptions().forEach(o -> {
                         collectSecretOption(o, secrets);
-                        collectSecurityOption(o, securityOptions);
+                        collectSecurityOption(o, securityOptions, 
securityOptionOwners, owners);
                     });
                 } else if (isLanguage) {
                     LanguageModel lm = JsonMapper.generateLanguageModel(json);
+                    Set<String> owners = owners("language:", lm.getName(), 
null);
                     lm.getOptions().forEach(o -> {
                         collectSecretOption(o, secrets);
-                        collectSecurityOption(o, securityOptions);
+                        collectSecurityOption(o, securityOptions, 
securityOptionOwners, owners);
                     });
                 }
             } catch (Exception e) {
@@ -212,6 +220,7 @@ public class UpdateSensitizeHelper extends 
AbstractGeneratorMojo {
                           + " distinct insecure security options across all 
the Camel components/dataformats/languages");
             try {
                 boolean updated = updateSecurityUtils(camelDir, 
securityOptions);
+                updated |= updateSecurityOptionOwners(camelDir, 
securityOptionOwners);
                 if (updated) {
                     getLog().info("Updated 
camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java file");
                 } else {
@@ -314,8 +323,30 @@ public class UpdateSensitizeHelper extends 
AbstractGeneratorMojo {
         }
     }
 
+    /**
+     * The owner names (such as component:nettyhttp) of a component, data 
format or language, in the same form as
+     * SecurityUtils computes them from a configuration key (lower case and 
without dashes)
+     */
+    private static Set<String> owners(String kind, String name, String 
alternativeNames) {
+        Set<String> answer = new TreeSet<>();
+        answer.add(kind + normalizeOwnerName(name));
+        if (!Strings.isNullOrEmpty(alternativeNames)) {
+            for (String alternative : alternativeNames.split(",")) {
+                if (!alternative.isBlank()) {
+                    answer.add(kind + normalizeOwnerName(alternative.trim()));
+                }
+            }
+        }
+        return answer;
+    }
+
+    private static String normalizeOwnerName(String name) {
+        return name.toLowerCase(Locale.ENGLISH).replace("-", "");
+    }
+
     private static void collectSecurityOption(
-            BaseOptionModel o, Map<String, String[]> securityOptions) {
+            BaseOptionModel o, Map<String, String[]> securityOptions, 
Map<String, Set<String>> securityOptionOwners,
+            Set<String> owners) {
         String security = o.getSecurity();
         if (!Strings.isNullOrEmpty(security) && !SECRET.equals(security)) {
             // only collect insecure:* categories; secrets are handled by 
SensitiveUtils
@@ -328,6 +359,7 @@ public class UpdateSensitizeHelper extends 
AbstractGeneratorMojo {
             }
             // only add if not already present (first wins)
             securityOptions.putIfAbsent(key, new String[] { security, 
insecureValue });
+            securityOptionOwners.computeIfAbsent(key, k -> new 
TreeSet<>()).addAll(owners);
         }
     }
 
@@ -367,4 +399,38 @@ public class UpdateSensitizeHelper extends 
AbstractGeneratorMojo {
         return false;
     }
 
+    private boolean updateSecurityOptionOwners(File camelDir, Map<String, 
Set<String>> securityOptionOwners)
+            throws Exception {
+        File java = new File(camelDir, 
"src/main/java/org/apache/camel/util/SecurityUtils.java");
+        String text = PackageHelper.loadText(java);
+        String spaces8 = "        ";
+        String spaces16 = "                ";
+
+        // one owner per line (the block is not formatted, as an option can 
have many owners)
+        StringJoiner sb = new StringJoiner("\n");
+        for (Map.Entry<String, Set<String>> entry : 
securityOptionOwners.entrySet()) {
+            StringJoiner owners = new StringJoiner(",\n");
+            entry.getValue().forEach(o -> owners.add(spaces16 + "\"" + o + 
"\""));
+            sb.add(spaces8 + "owners.put(\"" + entry.getKey() + "\", 
Set.of(\n" + owners + "));");
+        }
+        String changed = sb.toString();
+
+        String existing = Strings.between(text, OWNERS_START_TOKEN, 
OWNERS_END_TOKEN);
+        if (existing != null) {
+            existing = existing.trim();
+            changed = changed.trim();
+            if (existing.equals(changed)) {
+                return false;
+            } else {
+                String before = Strings.before(text, OWNERS_START_TOKEN);
+                String after = Strings.after(text, OWNERS_END_TOKEN);
+                text = before + OWNERS_START_TOKEN + "\n" + spaces8 + changed 
+ "\n" + spaces8 + OWNERS_END_TOKEN + after;
+                PackageHelper.writeText(java, text);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
 }

Reply via email to