VYPR
Moderate severityNVD Advisory· Published Jun 26, 2018· Updated Sep 16, 2024

CVE-2018-1000604

CVE-2018-1000604

Description

Persistent XSS in Jenkins Badge Plugin allows attackers with control over badge content to execute JavaScript in another user's browser.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Persistent XSS in Jenkins Badge Plugin allows attackers with control over badge content to execute JavaScript in another user's browser.

Vulnerability

A persisted cross-site scripting (XSS) vulnerability exists in Jenkins Badge Plugin versions 1.4 and earlier. The flaw is located in BadgeSummaryAction.java and HtmlBadgeAction.java, where user-controlled build badge content is rendered without proper sanitization. Attackers who can control build badge content (e.g., through pipeline scripts or job configuration) can inject arbitrary HTML and JavaScript that is stored and later executed when another user views the badge.

Exploitation

An attacker needs the ability to control build badge content, typically granted to users with Job/Configure permission. They inject malicious JavaScript into the badge text or HTML. The injected payload is stored and executed in the victim's browser when the victim performs certain UI actions, such as viewing the build page or interacting with the badge. No further user interaction beyond browsing the affected page is required.

Impact

Successful exploitation allows the attacker to execute arbitrary JavaScript in the context of the victim's browser. This can lead to session hijacking, credential theft, or performing actions on behalf of the victim within Jenkins, potentially gaining administrative access.

Mitigation

The vulnerability is fixed in Badge Plugin version 1.5, released on 2018-06-25. The fix introduces HTML sanitization using the antisamy-markup-formatter library and RawHtmlMarkupFormatter to escape untrusted content. Users should upgrade to version 1.5 or later. As a workaround, restrict the ability to configure jobs to trusted users to limit the attack surface. References: [1], [2], [3].

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
org.jenkins-ci.plugins:badgeMaven
< 1.51.5

Affected products

1

Patches

1
63a7744cef33

[SECURITY-906]

