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

davsclaus pushed a commit to branch fix/CAMEL-25134
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 3792df7e7b2af20d0dda4d9c49ce93856436a92c
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Sep 29 11:34:07 2026 +0200

    CAMEL-25134: camel-main - Security policy check matches component options 
by component
    
    The security policy check matched an insecure option by its name only, so 
an insecure option of one component
    was also reported for every component, data format or language with an 
option of the same name (such as tls=false
    because of camel-pinecone, and ssl=false because of camel-hivemq).
    
    The build tools now also generate which components, data formats and 
languages declare each security option, and
    a camel.component.<name>.<option> key (and the dataformat and language 
forms) is only checked against the options
    of that component, data format or language. Other keys are still checked by 
the option name.
    
    Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
    Signed-off-by: Claus Ibsen <[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 04d6027f2ded..76c7cf5a20c2 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
@@ -1236,11 +1236,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