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

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

commit b472c6854b8fe546a7a8bdcb9d10b9aed75f690d
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Mar 30 18:13:06 2026 +0200

    CAMEL-23273 - Camel-Jbang-mcp: Warn about sensitive data in POM content 
passed to migration tools
    
    Add PomSanitizer utility to detect and mask sensitive data (passwords,
    tokens, API keys, secrets) in POM content before processing. Strips
    <servers> and <distributionManagement> sections. Add sanitizePom boolean
    parameter (default: true) to camel_migration_analyze,
    camel_dependency_check, and camel_migration_wildfly_karaf tools. Update
    tool descriptions with sanitization guidance. Add 21 tests covering
    detection, masking, placeholder preservation, and tool integration.
    
    Signed-off-by: Andrea Cosentino <[email protected]>
---
 .../core/commands/mcp/DependencyCheckTools.java    |  35 ++-
 .../jbang/core/commands/mcp/MigrationTools.java    |  25 +-
 .../commands/mcp/MigrationWildflyKarafTools.java   |  27 +-
 .../dsl/jbang/core/commands/mcp/PomSanitizer.java  | 136 +++++++++
 .../commands/mcp/DependencyCheckToolsTest.java     |  89 ++++--
 .../jbang/core/commands/mcp/PomSanitizerTest.java  | 317 +++++++++++++++++++++
 6 files changed, 596 insertions(+), 33 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
