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

oscerd 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 563609bc1384 CAMEL-24732: camel-opa - withhold credential headers from 
the includeHeaders wildcard (#26430)
563609bc1384 is described below

commit 563609bc1384098420f6a46ec127f5c5c8b8f636
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Sep 15 12:27:53 2026 +0200

    CAMEL-24732: camel-opa - withhold credential headers from the 
includeHeaders wildcard (#26430)
    
    Do not forward credential/authorization headers to the OPA decision request 
when includeHeaders uses a wildcard.
    
    Co-Authored-By: Claude Opus 4.8 <[email protected]>
---
 .../org/apache/camel/catalog/components/opa.json   |  4 +--
 .../apache/camel/catalog/docs/opa-component.adoc   | 17 +++++++++---
 .../org/apache/camel/component/opa/opa.json        |  4 +--
 .../camel-opa/src/main/docs/opa-component.adoc     | 17 +++++++++---
 .../camel/component/opa/OpaConfiguration.java      |  6 ++--
 .../camel/component/opa/OpaPolicyEvaluator.java    | 26 +++++++++++++++++-
 .../camel/component/opa/OpaInputDocumentTest.java  | 32 ++++++++++++++++++++++
 .../component/dsl/OpaComponentBuilderFactory.java  | 10 +++++--
 .../endpoint/dsl/OpaEndpointBuilderFactory.java    | 10 +++++--
 9 files changed, 105 insertions(+), 21 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opa.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opa.json
index 23cf528871da..02e9044ba20b 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opa.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opa.json
@@ -27,7 +27,7 @@
     "allowKey": { "index": 0, "kind": "property", "displayName": "Allow Key", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "allow", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "The key to read the allow\/deny verdict from 
when the policy returns an object rather than a plain b [...]
     "configuration": { "index": 1, "kind": "property", "displayName": 
"Configuration", "group": "producer", "label": "", "required": false, "type": 
"object", "javaType": "org.apache.camel.component.opa.OpaConfiguration", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
component configuration." },
     "includeBody": { "index": 2, "kind": "property", "displayName": "Include 
Body", "group": "producer", "label": "", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Whether to send the message body to OPA as 
part of the input document. Disabled by default: bodies can b [...]
-    "includeHeaders": { "index": 3, "kind": "property", "displayName": 
"Include Headers", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "*", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of message header names 
to send to OPA in the input document. The defau [...]
+    "includeHeaders": { "index": 3, "kind": "property", "displayName": 
"Include Headers", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "*", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of message header names 
to send to OPA in the input document. The defau [...]
     "includeProperties": { "index": 4, "kind": "property", "displayName": 
"Include Properties", "group": "producer", "label": "", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of exchange property 
names to send to OPA in the input document, or {code } for all of [...]
     "lazyStartProducer": { "index": 5, "kind": "property", "displayName": 
"Lazy Start Producer", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether the producer should be started lazy (on the first message). By 
starting lazy you can use this to allow CamelContext and routes to startup in 
situations where a producer may otherwise fail [...]
     "serverUrl": { "index": 6, "kind": "property", "displayName": "Server 
Url", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "http:\/\/localhost:8181", 
"configurationClass": "org.apache.camel.component.opa.OpaConfiguration", 
"configurationField": "configuration", "description": "The base URL of the OPA 
server, without the {code \/v1\/data} suffix. The defaul [...]
@@ -47,7 +47,7 @@
     "policyPath": { "index": 0, "kind": "path", "displayName": "Policy Path", 
"group": "producer", "label": "", "required": true, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Path of the Rego rule head 
to evaluate, relative to the OPA data document. For a rule named allow in a 
policy declaring package authz.orders, this is authz\/orders\/allow. The path 
is taken from the endpoint only: i [...]
     "allowKey": { "index": 1, "kind": "parameter", "displayName": "Allow Key", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "allow", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "The key to read the allow\/deny verdict from 
when the policy returns an object rather than a plain  [...]
     "includeBody": { "index": 2, "kind": "parameter", "displayName": "Include 
Body", "group": "producer", "label": "", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Whether to send the message body to OPA as 
part of the input document. Disabled by default: bodies can  [...]
-    "includeHeaders": { "index": 3, "kind": "parameter", "displayName": 
"Include Headers", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "*", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of message header names 
to send to OPA in the input document. The defa [...]
+    "includeHeaders": { "index": 3, "kind": "parameter", "displayName": 
"Include Headers", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "*", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of message header names 
to send to OPA in the input document. The defa [...]
     "includeProperties": { "index": 4, "kind": "parameter", "displayName": 
"Include Properties", "group": "producer", "label": "", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of exchange property 
names to send to OPA in the input document, or {code } for all o [...]
     "serverUrl": { "index": 5, "kind": "parameter", "displayName": "Server 
Url", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "http:\/\/localhost:8181", 
"configurationClass": "org.apache.camel.component.opa.OpaConfiguration", 
"configurationField": "configuration", "description": "The base URL of the OPA 
server, without the {code \/v1\/data} suffix. The defau [...]
     "lazyStartProducer": { "index": 6, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produc [...]
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opa-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opa-component.adoc
index b20e097915a9..eaf35efdf89d 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opa-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/opa-component.adoc
@@ -120,10 +120,19 @@ 
https://github.com/apache/camel/blob/main/components/camel-opa/src/test/resource
 which is a worked example of both a plain boolean rule and a decision object 
with deny reasons; `OpaIT`
 alongside it shows the matching routes end to end.
 
-`headers` carries every message header by default. Set `includeHeaders` to a 
comma-separated list of names
-(matched case-insensitively) when the policy only needs a few of them. `body` 
is *not* sent unless `includeBody`
-is enabled: bodies can be large or streaming, and most authorization decisions 
do not need them. When you do
-enable it on a streaming body, enable stream caching so the body is still 
readable by the rest of the route.
+`headers` carries every message header by default, with one exception: the 
headers that carry a caller credential
+verbatim — `Authorization`, `Proxy-Authorization`, `Cookie` and `Set-Cookie` — 
are *withheld* from the wildcard.
+OPA's decision logging ships the whole `input` document, frequently to a 
remote collector, so the wildcard should
+not quietly export credentials off the box. A policy that genuinely needs one 
can still have it by naming the
+header: `includeHeaders=Authorization,user` sends it. Matching is 
case-insensitive, so `authorization` is withheld
+too. Set `includeHeaders` to a comma-separated list of names when the policy 
only needs a few of them.
+
+Prefer `includeProperties` for identity: a token that an earlier step has 
already *verified* belongs there, as
+<<authorizing-an-identity>> describes, rather than handing the raw credential 
to the policy to re-check.
+
+`body` is *not* sent unless `includeBody` is enabled: bodies can be large or 
streaming, and most authorization
+decisions do not need them. When you do enable it on a streaming body, enable 
stream caching so the body is still
+readable by the rest of the route.
 
 `properties` is empty unless you ask for it — see <<authorizing-an-identity>> 
below.
 
diff --git 
a/components/camel-opa/src/generated/resources/META-INF/org/apache/camel/component/opa/opa.json
 
b/components/camel-opa/src/generated/resources/META-INF/org/apache/camel/component/opa/opa.json
index 23cf528871da..02e9044ba20b 100644
--- 
a/components/camel-opa/src/generated/resources/META-INF/org/apache/camel/component/opa/opa.json
+++ 
b/components/camel-opa/src/generated/resources/META-INF/org/apache/camel/component/opa/opa.json
@@ -27,7 +27,7 @@
     "allowKey": { "index": 0, "kind": "property", "displayName": "Allow Key", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "allow", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "The key to read the allow\/deny verdict from 
when the policy returns an object rather than a plain b [...]
     "configuration": { "index": 1, "kind": "property", "displayName": 
"Configuration", "group": "producer", "label": "", "required": false, "type": 
"object", "javaType": "org.apache.camel.component.opa.OpaConfiguration", 
"deprecated": false, "autowired": false, "secret": false, "description": "The 
component configuration." },
     "includeBody": { "index": 2, "kind": "property", "displayName": "Include 
Body", "group": "producer", "label": "", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Whether to send the message body to OPA as 
part of the input document. Disabled by default: bodies can b [...]
-    "includeHeaders": { "index": 3, "kind": "property", "displayName": 
"Include Headers", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "*", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of message header names 
to send to OPA in the input document. The defau [...]
+    "includeHeaders": { "index": 3, "kind": "property", "displayName": 
"Include Headers", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "*", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of message header names 
to send to OPA in the input document. The defau [...]
     "includeProperties": { "index": 4, "kind": "property", "displayName": 
"Include Properties", "group": "producer", "label": "", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of exchange property 
names to send to OPA in the input document, or {code } for all of [...]
     "lazyStartProducer": { "index": 5, "kind": "property", "displayName": 
"Lazy Start Producer", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether the producer should be started lazy (on the first message). By 
starting lazy you can use this to allow CamelContext and routes to startup in 
situations where a producer may otherwise fail [...]
     "serverUrl": { "index": 6, "kind": "property", "displayName": "Server 
Url", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "http:\/\/localhost:8181", 
"configurationClass": "org.apache.camel.component.opa.OpaConfiguration", 
"configurationField": "configuration", "description": "The base URL of the OPA 
server, without the {code \/v1\/data} suffix. The defaul [...]
@@ -47,7 +47,7 @@
     "policyPath": { "index": 0, "kind": "path", "displayName": "Policy Path", 
"group": "producer", "label": "", "required": true, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Path of the Rego rule head 
to evaluate, relative to the OPA data document. For a rule named allow in a 
policy declaring package authz.orders, this is authz\/orders\/allow. The path 
is taken from the endpoint only: i [...]
     "allowKey": { "index": 1, "kind": "parameter", "displayName": "Allow Key", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "allow", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "The key to read the allow\/deny verdict from 
when the policy returns an object rather than a plain  [...]
     "includeBody": { "index": 2, "kind": "parameter", "displayName": "Include 
Body", "group": "producer", "label": "", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Whether to send the message body to OPA as 
part of the input document. Disabled by default: bodies can  [...]
-    "includeHeaders": { "index": 3, "kind": "parameter", "displayName": 
"Include Headers", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "*", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of message header names 
to send to OPA in the input document. The defa [...]
+    "includeHeaders": { "index": 3, "kind": "parameter", "displayName": 
"Include Headers", "group": "producer", "label": "", "required": false, "type": 
"string", "javaType": "java.lang.String", "deprecated": false, "autowired": 
false, "secret": false, "defaultValue": "*", "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of message header names 
to send to OPA in the input document. The defa [...]
     "includeProperties": { "index": 4, "kind": "parameter", "displayName": 
"Include Properties", "group": "producer", "label": "", "required": false, 
"type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "configurationClass": 
"org.apache.camel.component.opa.OpaConfiguration", "configurationField": 
"configuration", "description": "Comma-separated list of exchange property 
names to send to OPA in the input document, or {code } for all o [...]
     "serverUrl": { "index": 5, "kind": "parameter", "displayName": "Server 
Url", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "http:\/\/localhost:8181", 
"configurationClass": "org.apache.camel.component.opa.OpaConfiguration", 
"configurationField": "configuration", "description": "The base URL of the OPA 
server, without the {code \/v1\/data} suffix. The defau [...]
     "lazyStartProducer": { "index": 6, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produc [...]
diff --git a/components/camel-opa/src/main/docs/opa-component.adoc 
b/components/camel-opa/src/main/docs/opa-component.adoc
index b20e097915a9..eaf35efdf89d 100644
--- a/components/camel-opa/src/main/docs/opa-component.adoc
+++ b/components/camel-opa/src/main/docs/opa-component.adoc
@@ -120,10 +120,19 @@ 
https://github.com/apache/camel/blob/main/components/camel-opa/src/test/resource
 which is a worked example of both a plain boolean rule and a decision object 
with deny reasons; `OpaIT`
 alongside it shows the matching routes end to end.
 
-`headers` carries every message header by default. Set `includeHeaders` to a 
comma-separated list of names
-(matched case-insensitively) when the policy only needs a few of them. `body` 
is *not* sent unless `includeBody`
-is enabled: bodies can be large or streaming, and most authorization decisions 
do not need them. When you do
-enable it on a streaming body, enable stream caching so the body is still 
readable by the rest of the route.
+`headers` carries every message header by default, with one exception: the 
headers that carry a caller credential
+verbatim — `Authorization`, `Proxy-Authorization`, `Cookie` and `Set-Cookie` — 
are *withheld* from the wildcard.
+OPA's decision logging ships the whole `input` document, frequently to a 
remote collector, so the wildcard should
+not quietly export credentials off the box. A policy that genuinely needs one 
can still have it by naming the
+header: `includeHeaders=Authorization,user` sends it. Matching is 
case-insensitive, so `authorization` is withheld
+too. Set `includeHeaders` to a comma-separated list of names when the policy 
only needs a few of them.
+
+Prefer `includeProperties` for identity: a token that an earlier step has 
already *verified* belongs there, as
+<<authorizing-an-identity>> describes, rather than handing the raw credential 
to the policy to re-check.
+
+`body` is *not* sent unless `includeBody` is enabled: bodies can be large or 
streaming, and most authorization
+decisions do not need them. When you do enable it on a streaming body, enable 
stream caching so the body is still
+readable by the rest of the route.
 
 `properties` is empty unless you ask for it — see <<authorizing-an-identity>> 
below.
 
diff --git 
a/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaConfiguration.java
 
b/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaConfiguration.java
index bdb98ddda933..d1389c4bd7c1 100644
--- 
a/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaConfiguration.java
+++ 
b/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaConfiguration.java
@@ -77,8 +77,10 @@ public class OpaConfiguration implements Cloneable {
 
     /**
      * Comma-separated list of message header names to send to OPA in the 
input document. The default of {@code *} sends
-     * every header. Narrow it when the policy only needs a few headers, or 
when the message carries headers that should
-     * not leave the JVM.
+     * every header <em>except</em> those that carry a caller credential 
verbatim - {@code Authorization},
+     * {@code Proxy-Authorization}, {@code Cookie} and {@code Set-Cookie} - 
which are withheld because OPA's decision
+     * logging ships the whole input document, often off the box. A policy 
that genuinely needs one can still have it by
+     * naming the header here. Narrow the list when the policy only needs a 
few headers.
      */
     public String getIncludeHeaders() {
         return includeHeaders;
diff --git 
a/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaPolicyEvaluator.java
 
b/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaPolicyEvaluator.java
index 332ce66dd8ba..79dd9e6ce72c 100644
--- 
a/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaPolicyEvaluator.java
+++ 
b/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaPolicyEvaluator.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.component.opa;
 
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -40,6 +41,13 @@ public class OpaPolicyEvaluator {
 
     private static final String ALL_NAMES = "*";
 
+    /**
+     * Headers that carry a caller credential verbatim. They are withheld when 
{@code includeHeaders} is the wildcard,
+     * because OPA's decision logging ships the whole input document - often 
off the box - and a policy that needs a
+     * credential should say so by naming the header. Listing one explicitly 
still sends it.
+     */
+    private static final Set<String> CREDENTIAL_HEADERS = credentialHeaders();
+
     private final OPAClient client;
     private final String policyPath;
     private final String allowKey;
@@ -115,7 +123,7 @@ public class OpaPolicyEvaluator {
             String name = entry.getKey();
             // never feed our own decision headers back in: a policy must not 
be able to read a verdict
             // that an inbound message claimed for itself
-            if (isDecisionHeader(name) || !isIncluded(includedHeaders, name)) {
+            if (isDecisionHeader(name) || !isIncluded(includedHeaders, name) 
|| isWithheldCredential(name)) {
                 continue;
             }
             Object value = toJsonSafe(exchange, entry.getValue());
@@ -185,6 +193,22 @@ public class OpaPolicyEvaluator {
         return filter == null || !filter.isEmpty();
     }
 
+    /**
+     * A credential header is only sent when the configuration names it, never 
through the wildcard.
+     */
+    private boolean isWithheldCredential(String name) {
+        return includedHeaders == null && CREDENTIAL_HEADERS.contains(name);
+    }
+
+    private static Set<String> credentialHeaders() {
+        Set<String> names = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
+        names.add("Authorization");
+        names.add("Proxy-Authorization");
+        names.add("Cookie");
+        names.add("Set-Cookie");
+        return Collections.unmodifiableSet(names);
+    }
+
     private static boolean isDecisionHeader(String name) {
         return OpaConstants.DECISION_ALLOW.equalsIgnoreCase(name)
                 || OpaConstants.DECISION.equalsIgnoreCase(name)
diff --git 
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaInputDocumentTest.java
 
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaInputDocumentTest.java
index c39ccb5d0693..5b2af0a28676 100644
--- 
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaInputDocumentTest.java
+++ 
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaInputDocumentTest.java
@@ -90,6 +90,38 @@ class OpaInputDocumentTest extends CamelTestSupport {
         assertThat(input).containsEntry("body", "the payload");
     }
 
+    @Test
+    void withholdsCredentialHeadersFromTheWildcard() throws Exception {
+        Map<String, Object> input = inputSentFor(ENDPOINT, e -> {
+            e.getMessage().setHeader("user", "alice");
+            e.getMessage().setHeader("Authorization", "Bearer s3cr3t");
+            e.getMessage().setHeader("Cookie", "session=s3cr3t");
+            e.getMessage().setHeader("Proxy-Authorization", "Basic s3cr3t");
+            e.getMessage().setHeader("Set-Cookie", "session=abc; Path=/");
+        });
+
+        assertThat(headersOf(input)).containsOnlyKeys("user");
+    }
+
+    @Test
+    void matchesWithheldCredentialHeadersCaseInsensitively() throws Exception {
+        Map<String, Object> input = inputSentFor(ENDPOINT,
+                e -> e.getMessage().setHeader("authorization", "Bearer 
s3cr3t"));
+
+        assertThat(headersOf(input)).doesNotContainKey("authorization");
+    }
+
+    @Test
+    void sendsACredentialHeaderWhenItIsNamedExplicitly() throws Exception {
+        Map<String, Object> input = inputSentFor(ENDPOINT + 
"&includeHeaders=Authorization,user", e -> {
+            e.getMessage().setHeader("user", "alice");
+            e.getMessage().setHeader("Authorization", "Bearer s3cr3t");
+        });
+
+        // naming it is the opt-in: a policy that must inspect the token can 
still get it
+        assertThat(headersOf(input)).containsEntry("Authorization", "Bearer 
s3cr3t");
+    }
+
     @Test
     void sendsOnlyTheListedHeadersWhenNarrowed() throws Exception {
         Map<String, Object> input = inputSentFor(ENDPOINT + 
"&includeHeaders=user,tenant", e -> {
diff --git 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/OpaComponentBuilderFactory.java
 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/OpaComponentBuilderFactory.java
index 366d08d78b22..df6c08ed7cd4 100644
--- 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/OpaComponentBuilderFactory.java
+++ 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/OpaComponentBuilderFactory.java
@@ -111,9 +111,13 @@ public interface OpaComponentBuilderFactory {
         
         /**
          * Comma-separated list of message header names to send to OPA in the
-         * input document. The default of {code } sends every header. Narrow it
-         * when the policy only needs a few headers, or when the message 
carries
-         * headers that should not leave the JVM.
+         * input document. The default of {code } sends every header except
+         * those that carry a caller credential verbatim - Authorization, {code
+         * Proxy-Authorization}, Cookie and {code Set-Cookie} - which are
+         * withheld because OPA's decision logging ships the whole input
+         * document, often off the box. A policy that genuinely needs one can
+         * still have it by naming the header here. Narrow the list when the
+         * policy only needs a few headers.
          * 
          * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
          * 
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpaEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpaEndpointBuilderFactory.java
index 8b3f6f3bc950..65c075956b32 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpaEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpaEndpointBuilderFactory.java
@@ -102,9 +102,13 @@ public interface OpaEndpointBuilderFactory {
         }
         /**
          * Comma-separated list of message header names to send to OPA in the
-         * input document. The default of {code } sends every header. Narrow it
-         * when the policy only needs a few headers, or when the message 
carries
-         * headers that should not leave the JVM.
+         * input document. The default of {code } sends every header except
+         * those that carry a caller credential verbatim - Authorization, {code
+         * Proxy-Authorization}, Cookie and {code Set-Cookie} - which are
+         * withheld because OPA's decision logging ships the whole input
+         * document, often off the box. A policy that genuinely needs one can
+         * still have it by naming the header here. Narrow the list when the
+         * policy only needs a few headers.
          * 
          * The option is a: <code>java.lang.String</code> type.
          * 

Reply via email to