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;
+ }
+
}