index 2c964e5a9162..eb4201a80f15 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckTools.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.mcp;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import jakarta.enterprise.context.ApplicationScoped;
@@ -51,27 +52,51 @@ public class DependencyCheckTools {
                         + "detects outdated Camel dependencies compared to the 
latest catalog version, "
                         + "missing Maven dependencies for components used in 
routes, "
                         + "and version conflicts between the Camel BOM and 
explicit dependency overrides. "
-                        + "Returns actionable recommendations with corrected 
dependency snippets.")
+                        + "Returns actionable recommendations with corrected 
dependency snippets. "
+                        + "POM content is automatically sanitized to remove 
sensitive data (passwords, tokens, API keys, "
+                        + "repository credentials) unless sanitizePom is set 
to false.")
     public String camel_dependency_check(
-            @ToolArg(description = "The pom.xml file content") String 
pomContent,
+            @ToolArg(description = "The pom.xml file content. "
+                                   + "IMPORTANT: Avoid including sensitive 
data such as passwords, tokens, or API keys. "
+                                   + "Sensitive content is automatically 
detected and masked.") String pomContent,
             @ToolArg(description = "Route definitions (YAML, XML, or Java DSL) 
to check for missing component dependencies. "
                                    + "Multiple routes can be provided 
concatenated.") String routes,
             @ToolArg(description = "Runtime type: main, spring-boot, or 
quarkus (default: main)") String runtime,
             @ToolArg(description = "Camel version to use (e.g., 4.17.0). If 
not specified, uses the default catalog version.") String camelVersion,
             @ToolArg(description = "Platform BOM coordinates in GAV format 
(groupId:artifactId:version). "
-                                   + "When provided, overrides camelVersion.") 
String platformBom) {
+                                   + "When provided, overrides camelVersion.") 
String platformBom,
+            @ToolArg(description = "If true (default), automatically sanitize 
POM content by masking credentials "
+                                   + "and stripping <servers> and 
<distributionManagement> sections") Boolean sanitizePom) {
 
         if (pomContent == null || pomContent.isBlank()) {
             throw new ToolCallException("pomContent is required", null);
         }
 
         try {
+            // Sanitize POM content
+            String processedPom = pomContent;
+            List<String> sanitizationWarnings = new ArrayList<>();
+            if (sanitizePom == null || sanitizePom) {
+                PomSanitizer.SanitizationResult sr = 
PomSanitizer.sanitize(pomContent);
+                processedPom = sr.pomContent();
+                for (String pattern : sr.detectedPatterns()) {
+                    sanitizationWarnings.add("Sensitive data detected and 
masked: " + pattern);
+                }
+            }
+
             CamelCatalog catalog = catalogService.loadCatalog(runtime, 
camelVersion, platformBom);
 
-            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(pomContent);
+            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(processedPom);
 
             JsonObject result = new JsonObject();
 
+            // Add sanitization warnings if any
+            if (!sanitizationWarnings.isEmpty()) {
+                JsonArray sanitizationArr = new JsonArray();
+                sanitizationWarnings.forEach(sanitizationArr::add);
+                result.put("sanitizationWarnings", sanitizationArr);
+            }
+
             // Project info
             JsonObject projectInfo = new JsonObject();
             projectInfo.put("camelVersion", pom.camelVersion());
@@ -92,7 +117,7 @@ public class DependencyCheckTools {
             result.put("missingDependencies", missingDeps);
 
             // 3. Check for version conflicts (explicit overrides when BOM is 
present)
-            JsonArray conflicts = checkVersionConflicts(pomContent, pom);
+            JsonArray conflicts = checkVersionConflicts(processedPom, pom);
             result.put("versionConflicts", conflicts);
 
             // 4. Build recommendations
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
index f62bc97157e2..a3b0d22effe6 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationTools.java
@@ -49,21 +49,38 @@ public class MigrationTools {
      */
     @Tool(description = "Analyze a Camel project's pom.xml to detect the 
runtime type (main, spring-boot, quarkus, "
                         + "wildfly, karaf), Camel version, Java version, and 
Camel component dependencies. "
-                        + "This is the first step in a migration workflow.")
+                        + "This is the first step in a migration workflow. "
+                        + "POM content is automatically sanitized to remove 
sensitive data (passwords, tokens, API keys, "
+                        + "repository credentials) unless sanitizePom is set 
to false.")
     public ProjectAnalysisResult camel_migration_analyze(
-            @ToolArg(description = "The pom.xml file content") String 
pomContent) {
+            @ToolArg(description = "The pom.xml file content. "
+                                   + "IMPORTANT: Avoid including sensitive 
data such as passwords, tokens, or API keys. "
+                                   + "Sensitive content is automatically 
detected and masked.") String pomContent,
+            @ToolArg(description = "If true (default), automatically sanitize 
POM content by masking credentials "
+                                   + "and stripping <servers> and 
<distributionManagement> sections") Boolean sanitizePom) {
 
         if (pomContent == null || pomContent.isBlank()) {
             throw new ToolCallException("pomContent is required", null);
         }
 
         try {
-            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(pomContent);
+            // Sanitize POM content
+            String processedPom = pomContent;
+            List<String> sanitizationWarnings = new ArrayList<>();
+            if (sanitizePom == null || sanitizePom) {
+                PomSanitizer.SanitizationResult sr = 
PomSanitizer.sanitize(pomContent);
+                processedPom = sr.pomContent();
+                for (String pattern : sr.detectedPatterns()) {
+                    sanitizationWarnings.add("Sensitive data detected and 
masked: " + pattern);
+                }
+            }
+
+            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(processedPom);
 
             String runtimeType = pom.runtimeType();
             int majorVersion = pom.majorVersion();
 
-            List<String> warnings = new ArrayList<>();
+            List<String> warnings = new ArrayList<>(sanitizationWarnings);
             if (pom.camelVersion() == null) {
                 warnings.add("Could not detect Camel version from pom.xml. "
                              + "Check if the version is defined in a parent 
POM.");
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationWildflyKarafTools.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationWildflyKarafTools.java
index 17f938204dc2..1f83a74018cd 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationWildflyKarafTools.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationWildflyKarafTools.java
@@ -48,18 +48,35 @@ public class MigrationWildflyKarafTools {
                         + "IMPORTANT: When migrating to a different runtime 
(e.g., WildFly to Quarkus, Karaf to Spring Boot), "
                         + "you MUST use the archetype command returned by this 
tool to create a new project. "
                         + "Do NOT manually rewrite the pom.xml — always 
generate a new project with the archetype first, "
-                        + "then migrate routes and source files into it.")
+                        + "then migrate routes and source files into it. "
+                        + "POM content is automatically sanitized to remove 
sensitive data (passwords, tokens, API keys, "
+                        + "repository credentials) unless sanitizePom is set 
to false.")
     public WildflyKarafMigrationResult camel_migration_wildfly_karaf(
-            @ToolArg(description = "The pom.xml file content of the 
WildFly/Karaf project") String pomContent,
+            @ToolArg(description = "The pom.xml file content of the 
WildFly/Karaf project. "
+                                   + "IMPORTANT: Avoid including sensitive 
data such as passwords, tokens, or API keys. "
+                                   + "Sensitive content is automatically 
detected and masked.") String pomContent,
             @ToolArg(description = "Target runtime: spring-boot or quarkus 
(default: quarkus)") String targetRuntime,
-            @ToolArg(description = "Target Camel version (e.g., 4.18.0)") 
String targetVersion) {
+            @ToolArg(description = "Target Camel version (e.g., 4.18.0)") 
String targetVersion,
+            @ToolArg(description = "If true (default), automatically sanitize 
POM content by masking credentials "
+                                   + "and stripping <servers> and 
<distributionManagement> sections") Boolean sanitizePom) {
 
         if (pomContent == null || pomContent.isBlank()) {
             throw new ToolCallException("pomContent is required", null);
         }
 
         try {
-            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(pomContent);
+            // Sanitize POM content
+            String processedPom = pomContent;
+            List<String> sanitizationWarnings = new ArrayList<>();
+            if (sanitizePom == null || sanitizePom) {
+                PomSanitizer.SanitizationResult sr = 
PomSanitizer.sanitize(pomContent);
+                processedPom = sr.pomContent();
+                for (String pattern : sr.detectedPatterns()) {
+                    sanitizationWarnings.add("Sensitive data detected and 
masked: " + pattern);
+                }
+            }
+
+            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(processedPom);
 
             String sourceRuntime = pom.isWildfly() ? "wildfly" : pom.isKaraf() 
? "karaf" : "unknown";
             String resolvedTarget = targetRuntime != null && 
!targetRuntime.isBlank()
@@ -83,7 +100,7 @@ public class MigrationWildflyKarafTools {
                     .collect(Collectors.toList());
 
             // Warnings specific to the source runtime
-            List<String> warnings = new ArrayList<>();
+            List<String> warnings = new ArrayList<>(sanitizationWarnings);
             if ("karaf".equals(sourceRuntime)) {
                 warnings.add("Blueprint XML is not supported in Camel 3.x+. "
                              + "Routes must be converted to YAML DSL, XML DSL, 
or Java DSL.");
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PomSanitizer.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PomSanitizer.java
new file mode 100644
index 000000000000..766eb743b74e
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/main/java/org/apache/camel/dsl/jbang/core/commands/mcp/PomSanitizer.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.mcp;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.jboss.logging.Logger;
+
+/**
+ * Utility to detect and sanitize sensitive data in POM content before 
processing.
+ * <p>
+ * Scans for common credential patterns (passwords, tokens, API keys, secrets) 
and optionally strips or masks them. Also
+ * removes {@code <servers>} and {@code <distributionManagement>} sections 
which may contain private repository
+ * credentials and URLs.
+ */
+final class PomSanitizer {
+
+    private static final Logger LOG = Logger.getLogger(PomSanitizer.class);
+
+    private static final String SENSITIVE_KEYWORDS
+            = 
"password|passwd|token|apikey|api-key|api_key|secret|secretkey|secret-key|secret_key"
+              + 
"|accesskey|access-key|access_key|passphrase|privatekey|private-key|private_key|credentials";
+
+    /**
+     * Pattern matching XML elements whose tag names contain sensitive 
keywords. Captures: group(1) = element name,
+     * group(2) = element value.
+     */
+    private static final Pattern SENSITIVE_ELEMENT_PATTERN = Pattern.compile(
+            "<([a-zA-Z0-9_.:-]*(?:" + SENSITIVE_KEYWORDS + 
")[a-zA-Z0-9_.:-]*)>"
+                                                                             + 
"\\s*([^<]+?)\\s*"
+                                                                             + 
"</\\1>",
+            Pattern.CASE_INSENSITIVE);
+
+    /** Pattern matching {@code <servers>...</servers>} sections. */
+    private static final Pattern SERVERS_SECTION_PATTERN = Pattern.compile(
+            "<servers>.*?</servers>", Pattern.DOTALL);
+
+    /** Pattern matching {@code 
<distributionManagement>...</distributionManagement>} sections. */
+    private static final Pattern DIST_MGMT_SECTION_PATTERN = Pattern.compile(
+            "<distributionManagement>.*?</distributionManagement>", 
Pattern.DOTALL);
+
+    private PomSanitizer() {
+    }
+
+    /**
+     * Detect sensitive content patterns in POM content.
+     *
+     * @return list of descriptions of detected sensitive patterns
+     */
+    static List<String> detectSensitiveContent(String pomContent) {
+        Set<String> findings = new LinkedHashSet<>();
+
+        Matcher matcher = SENSITIVE_ELEMENT_PATTERN.matcher(pomContent);
+        while (matcher.find()) {
+            String value = matcher.group(2).trim();
+            // Property placeholders like ${my.password} are not actual secrets
+            if (!value.startsWith("${")) {
+                findings.add(matcher.group(1));
+            }
+        }
+
+        if (SERVERS_SECTION_PATTERN.matcher(pomContent).find()) {
+            findings.add("<servers> section (may contain repository 
credentials)");
+        }
+
+        if (DIST_MGMT_SECTION_PATTERN.matcher(pomContent).find()) {
+            findings.add("<distributionManagement> section (may contain 
private repository URLs)");
+        }
+
+        return new ArrayList<>(findings);
+    }
+
+    /**
+     * Sanitize POM content by masking sensitive element values and stripping 
credential sections ({@code <servers>} and
+     * {@code <distributionManagement>}).
+     * <p>
+     * Property placeholders (e.g., {@code ${db.password}}) are preserved 
since they do not contain actual secret
+     * values.
+     *
+     * @return sanitization result with the processed POM content and detected 
patterns
+     */
+    static SanitizationResult sanitize(String pomContent) {
+        List<String> detected = detectSensitiveContent(pomContent);
+
+        String sanitized = pomContent;
+
+        // Mask sensitive element values (preserve property placeholders)
+        sanitized = SENSITIVE_ELEMENT_PATTERN.matcher(sanitized).replaceAll(mr 
-> {
+            String value = mr.group(2).trim();
+            if (value.startsWith("${")) {
+                return Matcher.quoteReplacement(mr.group());
+            }
+            return Matcher.quoteReplacement(
+                    "<" + mr.group(1) + ">***MASKED***</" + mr.group(1) + ">");
+        });
+
+        // Strip servers section
+        sanitized = SERVERS_SECTION_PATTERN.matcher(sanitized).replaceAll("");
+
+        // Strip distributionManagement section
+        sanitized = 
DIST_MGMT_SECTION_PATTERN.matcher(sanitized).replaceAll("");
+
+        boolean wasSanitized = !sanitized.equals(pomContent);
+
+        if (!detected.isEmpty()) {
+            LOG.warnf("Sensitive data detected in pomContent: %s. Content was 
sanitized before processing.", detected);
+        }
+
+        return new SanitizationResult(sanitized, detected, wasSanitized);
+    }
+
+    record SanitizationResult(
+            String pomContent,
+            List<String> detectedPatterns,
+            boolean wasSanitized) {
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
index de560acfafaf..4be2de6b648a 100644
--- 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/DependencyCheckToolsTest.java
@@ -143,14 +143,14 @@ class DependencyCheckToolsTest {
 
     @Test
     void nullPomThrows() {
-        assertThatThrownBy(() -> tools.camel_dependency_check(null, null, 
null, null, null))
+        assertThatThrownBy(() -> tools.camel_dependency_check(null, null, 
null, null, null, null))
                 .isInstanceOf(ToolCallException.class)
                 .hasMessageContaining("required");
     }
 
     @Test
     void blankPomThrows() {
-        assertThatThrownBy(() -> tools.camel_dependency_check("   ", null, 
null, null, null))
+        assertThatThrownBy(() -> tools.camel_dependency_check("   ", null, 
null, null, null, null))
                 .isInstanceOf(ToolCallException.class)
                 .hasMessageContaining("required");
     }
@@ -159,7 +159,7 @@ class DependencyCheckToolsTest {
 
     @Test
     void resultContainsProjectInfo() throws Exception {
-        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null);
+        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonObject projectInfo = result.getMap("projectInfo");
 
@@ -170,7 +170,7 @@ class DependencyCheckToolsTest {
 
     @Test
     void detectsSpringBootRuntime() throws Exception {
-        String json = tools.camel_dependency_check(POM_SPRING_BOOT, null, 
null, null, null);
+        String json = tools.camel_dependency_check(POM_SPRING_BOOT, null, 
null, null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonObject projectInfo = result.getMap("projectInfo");
 
@@ -181,7 +181,7 @@ class DependencyCheckToolsTest {
 
     @Test
     void detectsOutdatedVersion() throws Exception {
-        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null);
+        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonObject versionStatus = result.getMap("versionStatus");
 
@@ -193,7 +193,7 @@ class DependencyCheckToolsTest {
 
     @Test
     void versionStatusContainsCatalogVersion() throws Exception {
-        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null);
+        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonObject versionStatus = result.getMap("versionStatus");
 
@@ -207,7 +207,7 @@ class DependencyCheckToolsTest {
         // POM without BOM has only camel-core, route uses kafka
         String route = "from:\n  uri: kafka:myTopic\n  steps:\n    - to: 
log:out";
 
-        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route, 
null, null, null);
+        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route, 
null, null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonArray missing = (JsonArray) result.get("missingDependencies");
 
@@ -222,7 +222,7 @@ class DependencyCheckToolsTest {
         // POM_WITH_BOM already has camel-kafka
         String route = "from:\n  uri: kafka:myTopic\n  steps:\n    - to: 
log:out";
 
-        String json = tools.camel_dependency_check(POM_WITH_BOM, route, null, 
null, null);
+        String json = tools.camel_dependency_check(POM_WITH_BOM, route, null, 
null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonArray missing = (JsonArray) result.get("missingDependencies");
 
@@ -237,7 +237,7 @@ class DependencyCheckToolsTest {
     void missingDepContainsSnippet() throws Exception {
         String route = "from:\n  uri: kafka:myTopic\n  steps:\n    - to: 
log:out";
 
-        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route, 
null, null, null);
+        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route, 
null, null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonArray missing = (JsonArray) result.get("missingDependencies");
 
@@ -256,7 +256,7 @@ class DependencyCheckToolsTest {
 
     @Test
     void noMissingDepsWithoutRoutes() throws Exception {
-        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null);
+        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonArray missing = (JsonArray) result.get("missingDependencies");
 
@@ -268,7 +268,7 @@ class DependencyCheckToolsTest {
         // timer, log, direct are core components - should not be reported as 
missing
         String route = "from:\n  uri: timer:tick\n  steps:\n    - to: log:out";
 
-        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route, 
null, null, null);
+        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route, 
null, null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonArray missing = (JsonArray) result.get("missingDependencies");
 
@@ -282,7 +282,7 @@ class DependencyCheckToolsTest {
 
     @Test
     void detectsVersionConflictWithBom() throws Exception {
-        String json = tools.camel_dependency_check(POM_WITH_CONFLICT, null, 
null, null, null);
+        String json = tools.camel_dependency_check(POM_WITH_CONFLICT, null, 
null, null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonArray conflicts = (JsonArray) result.get("versionConflicts");
 
@@ -296,7 +296,7 @@ class DependencyCheckToolsTest {
     @Test
     void noConflictWithPropertyPlaceholderVersion() throws Exception {
         // POM_WITH_BOM uses ${camel.version} - not a conflict
-        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null);
+        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonArray conflicts = (JsonArray) result.get("versionConflicts");
 
@@ -306,7 +306,7 @@ class DependencyCheckToolsTest {
     @Test
     void noConflictWithoutBom() throws Exception {
         // No BOM means explicit versions are expected
-        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, null, 
null, null, null);
+        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, null, 
null, null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonArray conflicts = (JsonArray) result.get("versionConflicts");
 
@@ -317,7 +317,7 @@ class DependencyCheckToolsTest {
 
     @Test
     void recommendsUpgradeWhenOutdated() throws Exception {
-        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null);
+        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonArray recommendations = (JsonArray) result.get("recommendations");
 
@@ -329,7 +329,7 @@ class DependencyCheckToolsTest {
 
     @Test
     void recommendsBomWhenMissing() throws Exception {
-        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, null, 
null, null, null);
+        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, null, 
null, null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonArray recommendations = (JsonArray) result.get("recommendations");
 
@@ -343,7 +343,7 @@ class DependencyCheckToolsTest {
     void recommendsMissingDeps() throws Exception {
         String route = "from:\n  uri: kafka:myTopic\n  steps:\n    - to: 
log:out";
 
-        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route, 
null, null, null);
+        String json = tools.camel_dependency_check(POM_WITHOUT_BOM, route, 
null, null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonArray recommendations = (JsonArray) result.get("recommendations");
 
@@ -359,7 +359,7 @@ class DependencyCheckToolsTest {
     void summaryShowsHealthyWhenNoIssues() throws Exception {
         // Use a pom with current catalog version to avoid outdated flag
         // Since we can't easily match the catalog version, we just check 
structure
-        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null);
+        String json = tools.camel_dependency_check(POM_WITH_BOM, null, null, 
null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonObject summary = result.getMap("summary");
 
@@ -373,7 +373,7 @@ class DependencyCheckToolsTest {
     void summaryCountsAllIssues() throws Exception {
         String route = "from:\n  uri: kafka:myTopic\n  steps:\n    - to: 
log:out";
 
-        String json = tools.camel_dependency_check(POM_WITH_CONFLICT, route, 
null, null, null);
+        String json = tools.camel_dependency_check(POM_WITH_CONFLICT, route, 
null, null, null, null);
         JsonObject result = (JsonObject) Jsoner.deserialize(json);
         JsonObject summary = result.getMap("summary");
 
@@ -392,4 +392,55 @@ class DependencyCheckToolsTest {
         assertThat(DependencyCheckTools.compareVersions("3.20.0", 
"4.0.0")).isNegative();
         assertThat(DependencyCheckTools.compareVersions("4.19.0-SNAPSHOT", 
"4.19.0")).isZero();
     }
+
+    // ---- POM sanitization ----
+
+    private static final String POM_WITH_SENSITIVE_DATA = """
+            <project>
+                <properties>
+                    <camel.version>4.10.0</camel.version>
+                    <maven.compiler.release>21</maven.compiler.release>
+                    <db.password>superSecret123</db.password>
+                </properties>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-core</artifactId>
+                    </dependency>
+                </dependencies>
+            </project>
+            """;
+
+    @Test
+    void sanitizationMasksSensitiveData() throws Exception {
+        String json = tools.camel_dependency_check(POM_WITH_SENSITIVE_DATA, 
null, null, null, null, null);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+
+        // Should have sanitization warnings
+        JsonArray warnings = (JsonArray) result.get("sanitizationWarnings");
+        assertThat(warnings).isNotNull();
+        assertThat(warnings).isNotEmpty();
+        assertThat(warnings.stream().map(Object::toString).toList())
+                .anyMatch(w -> w.contains("db.password"));
+    }
+
+    @Test
+    void sanitizationDisabledWhenFalse() throws Exception {
+        String json = tools.camel_dependency_check(POM_WITH_SENSITIVE_DATA, 
null, null, null, null, false);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+
+        // Should NOT have sanitization warnings
+        assertThat(result.get("sanitizationWarnings")).isNull();
+    }
+
+    @Test
+    void sanitizationStillParsesCorrectly() throws Exception {
+        String json = tools.camel_dependency_check(POM_WITH_SENSITIVE_DATA, 
null, null, null, null, null);
+        JsonObject result = (JsonObject) Jsoner.deserialize(json);
+        JsonObject projectInfo = result.getMap("projectInfo");
+
+        // Core analysis should still work after sanitization
+        assertThat(projectInfo.getString("camelVersion")).isEqualTo("4.10.0");
+        assertThat(projectInfo.getString("runtimeType")).isEqualTo("main");
+    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PomSanitizerTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PomSanitizerTest.java
new file mode 100644
index 000000000000..6f2758d72804
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/PomSanitizerTest.java
@@ -0,0 +1,317 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.mcp;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class PomSanitizerTest {
+
+    // A clean POM with no sensitive data
+    private static final String CLEAN_POM = """
+            <project>
+                <properties>
+                    <camel.version>4.10.0</camel.version>
+                    <maven.compiler.release>21</maven.compiler.release>
+                </properties>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-core</artifactId>
+                    </dependency>
+                </dependencies>
+            </project>
+            """;
+
+    // A POM with various sensitive elements
+    private static final String POM_WITH_CREDENTIALS = """
+            <project>
+                <properties>
+                    <camel.version>4.10.0</camel.version>
+                    <db.password>superSecret123</db.password>
+                    <api.token>tok_abc123xyz</api.token>
+                </properties>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-core</artifactId>
+                    </dependency>
+                </dependencies>
+            </project>
+            """;
+
+    // A POM with property placeholders (not actual secrets)
+    private static final String POM_WITH_PLACEHOLDERS = """
+            <project>
+                <properties>
+                    <camel.version>4.10.0</camel.version>
+                    <db.password>${env.DB_PASSWORD}</db.password>
+                    <api.token>${env.API_TOKEN}</api.token>
+                </properties>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-core</artifactId>
+                    </dependency>
+                </dependencies>
+            </project>
+            """;
+
+    // A POM with servers section
+    private static final String POM_WITH_SERVERS = """
+            <project>
+                <properties>
+                    <camel.version>4.10.0</camel.version>
+                </properties>
+                <servers>
+                    <server>
+                        <id>my-repo</id>
+                        <username>admin</username>
+                        <password>repoPassword</password>
+                    </server>
+                </servers>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-core</artifactId>
+                    </dependency>
+                </dependencies>
+            </project>
+            """;
+
+    // A POM with distributionManagement section
+    private static final String POM_WITH_DIST_MGMT = """
+            <project>
+                <properties>
+                    <camel.version>4.10.0</camel.version>
+                </properties>
+                <distributionManagement>
+                    <repository>
+                        <id>internal-releases</id>
+                        <url>https://private.repo.example.com/releases</url>
+                    </repository>
+                </distributionManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-core</artifactId>
+                    </dependency>
+                </dependencies>
+            </project>
+            """;
+
+    // A POM with multiple sensitive patterns
+    private static final String POM_WITH_MULTIPLE_SENSITIVE = """
+            <project>
+                <properties>
+                    <camel.version>4.10.0</camel.version>
+                    <secret>myAppSecret</secret>
+                    <apiKey>key_12345</apiKey>
+                    <accessKey>AKIA1234567890</accessKey>
+                </properties>
+                <servers>
+                    <server>
+                        <id>repo</id>
+                        <password>pass</password>
+                    </server>
+                </servers>
+                <distributionManagement>
+                    <repository>
+                        <id>releases</id>
+                        <url>https://repo.example.com/releases</url>
+                    </repository>
+                </distributionManagement>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-core</artifactId>
+                    </dependency>
+                </dependencies>
+            </project>
+            """;
+
+    // ---- Detection tests ----
+
+    @Test
+    void detectsPasswordElement() {
+        List<String> findings = 
PomSanitizer.detectSensitiveContent(POM_WITH_CREDENTIALS);
+        assertThat(findings).anyMatch(f -> f.contains("password"));
+    }
+
+    @Test
+    void detectsTokenElement() {
+        List<String> findings = 
PomSanitizer.detectSensitiveContent(POM_WITH_CREDENTIALS);
+        assertThat(findings).anyMatch(f -> f.contains("token"));
+    }
+
+    @Test
+    void detectsApiKeyElement() {
+        String pom = 
"<project><properties><apiKey>key123</apiKey></properties></project>";
+        List<String> findings = PomSanitizer.detectSensitiveContent(pom);
+        assertThat(findings).anyMatch(f -> f.contains("apiKey"));
+    }
+
+    @Test
+    void detectsSecretElement() {
+        String pom = 
"<project><properties><secret>s3cr3t</secret></properties></project>";
+        List<String> findings = PomSanitizer.detectSensitiveContent(pom);
+        assertThat(findings).anyMatch(f -> f.contains("secret"));
+    }
+
+    @Test
+    void detectsPropertyStyleNames() {
+        List<String> findings = 
PomSanitizer.detectSensitiveContent(POM_WITH_CREDENTIALS);
+        assertThat(findings).anyMatch(f -> f.equals("db.password"));
+        assertThat(findings).anyMatch(f -> f.equals("api.token"));
+    }
+
+    @Test
+    void ignoresPropertyPlaceholders() {
+        List<String> findings = 
PomSanitizer.detectSensitiveContent(POM_WITH_PLACEHOLDERS);
+        // Property placeholders should not be flagged as sensitive
+        assertThat(findings).noneMatch(f -> f.equals("db.password"));
+        assertThat(findings).noneMatch(f -> f.equals("api.token"));
+    }
+
+    @Test
+    void detectsServersSection() {
+        List<String> findings = 
PomSanitizer.detectSensitiveContent(POM_WITH_SERVERS);
+        assertThat(findings).anyMatch(f -> f.contains("<servers>"));
+    }
+
+    @Test
+    void detectsDistributionManagementSection() {
+        List<String> findings = 
PomSanitizer.detectSensitiveContent(POM_WITH_DIST_MGMT);
+        assertThat(findings).anyMatch(f -> 
f.contains("<distributionManagement>"));
+    }
+
+    @Test
+    void noDetectionForCleanPom() {
+        List<String> findings = PomSanitizer.detectSensitiveContent(CLEAN_POM);
+        assertThat(findings).isEmpty();
+    }
+
+    @Test
+    void detectsMultipleSensitiveElements() {
+        List<String> findings = 
PomSanitizer.detectSensitiveContent(POM_WITH_MULTIPLE_SENSITIVE);
+        assertThat(findings.size()).isGreaterThanOrEqualTo(3);
+    }
+
+    // ---- Sanitization tests ----
+
+    @Test
+    void masksPasswordValues() {
+        PomSanitizer.SanitizationResult result = 
PomSanitizer.sanitize(POM_WITH_CREDENTIALS);
+        
assertThat(result.pomContent()).contains("<db.password>***MASKED***</db.password>");
+        assertThat(result.pomContent()).doesNotContain("superSecret123");
+    }
+
+    @Test
+    void masksTokenValues() {
+        PomSanitizer.SanitizationResult result = 
PomSanitizer.sanitize(POM_WITH_CREDENTIALS);
+        
assertThat(result.pomContent()).contains("<api.token>***MASKED***</api.token>");
+        assertThat(result.pomContent()).doesNotContain("tok_abc123xyz");
+    }
+
+    @Test
+    void preservesPropertyPlaceholders() {
+        PomSanitizer.SanitizationResult result = 
PomSanitizer.sanitize(POM_WITH_PLACEHOLDERS);
+        assertThat(result.pomContent()).contains("${env.DB_PASSWORD}");
+        assertThat(result.pomContent()).contains("${env.API_TOKEN}");
+    }
+
+    @Test
+    void stripsServersSection() {
+        PomSanitizer.SanitizationResult result = 
PomSanitizer.sanitize(POM_WITH_SERVERS);
+        assertThat(result.pomContent()).doesNotContain("<servers>");
+        assertThat(result.pomContent()).doesNotContain("repoPassword");
+        assertThat(result.wasSanitized()).isTrue();
+    }
+
+    @Test
+    void stripsDistributionManagement() {
+        PomSanitizer.SanitizationResult result = 
PomSanitizer.sanitize(POM_WITH_DIST_MGMT);
+        
assertThat(result.pomContent()).doesNotContain("<distributionManagement>");
+        
assertThat(result.pomContent()).doesNotContain("private.repo.example.com");
+        assertThat(result.wasSanitized()).isTrue();
+    }
+
+    @Test
+    void cleanPomUnchanged() {
+        PomSanitizer.SanitizationResult result = 
PomSanitizer.sanitize(CLEAN_POM);
+        assertThat(result.pomContent()).isEqualTo(CLEAN_POM);
+        assertThat(result.wasSanitized()).isFalse();
+        assertThat(result.detectedPatterns()).isEmpty();
+    }
+
+    @Test
+    void sanitizedPomStillParseable() throws Exception {
+        PomSanitizer.SanitizationResult result = 
PomSanitizer.sanitize(POM_WITH_CREDENTIALS);
+        // Should still be valid XML that can be parsed
+        MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(result.pomContent());
+        assertThat(pom.camelVersion()).isEqualTo("4.10.0");
+    }
+
+    @Test
+    void sanitizesMultiplePatterns() {
+        PomSanitizer.SanitizationResult result = 
PomSanitizer.sanitize(POM_WITH_MULTIPLE_SENSITIVE);
+        assertThat(result.pomContent()).doesNotContain("myAppSecret");
+        assertThat(result.pomContent()).doesNotContain("key_12345");
+        assertThat(result.pomContent()).doesNotContain("AKIA1234567890");
+        assertThat(result.pomContent()).doesNotContain("<servers>");
+        
assertThat(result.pomContent()).doesNotContain("<distributionManagement>");
+        assertThat(result.wasSanitized()).isTrue();
+        assertThat(result.detectedPatterns().size()).isGreaterThanOrEqualTo(3);
+    }
+
+    @Test
+    void resultReportsWasSanitized() {
+        PomSanitizer.SanitizationResult result = 
PomSanitizer.sanitize(POM_WITH_CREDENTIALS);
+        assertThat(result.wasSanitized()).isTrue();
+        assertThat(result.detectedPatterns()).isNotEmpty();
+    }
+
+    @Test
+    void resultReportsNotSanitizedForCleanPom() {
+        PomSanitizer.SanitizationResult result = 
PomSanitizer.sanitize(CLEAN_POM);
+        assertThat(result.wasSanitized()).isFalse();
+    }
+
+    @Test
+    void caseInsensitiveDetection() {
+        String pom = 
"<project><properties><PASSWORD>secret</PASSWORD></properties></project>";
+        List<String> findings = PomSanitizer.detectSensitiveContent(pom);
+        assertThat(findings).isNotEmpty();
+    }
+
+    @Test
+    void detectsAccessKeyElement() {
+        String pom = 
"<project><properties><accessKey>AKIA123</accessKey></properties></project>";
+        List<String> findings = PomSanitizer.detectSensitiveContent(pom);
+        assertThat(findings).anyMatch(f -> f.contains("accessKey"));
+    }
+
+    @Test
+    void detectsPassphraseElement() {
+        String pom = 
"<project><properties><passphrase>my-passphrase</passphrase></properties></project>";
+        List<String> findings = PomSanitizer.detectSensitiveContent(pom);
+        assertThat(findings).anyMatch(f -> f.contains("passphrase"));
+    }
+}


Reply via email to