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

acosentino 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 95ca2a8294dd CAMEL-23273: Camel-Jbang-mcp: Sanitize sensitive data in 
POM content passed to migration tools (#22344)
95ca2a8294dd is described below

commit 95ca2a8294dd0a7b2d1e2b137bbf26d82c00015e
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Apr 1 13:31:55 2026 +0200

    CAMEL-23273: Camel-Jbang-mcp: Sanitize sensitive data in POM content passed 
to migration tools (#22344)
    
    * 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]>
    
    * CAMEL-23273: Address review feedback on POM sanitizer
    
    - Remove <servers> and <distributionManagement> section stripping (servers
      belongs to settings.xml, distributionManagement contains URLs not 
credentials)
    - Extract PomSanitizer.process() helper to eliminate code duplication across
      MigrationTools, DependencyCheckTools, and MigrationWildflyKarafTools
    - Consolidate per-pattern warnings into a single summary warning
    - Remove unused wasSanitized field from SanitizationResult
    - Document regex-based detection limitations (false positives/negatives)
    - Add sanitization integration tests for MigrationTools and
      MigrationWildflyKarafTools
    
    Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
    
    ---------
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
---
 .../core/commands/mcp/DependencyCheckTools.java    |  24 +-
 .../jbang/core/commands/mcp/MigrationTools.java    |  15 +-
 .../commands/mcp/MigrationWildflyKarafTools.java   |  17 +-
 .../dsl/jbang/core/commands/mcp/PomSanitizer.java  | 146 +++++++++++++
 .../commands/mcp/DependencyCheckToolsTest.java     |  89 ++++++--
 .../core/commands/mcp/MigrationToolsTest.java      |  98 +++++++++
 .../mcp/MigrationWildflyKarafToolsTest.java        | 111 ++++++++++
 .../jbang/core/commands/mcp/PomSanitizerTest.java  | 241 +++++++++++++++++++++
 8 files changed, 708 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 c135563558c2..67a04bb9ecef 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
@@ -52,27 +52,41 @@ 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 mask 
sensitive data (passwords, tokens, API keys) "
+                        + "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") Boolean sanitizePom) {
 
         if (pomContent == null || pomContent.isBlank()) {
             throw new ToolCallException("pomContent is required", null);
         }
 
         try {
+            PomSanitizer.ProcessedPom processed = 
PomSanitizer.process(pomContent, sanitizePom);
+
             CamelCatalog catalog = catalogService.loadCatalog(runtime, 
camelVersion, platformBom);
 
-            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(pomContent);
+            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(processed.content());
 
             JsonObject result = new JsonObject();
 
+            // Add sanitization warnings if any
+            if (!processed.warnings().isEmpty()) {
+                JsonArray sanitizationArr = new JsonArray();
+                processed.warnings().forEach(sanitizationArr::add);
+                result.put("sanitizationWarnings", sanitizationArr);
+            }
+
             // Project info
             JsonObject projectInfo = new JsonObject();
             projectInfo.put("camelVersion", pom.camelVersion());
@@ -93,7 +107,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(processed.content(), 
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 9628a9132f1d..66bdcd624243 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
@@ -50,21 +50,28 @@ public class MigrationTools {
     @Tool(annotations = @Tool.Annotations(readOnlyHint = true, destructiveHint 
= false, openWorldHint = false),
           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 mask 
sensitive data (passwords, tokens, API keys) "
+                        + "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") Boolean sanitizePom) {
 
         if (pomContent == null || pomContent.isBlank()) {
             throw new ToolCallException("pomContent is required", null);
         }
 
         try {
-            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(pomContent);
+            PomSanitizer.ProcessedPom processed = 
PomSanitizer.process(pomContent, sanitizePom);
+
+            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(processed.content());
 
             String runtimeType = pom.runtimeType();
             int majorVersion = pom.majorVersion();
 
-            List<String> warnings = new ArrayList<>();
+            List<String> warnings = new ArrayList<>(processed.warnings());
             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 acadcc3e287b..8d001a258755 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
@@ -49,18 +49,25 @@ 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 mask 
sensitive data (passwords, tokens, API keys) "
+                        + "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") Boolean sanitizePom) {
 
         if (pomContent == null || pomContent.isBlank()) {
             throw new ToolCallException("pomContent is required", null);
         }
 
         try {
-            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(pomContent);
+            PomSanitizer.ProcessedPom processed = 
PomSanitizer.process(pomContent, sanitizePom);
+
+            MigrationData.PomAnalysis pom = 
MigrationData.parsePomContent(processed.content());
 
             String sourceRuntime = pom.isWildfly() ? "wildfly" : pom.isKaraf() 
? "karaf" : "unknown";
             String resolvedTarget = targetRuntime != null && 
!targetRuntime.isBlank()
@@ -84,7 +91,7 @@ public class MigrationWildflyKarafTools {
                     .collect(Collectors.toList());
 
             // Warnings specific to the source runtime
-            List<String> warnings = new ArrayList<>();
+            List<String> warnings = new ArrayList<>(processed.warnings());
             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..ecb50eddb2d4
--- /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,146 @@
+/*
+ * 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 java.util.stream.Collectors;
+
+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) 
in XML element values and masks them.
+ * Property placeholders (e.g., {@code ${db.password}}) are preserved since 
they reference external values and do not
+ * contain actual secrets.
+ * <p>
+ * <b>Limitations:</b> Detection is tag-name-based using keyword matching. 
This means:
+ * <ul>
+ * <li><b>False positives</b> — non-secret values in elements whose names 
happen to contain a keyword (e.g.,
+ * {@code <password-policy>strict</password-policy>},
+ * {@code <token-refresh-interval>300</token-refresh-interval>}).</li>
+ * <li><b>False negatives</b> — actual secrets in elements with non-obvious 
names (e.g., credentials embedded in JDBC
+ * URLs, or elements named {@code <my.credential>} where the singular form is 
not in the keyword list).</li>
+ * </ul>
+ * This heuristic is a best-effort safety net, not a guarantee. Users should 
still avoid passing sensitive data.
+ */
+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);
+
+    private PomSanitizer() {
+    }
+
+    /**
+     * Detect sensitive content patterns in POM content.
+     *
+     * @return list of element names that contain sensitive values
+     */
+    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));
+            }
+        }
+
+        return new ArrayList<>(findings);
+    }
+
+    /**
+     * Sanitize POM content by masking sensitive element values.
+     * <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) + ">");
+        });
+
+        if (!detected.isEmpty()) {
+            LOG.warnf("Sensitive data detected in pomContent: %s. Content was 
sanitized before processing.", detected);
+        }
+
+        return new SanitizationResult(sanitized, detected);
+    }
+
+    /**
+     * Process POM content with optional sanitization. This is the entry point 
for tool methods.
+     *
+     * @param  pomContent the raw POM content
+     * @param  sanitize   if {@code null} or {@code true}, sanitize; if {@code 
false}, skip sanitization
+     * @return            processed result with content and any warnings
+     */
+    static ProcessedPom process(String pomContent, Boolean sanitize) {
+        if (sanitize != null && !sanitize) {
+            return new ProcessedPom(pomContent, List.of());
+        }
+        SanitizationResult sr = sanitize(pomContent);
+        List<String> warnings = new ArrayList<>();
+        if (!sr.detectedPatterns().isEmpty()) {
+            warnings.add("Sensitive data detected and masked: "
+                         + 
sr.detectedPatterns().stream().collect(Collectors.joining(", ")));
+        }
+        return new ProcessedPom(sr.pomContent(), warnings);
+    }
+
+    record SanitizationResult(
+            String pomContent,
+            List<String> detectedPatterns) {
+    }
+
+    record ProcessedPom(
+            String content,
+            List<String> warnings) {
+    }
+}
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/MigrationToolsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationToolsTest.java
new file mode 100644
index 000000000000..ed801bae50fd
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationToolsTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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 org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class MigrationToolsTest {
+
+    private final MigrationTools tools;
+
+    MigrationToolsTest() {
+        tools = new MigrationTools();
+        tools.migrationData = new MigrationData();
+    }
+
+    private static final String CLEAN_POM = """
+            <project>
+                <properties>
+                    <camel.version>3.20.0</camel.version>
+                    <maven.compiler.release>17</maven.compiler.release>
+                </properties>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-core</artifactId>
+                        <version>${camel.version}</version>
+                    </dependency>
+                </dependencies>
+            </project>
+            """;
+
+    private static final String POM_WITH_SENSITIVE_DATA = """
+            <project>
+                <properties>
+                    <camel.version>3.20.0</camel.version>
+                    <maven.compiler.release>17</maven.compiler.release>
+                    <db.password>superSecret123</db.password>
+                    <api.token>tok_abc123xyz</api.token>
+                </properties>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-core</artifactId>
+                        <version>${camel.version}</version>
+                    </dependency>
+                </dependencies>
+            </project>
+            """;
+
+    // ---- POM sanitization ----
+
+    @Test
+    void sanitizationMasksSensitiveData() {
+        MigrationTools.ProjectAnalysisResult result = 
tools.camel_migration_analyze(POM_WITH_SENSITIVE_DATA, null);
+
+        assertThat(result.warnings()).anyMatch(w -> w.contains("db.password"));
+        assertThat(result.warnings()).anyMatch(w -> w.contains("api.token"));
+    }
+
+    @Test
+    void sanitizationDisabledWhenFalse() {
+        MigrationTools.ProjectAnalysisResult result = 
tools.camel_migration_analyze(POM_WITH_SENSITIVE_DATA, false);
+
+        // No sanitization warnings when disabled
+        assertThat(result.warnings()).noneMatch(w -> w.contains("Sensitive 
data detected"));
+    }
+
+    @Test
+    void analysisWorksAfterSanitization() {
+        MigrationTools.ProjectAnalysisResult result = 
tools.camel_migration_analyze(POM_WITH_SENSITIVE_DATA, null);
+
+        assertThat(result.camelVersion()).isEqualTo("3.20.0");
+        assertThat(result.runtimeType()).isEqualTo("main");
+    }
+
+    @Test
+    void cleanPomHasNoSanitizationWarnings() {
+        MigrationTools.ProjectAnalysisResult result = 
tools.camel_migration_analyze(CLEAN_POM, null);
+
+        assertThat(result.warnings()).noneMatch(w -> w.contains("Sensitive 
data detected"));
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationWildflyKarafToolsTest.java
 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationWildflyKarafToolsTest.java
new file mode 100644
index 000000000000..d60dccb98f56
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-mcp/src/test/java/org/apache/camel/dsl/jbang/core/commands/mcp/MigrationWildflyKarafToolsTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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 org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class MigrationWildflyKarafToolsTest {
+
+    private final MigrationWildflyKarafTools tools;
+
+    MigrationWildflyKarafToolsTest() {
+        tools = new MigrationWildflyKarafTools();
+        tools.migrationData = new MigrationData();
+    }
+
+    // A WildFly POM with sensitive data
+    private static final String WILDFLY_POM_WITH_SENSITIVE_DATA = """
+            <project>
+                <properties>
+                    <camel.version>2.25.0</camel.version>
+                    <db.password>superSecret123</db.password>
+                </properties>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-core</artifactId>
+                        <version>${camel.version}</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-cdi</artifactId>
+                        <version>${camel.version}</version>
+                    </dependency>
+                </dependencies>
+            </project>
+            """;
+
+    // A clean WildFly POM
+    private static final String WILDFLY_POM_CLEAN = """
+            <project>
+                <properties>
+                    <camel.version>2.25.0</camel.version>
+                </properties>
+                <dependencies>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-core</artifactId>
+                        <version>${camel.version}</version>
+                    </dependency>
+                    <dependency>
+                        <groupId>org.apache.camel</groupId>
+                        <artifactId>camel-cdi</artifactId>
+                        <version>${camel.version}</version>
+                    </dependency>
+                </dependencies>
+            </project>
+            """;
+
+    // ---- POM sanitization ----
+
+    @Test
+    void sanitizationMasksSensitiveData() {
+        MigrationWildflyKarafTools.WildflyKarafMigrationResult result
+                = 
tools.camel_migration_wildfly_karaf(WILDFLY_POM_WITH_SENSITIVE_DATA, null, 
"4.18.0", null);
+
+        assertThat(result.warnings()).anyMatch(w -> w.contains("db.password"));
+    }
+
+    @Test
+    void sanitizationDisabledWhenFalse() {
+        MigrationWildflyKarafTools.WildflyKarafMigrationResult result
+                = 
tools.camel_migration_wildfly_karaf(WILDFLY_POM_WITH_SENSITIVE_DATA, null, 
"4.18.0", false);
+
+        // No sanitization warnings when disabled
+        assertThat(result.warnings()).noneMatch(w -> w.contains("Sensitive 
data detected"));
+    }
+
+    @Test
+    void analysisWorksAfterSanitization() {
+        MigrationWildflyKarafTools.WildflyKarafMigrationResult result
+                = 
tools.camel_migration_wildfly_karaf(WILDFLY_POM_WITH_SENSITIVE_DATA, null, 
"4.18.0", null);
+
+        assertThat(result.sourceRuntime()).isEqualTo("wildfly");
+        assertThat(result.sourceCamelVersion()).isEqualTo("2.25.0");
+        assertThat(result.targetRuntime()).isEqualTo("quarkus");
+    }
+
+    @Test
+    void cleanPomHasNoSanitizationWarnings() {
+        MigrationWildflyKarafTools.WildflyKarafMigrationResult result
+                = tools.camel_migration_wildfly_karaf(WILDFLY_POM_CLEAN, null, 
"4.18.0", null);
+
+        assertThat(result.warnings()).noneMatch(w -> w.contains("Sensitive 
data detected"));
+    }
+}
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..d77e5a3d8e05
--- /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,241 @@
+/*
+ * 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 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>
+                <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 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 cleanPomUnchanged() {
+        PomSanitizer.SanitizationResult result = 
PomSanitizer.sanitize(CLEAN_POM);
+        assertThat(result.pomContent()).isEqualTo(CLEAN_POM);
+        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.detectedPatterns().size()).isGreaterThanOrEqualTo(3);
+    }
+
+    @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"));
+    }
+
+    // ---- Process helper tests ----
+
+    @Test
+    void processReturnsSingleSummaryWarning() {
+        PomSanitizer.ProcessedPom result = 
PomSanitizer.process(POM_WITH_CREDENTIALS, null);
+        assertThat(result.warnings()).hasSize(1);
+        assertThat(result.warnings().get(0)).contains("db.password");
+        assertThat(result.warnings().get(0)).contains("api.token");
+    }
+
+    @Test
+    void processSkipsSanitizationWhenFalse() {
+        PomSanitizer.ProcessedPom result = 
PomSanitizer.process(POM_WITH_CREDENTIALS, false);
+        assertThat(result.warnings()).isEmpty();
+        assertThat(result.content()).isEqualTo(POM_WITH_CREDENTIALS);
+    }
+
+    @Test
+    void processNoWarningsForCleanPom() {
+        PomSanitizer.ProcessedPom result = PomSanitizer.process(CLEAN_POM, 
null);
+        assertThat(result.warnings()).isEmpty();
+        assertThat(result.content()).isEqualTo(CLEAN_POM);
+    }
+}

Reply via email to