5 files changed · +63 5
  • pom.xml+5 0 modified
    @@ -112,6 +112,11 @@
                 <version>1.39</version>
             </dependency>
     
    +        <dependency>
    +            <groupId>org.jenkins-ci.plugins</groupId>
    +            <artifactId>antisamy-markup-formatter</artifactId>
    +            <version>1.5</version>
    +        </dependency>
             <dependency>
                 <groupId>org.jenkins-ci.plugins.workflow</groupId>
                 <artifactId>workflow-cps</artifactId>
    
  • src/main/java/com/jenkinsci/plugins/badge/action/BadgeSummaryAction.java+17 1 modified
    @@ -23,14 +23,21 @@
      */
    
     package com.jenkinsci.plugins.badge.action;
    
     
    
    +import hudson.markup.RawHtmlMarkupFormatter;
    
     import org.apache.commons.lang.StringEscapeUtils;
    
     import org.jenkinsci.plugins.scriptsecurity.sandbox.whitelists.Whitelisted;
    
     import org.kohsuke.stapler.export.Exported;
    
     import org.kohsuke.stapler.export.ExportedBean;
    
     
    
    +import java.io.IOException;
    
    +import java.util.logging.Level;
    
    +import java.util.logging.Logger;
    
    +
    
    +
    
     @ExportedBean(defaultVisibility = 2)
    
     public class BadgeSummaryAction extends AbstractAction {
    
       private static final long serialVersionUID = 1L;
    
    +  private static final Logger LOGGER = Logger.getLogger(BadgeSummaryAction.class.getName());
    
     
    
       private final String iconPath;
    
       private String summaryText = "";
    
    @@ -57,9 +64,18 @@ public String getIconPath() {
         return iconPath;
    
       }
    
     
    
    +  public String getRawText() {
    
    +    return summaryText;
    
    +  }
    
    +
    
       @Exported
    
       public String getText() {
    
    -    return summaryText;
    
    +    try {
    
    +      return new RawHtmlMarkupFormatter(false).translate(summaryText);
    
    +    } catch (IOException e) {
    
    +      LOGGER.log(Level.WARNING, "Error preparing summary text for ui", e);
    
    +      return "<b><font color=\"red\">ERROR</font></b>";
    
    +    }
    
       }
    
     
    
       @Whitelisted
    
    
  • src/main/java/com/jenkinsci/plugins/badge/action/HtmlBadgeAction.java+17 2 modified
    @@ -23,12 +23,19 @@
      */
    
     package com.jenkinsci.plugins.badge.action;
    
     
    
    +import hudson.markup.RawHtmlMarkupFormatter;
    
     import org.kohsuke.stapler.export.Exported;
    
     import org.kohsuke.stapler.export.ExportedBean;
    
     
    
    +import java.io.IOException;
    
    +import java.util.logging.Level;
    
    +import java.util.logging.Logger;
    
    +
    
     @ExportedBean(defaultVisibility = 2)
    
     public class HtmlBadgeAction extends AbstractBadgeAction {
    
       private static final long serialVersionUID = 1L;
    
    +  private static final Logger LOGGER = Logger.getLogger(BadgeSummaryAction.class.getName());
    
    +
    
       private final String html;
    
     
    
       private HtmlBadgeAction(String html) {
    
    @@ -52,9 +59,17 @@ public String getIconFileName() {
         return null;
    
       }
    
     
    
    -  @Exported
    
    -  public String getHtml() {
    
    +  public String getRawHtml() {
    
         return html;
    
       }
    
     
    
    +  @Exported
    
    +  public String getHtml() {
    
    +    try {
    
    +      return new RawHtmlMarkupFormatter(false).translate(html);
    
    +    } catch (IOException e) {
    
    +      LOGGER.log(Level.WARNING, "Error preparing html content for ui", e);
    
    +      return "<b><font color=\"red\">ERROR</font></b>";
    
    +    }
    
    +  }
    
     }
    
    
  • src/test/java/com/jenkinsci/plugins/badge/dsl/AddHtmlBadgeStepTest.java+14 1 modified
    @@ -40,6 +40,18 @@ public class AddHtmlBadgeStepTest extends AbstractBadgeTest {
       @Test
    
       public void addHtmlBadge() throws Exception {
    
         String html = UUID.randomUUID().toString();
    
    +    testAddHtmlBadge(html, html);
    
    +  }
    
    +
    
    +  @Test
    
    +  public void addHtmlBadge_remove_script() throws Exception {
    
    +    String uuid = UUID.randomUUID().toString();
    
    +    String html = uuid + "<script>alert('exploit!');</script>";
    
    +    testAddHtmlBadge(html, uuid);
    
    +  }
    
    +
    
    +
    
    +  private void testAddHtmlBadge(String html, String expected) throws Exception {
    
         WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
    
     
    
         String script = "addHtmlBadge(\"" + html + "\")";
    
    @@ -51,6 +63,7 @@ public void addHtmlBadge() throws Exception {
         assertEquals(1, badgeActions.size());
    
     
    
         HtmlBadgeAction action = (HtmlBadgeAction) badgeActions.get(0);
    
    -    assertEquals(html, action.getHtml());
    
    +    assertEquals(expected, action.getHtml());
    
    +    assertEquals(html, action.getRawHtml());
    
       }
    
     }
    
    
  • src/test/java/com/jenkinsci/plugins/badge/dsl/CreateSummaryStepTest.java+10 1 modified
    @@ -51,6 +51,15 @@ public void createSummary_html_unescaped() throws Exception {
         assertEquals("<li>" + text + "</li>", action.getText());
    
       }
    
     
    
    +  @Test
    
    +  public void createSummary_html_unescaped_remove_script() throws Exception {
    
    +    String text = randomUUID().toString();
    
    +    String html = "<li>" + text + "</li><script>alert(\"exploit!\");</script>";
    
    +    BadgeSummaryAction action = createSummary("summary.appendText('" + html + "', false);");
    
    +    assertEquals("<li>" + text + "</li>", action.getText());
    
    +    assertEquals(html, action.getRawText());
    
    +  }
    
    +
    
       @Test
    
       public void createSummary_html_escaped() throws Exception {
    
         String text = randomUUID().toString();
    
    @@ -85,7 +94,7 @@ public void createSummary_with_text() throws Exception {
         String text = randomUUID().toString();
    
     
    
         WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
    
    -    p.setDefinition(new CpsFlowDefinition("def summary = createSummary(icon:\"" + icon + "\", text:\""+text+"\")", true));
    
    +    p.setDefinition(new CpsFlowDefinition("def summary = createSummary(icon:\"" + icon + "\", text:\"" + text + "\")", true));
    
         WorkflowRun b = r.assertBuildStatusSuccess(p.scheduleBuild2(0));
    
         List<BadgeSummaryAction> summaryActions = b.getActions(BadgeSummaryAction.class);
    
         assertEquals(1, summaryActions.size());
    
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.