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

jerryshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 761c106bdb [#13403] fix(core): quote replacement values in 
JobManager.replacePlaceholder (#13404)
761c106bdb is described below

commit 761c106bdbf4846fe91794560ef54bc7d9a1d5d0
Author: YangJie <[email protected]>
AuthorDate: Tue Sep 22 05:12:18 2026 -0400

    [#13403] fix(core): quote replacement values in 
JobManager.replacePlaceholder (#13404)
    
    ### What changes were proposed in this pull request?
    
    `JobManager.replacePlaceholder` now passes replacement values through
    `Matcher.quoteReplacement`, so substitution is literal. The no-match
    fallback keeps the original placeholder text as before.
    
    ### Why are the changes needed?
    
    `Matcher.appendReplacement` interprets `$` and `\` in the replacement as
    group references and escapes, so a value like `cost=$5` threw
    `IndexOutOfBoundsException` and `p$1x` substituted the wrong text.
    Placeholders are documented as literal substitution.
    
    Fix: #13403
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    
    Added `TestJobTemplate.testReplacePlaceholdersWithSpecialCharacters`,
    covering values with `$5`, `p$1x`, and a backslash. It fails against the
    pre-fix code.
---
 .../main/java/org/apache/gravitino/job/JobManager.java   |  2 +-
 .../java/org/apache/gravitino/job/TestJobTemplate.java   | 16 ++++++++++++++++
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/core/src/main/java/org/apache/gravitino/job/JobManager.java 
b/core/src/main/java/org/apache/gravitino/job/JobManager.java
index 6fef520af7..56614b15ba 100644
--- a/core/src/main/java/org/apache/gravitino/job/JobManager.java
+++ b/core/src/main/java/org/apache/gravitino/job/JobManager.java
@@ -957,7 +957,7 @@ public class JobManager implements JobOperationDispatcher {
       String key = matcher.group(1);
       String replacement = replacements.get(key);
       if (replacement != null) {
-        matcher.appendReplacement(result, replacement);
+        matcher.appendReplacement(result, 
Matcher.quoteReplacement(replacement));
       } else {
         // If no replacement is found, keep the placeholder as is
         matcher.appendReplacement(result, matcher.group(0));
diff --git a/core/src/test/java/org/apache/gravitino/job/TestJobTemplate.java 
b/core/src/test/java/org/apache/gravitino/job/TestJobTemplate.java
index 3ce6d1b87c..7d2115d8c3 100644
--- a/core/src/test/java/org/apache/gravitino/job/TestJobTemplate.java
+++ b/core/src/test/java/org/apache/gravitino/job/TestJobTemplate.java
@@ -141,6 +141,22 @@ public class TestJobTemplate {
     Assertions.assertEquals("Hello, Hank! Your score is 300.", result);
   }
 
+  @Test
+  public void testReplacePlaceholdersWithSpecialCharacters() {
+    // Replacement values containing '$' and '\' must be treated as literal 
text,
+    // not as Matcher replacement syntax (group references and escapes).
+    String template = "config={{conf}}";
+
+    Map<String, String> replacements = ImmutableMap.of("conf", "path=C:\\tmp 
and cost=$5");
+    String result = JobManager.replacePlaceholder(template, replacements);
+    Assertions.assertEquals("config=path=C:\\tmp and cost=$5", result);
+
+    // A '$' followed by a digit must not substitute the placeholder's own 
group
+    replacements = ImmutableMap.of("conf", "p$1x");
+    result = JobManager.replacePlaceholder(template, replacements);
+    Assertions.assertEquals("config=p$1x", result);
+  }
+
   @Test
   public void testFetchFilesFromUir() throws IOException {
     File testFile1 = Files.createTempFile(tempDir.toPath(), "testFile1", 
".txt").toFile();

Reply via email to