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 8ad18065b44d CAMEL-26916: camel-yaml-dsl-validator - an insecure
opt-in that can be set on the component keeps its default when reading the body
(#29808)
8ad18065b44d is described below
commit 8ad18065b44d2490757fef7c9a013b69209c0b46
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Nov 13 11:02:37 2026 +0100
CAMEL-26916: camel-yaml-dsl-validator - an insecure opt-in that can be set
on the component keeps its default when reading the body (#29808)
The YAML validator treated a body type that depends on an option the route
does not set, but that can be set on the component, as not known, and said
nothing. For the insecure opt-ins (security insecure:ssl,
insecure:serialization or insecure:dev) that made it go quiet where it should
not: pulsar (allowJavaSerializedObject), jms/activemq/activemq6/amqp
(transferExchange, objectMessageEnabled), sjms/sjms2 (objectMessageEnabled),
mina (transferExchange, objectCodecPattern), netty (trans [...]
Such an option is off unless turned on, and one turned on for the whole
component is reported by the security policy at startup, so
CatalogBodyTypes.componentSide() no longer counts it as "can be set elsewhere":
it keeps its default unless the route sets it.
- BodyTypeResolver documents it on the hidden parameter, and
body-types.adoc states the rule.
- TextBodyFlowTest expects the pulsar finding again (dropped by #29807),
and testTheBodyOfAnInsecureOptIn checks that a route leaving
allowJavaSerializedObject alone is reported and one turning it on is not.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Signed-off-by: Claus Ibsen <[email protected]>
---
.../user-manual/modules/ROOT/pages/body-types.adoc | 5 ++-
.../camel/dsl/yaml/validator/CatalogBodyTypes.java | 13 ++++++-
.../camel/dsl/yaml/validator/TextBodyFlowTest.java | 43 +++++++++++++++++++---
.../camel/tooling/model/BodyTypeResolver.java | 3 +-
4 files changed, 55 insertions(+), 9 deletions(-)
diff --git a/docs/user-manual/modules/ROOT/pages/body-types.adoc
b/docs/user-manual/modules/ROOT/pages/body-types.adoc
index 7a6eb96450d2..291bef09072b 100644
--- a/docs/user-manual/modules/ROOT/pages/body-types.adoc
+++ b/docs/user-manual/modules/ROOT/pages/body-types.adoc
@@ -432,7 +432,10 @@ option, but it can be set elsewhere: not known.
the model the step uses (the language of its expression, its data format, its
endpoint, or the data type transformer
of its `toType`), with the options of that model. `steps` is the body the
steps of the EIP end with.
. *An option the side names whose value is not known before the route runs* (a
`+{{placeholder}}+` or a `+${expression}+`),
-or an option the step does not set but that can be set elsewhere, such as on
the component: not known.
+or an option the step does not set but that can be set elsewhere, such as on
the component: not known. An insecure
+opt-in (its `security` is `insecure:ssl`, `insecure:serialization` or
`insecure:dev`, such as `allowJavaSerializedObject`)
+is not such an option: it keeps its default when the step does not set it, as
one turned on for the whole component is
+reported by the xref:security-policy.adoc[security policy] at startup.
. *An option of `dependsOn` the step sets:* the `byOption` entry for that
value says the body. When it has a
`javaTypeOption` the step sets, the body is an object of that class. Without
such an entry: not known.
. *The `javaTypeOption` of the side, when the step sets it:* an object of the
class it names, or null. It wins over the
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/CatalogBodyTypes.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/CatalogBodyTypes.java
index 6f6bcdf0275a..d4c5b7cc6a23 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/CatalogBodyTypes.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/CatalogBodyTypes.java
@@ -255,7 +255,11 @@ final class CatalogBodyTypes {
}
Set<String> configurable = new HashSet<>();
for (BaseOptionModel option : model.getComponentOptions()) {
- configurable.add(option.getName());
+ // an insecure option (allowJavaSerializedObject,
transferExchange) is an opt-in: it keeps its default when
+ // the step does not set it, as one turned on for the whole
component is reported by the security policy
+ if (!isInsecure(option)) {
+ configurable.add(option.getName());
+ }
}
Map<String, String> set = values(node != null && node.isObject() ?
node.get("parameters") : null);
String text = node != null && node.isString() ? node.asString()
@@ -299,6 +303,13 @@ final class CatalogBodyTypes {
}
}
+ /**
+ * Whether the option is one of the insecure opt-ins: security
insecure:ssl, insecure:serialization or insecure:dev.
+ */
+ private static boolean isInsecure(BaseOptionModel option) {
+ return option.getSecurity() != null &&
option.getSecurity().startsWith("insecure:");
+ }
+
/**
* What a side says for the options a step sets, by the rule the tools
share (BodyTypeResolver of the tooling
* model); null when it is not known for the step.
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java
index 286374536237..13d37bd6b07f 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java
@@ -1430,10 +1430,10 @@ public class TextBodyFlowTest {
@Test
public void testTheBodyOfMessaging() {
- // the paho consumer gives bytes, so a field read needs an unmarshal;
smtp sends what converts to text, so
- // parsed data must be marshalled first, and so must the reply of a
nats consumer that answers (InOut); the
- // converter of spring-rabbitmq and the allowJavaSerializedObject
option of pulsar may be set on the component,
- // and an imap mail may be text, a stream or a multipart, which say
nothing
+ // the paho consumer gives bytes, so a field read needs an unmarshal;
pulsar and smtp send what converts to
+ // bytes or text, so parsed data must be marshalled first, and so must
the reply of a nats consumer that answers
+ // (InOut); the converter of spring-rabbitmq may be set on the
component, and an imap mail may be text, a stream
+ // or a multipart, which say nothing
String yaml = """
- route:
id: mqtt
@@ -1502,10 +1502,10 @@ public class TextBodyFlowTest {
name: id
simple: ${body[id]}
""";
- assertThat(lines(yaml)).hasSize(3).anySatisfy(m -> assertThat(m)
+ assertThat(lines(yaml)).hasSize(4).anySatisfy(m -> assertThat(m)
.contains("route mqtt: simple reads a field of the body
(${body[id]})")
.contains("the paho consumer gives it as bytes"))
- .noneSatisfy(m -> assertThat(m).contains("route publish:"))
+ .anySatisfy(m -> assertThat(m).contains("to: pulsar needs the
body as text, bytes, a stream or a file"))
.anySatisfy(m -> assertThat(m)
.contains("route service:")
.contains("the body at the end of the route is the
reply to the nats request"))
@@ -1513,6 +1513,37 @@ public class TextBodyFlowTest {
.contains("route mail: to: smtp needs the body as
text, bytes, a stream or a file"));
}
+ @Test
+ public void testTheBodyOfAnInsecureOptIn() {
+ // allowJavaSerializedObject of pulsar can be set on the component,
but it is an insecure opt-in: it keeps its
+ // default (off) when the route does not set it, so parsed data must
be marshalled first; a route that turns it
+ // on sends any body, which says nothing
+ String yaml = """
+ - route:
+ id: publish
+ from:
+ uri: timer:tick
+ steps:
+ - setBody:
+ constant: '{"id": 1}'
+ - unmarshal:
+ json: {}
+ - to: pulsar:persistent://public/default/orders
+ - route:
+ id: legacy
+ from:
+ uri: timer:tick
+ steps:
+ - setBody:
+ constant: '{"id": 1}'
+ - unmarshal:
+ json: {}
+ - to:
pulsar:persistent://public/default/orders?allowJavaSerializedObject=true
+ """;
+ assertThat(lines(yaml)).hasSize(1).first().asString()
+ .contains("route publish: to: pulsar needs the body as text,
bytes, a stream or a file");
+ }
+
@Test
public void testTheBodyOfMoreComponents() {
// ssh runs the body as a command and gives its output as a stream,
and so does its consumer; spring-redis keeps
diff --git
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeResolver.java
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeResolver.java
index 0a45b58ff7ec..34cef6604c23 100644
---
a/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeResolver.java
+++
b/tooling/camel-tooling-model/src/main/java/org/apache/camel/tooling/model/BodyTypeResolver.java
@@ -73,7 +73,8 @@ public final class BodyTypeResolver {
* @param options the options the step sets, by name: the endpoint uri,
the parameters or the options of a data
* format
* @param hidden the options that can be set elsewhere when the step
does not set them, such as the options of the
- * component
+ * component; not the insecure opt-ins
(allowJavaSerializedObject), which keep their default unless
+ * the step sets them
* @return the body, or null when it is not known for the step
*/
public static Resolved resolve(BodyTypeModel side, Map<String, String>
options, Set<String> hidden) {