CVE-2019-10335
Description
A stored cross site scripting vulnerability in Jenkins ElectricFlow Plugin 1.1.5 and earlier allowed attackers able to configure jobs in Jenkins or control the output of the ElectricFlow API to inject arbitrary HTML and JavaScript in the plugin-provided output on build status pages.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Jenkins ElectricFlow Plugin 1.1.5 and earlier had a stored XSS vulnerability allowing attackers to inject arbitrary HTML/JavaScript via job configuration or API output.
Vulnerability
Overview
The Jenkins ElectricFlow Plugin, in versions 1.1.5 and earlier, contains a stored cross-site scripting (XSS) vulnerability. The root cause is insufficient escaping of user-controlled data when rendering output on build status pages. The plugin directly appended parameters, names, and values from job configurations or ElectricFlow API responses into HTML without proper encoding, as shown by the fix that introduced HtmlUtils.encodeForHtml() calls [4].
Exploitation and
Attack Surface
An attacker can exploit this by either having the ability to configure jobs in Jenkins (e.g., as a user with Job/Configure permission) or by controlling the output of the ElectricFlow API, which the plugin displays. This does not require authentication to the ElectricFlow service directly, but rather leverages Jenkins' own permissions or external API manipulation. The attack is low-complexity, requires no special privileges beyond job configuration, and is network-based [1][2].
Impact
Successful exploitation allows the attacker to inject arbitrary HTML and JavaScript into the build status page. When other users or administrators view the affected build status page, the injected script executes in their browser session within the Jenkins context. This can lead to session hijacking, credential theft, defacement, or further malicious actions against the Jenkins instance. The vulnerability is classified as medium severity with a CVSSv3 score of 6.1 [1][2].
Mitigation
Status
The vulnerability was fixed in ElectricFlow Plugin version 1.1.7, released on June 11, 2019 [3]. Users should upgrade to version 1.1.7 or later immediately. As of now, there is no evidence of active exploitation in the wild, and the vulnerability is not listed on the CISA Known Exploited Vulnerabilities (KEV) catalog.
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.
| Package | Affected versions | Patched versions |
|---|---|---|
org.jenkins-ci.plugins:electricflowMaven | < 1.1.7 | 1.1.7 |
Affected products
2- Jenkins project/Jenkins ElectricFlow Pluginv5Range: 1.1.5 and earlier
Patches
11a90ee7727f8[SECURITY-1412]
11 files changed · +76 −37
pom.xml+10 −0 modified@@ -95,5 +95,15 @@ <artifactId>jackson-xml-databind</artifactId> <version>0.6.2</version> </dependency> + <dependency> + <groupId>org.owasp.encoder</groupId> + <artifactId>encoder</artifactId> + <version>1.2.2</version> + </dependency> + <dependency> + <groupId>com.googlecode.owasp-java-html-sanitizer</groupId> + <artifactId>owasp-java-html-sanitizer</artifactId> + <version>20190503.1</version> + </dependency> </dependencies> </project>
src/main/java/org/jenkinsci/plugins/electricflow/ElectricFlowDeployApplication.java+3 −2 modified@@ -28,6 +28,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jenkinsci.plugins.electricflow.ui.FieldValidationStatus; +import org.jenkinsci.plugins.electricflow.ui.HtmlUtils; import org.jenkinsci.plugins.electricflow.ui.SelectFieldUtils; import org.jenkinsci.plugins.electricflow.ui.SelectItemValidationWrapper; import org.kohsuke.stapler.DataBoundConstructor; @@ -224,12 +225,12 @@ private String getSummaryHtml( + "<table cellspacing=\"2\" cellpadding=\"4\"> \n" + " <tr>\n" + " <td>Application Name:</td>\n" - + " <td><a href='" + applicationUrl + "'>" + applicationName + + " <td><a href='" + HtmlUtils.encodeForHtml(applicationUrl) + "'>" + HtmlUtils.encodeForHtml(applicationName) + "</a></td> \n" + " </tr>\n" + " <tr>\n" + " <td>Deploy run URL:</td>\n" - + " <td><a href='" + deployRunUrl + "'>" + deployRunUrl + + " <td><a href='" + HtmlUtils.encodeForHtml(deployRunUrl) + "'>" + HtmlUtils.encodeForHtml(deployRunUrl) + "</a></td> \n" + " </tr>";
src/main/java/org/jenkinsci/plugins/electricflow/ElectricFlowGenericRestApi.java+7 −6 modified@@ -15,6 +15,7 @@ import javax.annotation.Nonnull; +import org.jenkinsci.plugins.electricflow.ui.HtmlUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.DataBoundSetter; @@ -130,12 +131,12 @@ private String getSummaryHtml( + "<table cellspacing=\"2\" cellpadding=\"4\"> \n" + " <tr>\n" + " <td style='width:20%;'>URL Path:</td>\n" - + " <td><a href='" + url + "'>" + url + + " <td><a href='" + HtmlUtils.encodeForHtml(url) + "'>" + HtmlUtils.encodeForHtml(url) + "</a></td> \n" + " </tr>\n" + " <tr>\n" + " <td>HTTP Method:</td>\n" - + " <td>" + httpMethod + "</td> \n" + + " <td>" + HtmlUtils.encodeForHtml(httpMethod) + "</td> \n" + " </tr>\n"; if (!HttpMethod.GET.equals(HttpMethod.valueOf(httpMethod))) { @@ -151,10 +152,10 @@ private String getSummaryHtml( for (Pair pair : parameters) { strBuilder.append(" <tr>\n" + " <td> ") - .append(pair.getKey()) + .append(HtmlUtils.encodeForHtml(pair.getKey())) .append(":</td>\n" + " <td>") - .append(pair.getValue()) + .append(HtmlUtils.encodeForHtml(pair.getValue())) .append("</td> \n" + " </tr>\n"); } @@ -164,14 +165,14 @@ private String getSummaryHtml( else if (!body.isEmpty()) { summaryText = summaryText + " <tr>\n" + " <td>Body:</td>\n" - + " <td>" + formatJsonOutput(body) + "</td> \n" + + " <td>" + HtmlUtils.encodeForHtml(formatJsonOutput(body)) + "</td> \n" + " </tr>\n"; } } summaryText = summaryText + " <tr>\n" + " <td>Result:</td>\n" - + " <td><pre>" + formatJsonOutput(result) + + " <td><pre>" + HtmlUtils.encodeForHtml(formatJsonOutput(result)) + "</pre></td> \n" + " </tr>\n"; summaryText = summaryText + "</table>";
src/main/java/org/jenkinsci/plugins/electricflow/ElectricFlowPipelinePublisher.java+4 −3 modified@@ -21,6 +21,7 @@ import org.apache.commons.logging.LogFactory; import org.jenkinsci.plugins.electricflow.ui.FieldValidationStatus; +import org.jenkinsci.plugins.electricflow.ui.HtmlUtils; import org.jenkinsci.plugins.electricflow.ui.SelectFieldUtils; import org.jenkinsci.plugins.electricflow.ui.SelectItemValidationWrapper; import org.kohsuke.stapler.DataBoundConstructor; @@ -284,16 +285,16 @@ private String getSummaryHtml( + "<table cellspacing=\"2\" cellpadding=\"4\"> \n" + " <tr>\n" + " <td>Pipeline URL:</td>\n" - + " <td><a href='" + url + "'>" + url + "</a></td> \n" + + " <td><a href='" + HtmlUtils.encodeForHtml(url) + "'>" + HtmlUtils.encodeForHtml(url) + "</a></td> \n" + " </tr>\n" + " <tr>\n" + " <td>Pipeline Name:</td>\n" - + " <td><a href='" + url + "'>" + pipelineName + + " <td><a href='" + HtmlUtils.encodeForHtml(url) + "'>" + HtmlUtils.encodeForHtml(pipelineName) + "</a></td> \n" + " </tr>\n" + " <tr>\n" + " <td>Project Name:</td>\n" - + " <td>" + projectName + "</td> \n" + + " <td>" + HtmlUtils.encodeForHtml(projectName) + "</td> \n" + " </tr>"; summaryText = Utils.getParametersHTML(parameters, summaryText,
src/main/java/org/jenkinsci/plugins/electricflow/ElectricFlowPublishApplication.java+6 −6 modified@@ -26,6 +26,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jenkinsci.plugins.electricflow.ui.HtmlUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -210,7 +211,7 @@ private String getSummaryHtml( + "<table cellspacing=\"2\" cellpadding=\"4\"> \n" + " <tr>\n" + " <td>Application URL:</td>\n" - + " <td><a href='" + url + "'>" + url + "</a></td> \n" + + " <td><a href='" + HtmlUtils.encodeForHtml(url) + "'>" + HtmlUtils.encodeForHtml(url) + "</a></td> \n" + " </tr>\n"; if (!zipFiles.isEmpty()) { @@ -238,8 +239,7 @@ private String getSummaryHtml( byte[] encoded = Files.readAllBytes(Paths.get( manifestPath)); - jsonContent = "<pre>" + new String(encoded, "UTF-8") - + "</pre>"; + jsonContent = new String(encoded, "UTF-8"); } catch (IOException e) { logger.println( @@ -253,7 +253,7 @@ private String getSummaryHtml( strBuilder.append(" <tr>\n" + " <td> ") - .append(fileName) + .append(HtmlUtils.encodeForHtml(fileName)) .append("</td>\n" + " <td>") .append("</td> \n" @@ -263,10 +263,10 @@ private String getSummaryHtml( if (!jsonContent.isEmpty()) { strBuilder.append(" <tr>\n" + " <td> ") - .append(MANIFEST_NAME) + .append(HtmlUtils.encodeForHtml(MANIFEST_NAME)) .append("</td>\n" + " <td>") - .append(jsonContent) + .append("<pre>").append(HtmlUtils.encodeForHtml(jsonContent)).append("</pre>") .append("</td> \n" + " </tr>\n"); }
src/main/java/org/jenkinsci/plugins/electricflow/ElectricFlowRunProcedure.java+2 −1 modified@@ -28,6 +28,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jenkinsci.plugins.electricflow.ui.FieldValidationStatus; +import org.jenkinsci.plugins.electricflow.ui.HtmlUtils; import org.jenkinsci.plugins.electricflow.ui.SelectFieldUtils; import org.jenkinsci.plugins.electricflow.ui.SelectItemValidationWrapper; import org.kohsuke.stapler.DataBoundConstructor; @@ -166,7 +167,7 @@ private String getSummaryHtml( + "<table cellspacing=\"2\" cellpadding=\"4\"> \n" + " <tr>\n" + " <td>Procedure Name:</td>\n" - + " <td><a href='" + jobUrl + "'>" + procedureName + "</a></td> \n" + + " <td><a href='" + HtmlUtils.encodeForHtml(jobUrl) + "'>" + HtmlUtils.encodeForHtml(procedureName) + "</a></td> \n" + " </tr>"; summaryText = Utils.getParametersHTML(parameters, summaryText,
src/main/java/org/jenkinsci/plugins/electricflow/ElectricFlowTriggerRelease.java+6 −5 modified@@ -27,6 +27,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jenkinsci.plugins.electricflow.ui.FieldValidationStatus; +import org.jenkinsci.plugins.electricflow.ui.HtmlUtils; import org.jenkinsci.plugins.electricflow.ui.SelectFieldUtils; import org.jenkinsci.plugins.electricflow.ui.SelectItemValidationWrapper; import org.kohsuke.stapler.DataBoundConstructor; @@ -210,28 +211,28 @@ private String getSummaryHtml( + "<table cellspacing=\"2\" cellpadding=\"4\"> \n" + " <tr>\n" + " <td>Release Name:</td>\n" - + " <td><a href='" + urlRelease + "'>" + releaseName + + " <td><a href='" + HtmlUtils.encodeForHtml(urlRelease) + "'>" + HtmlUtils.encodeForHtml(releaseName) + "</a></td> \n" + " </tr>\n" + " <tr>\n" + " <td>Pipeline URL:</td>\n" - + " <td><a href='" + urlPipeline + "'>" + urlPipeline + + " <td><a href='" + HtmlUtils.encodeForHtml(urlPipeline) + "'>" + HtmlUtils.encodeForHtml(urlPipeline) + "</a></td> \n" + " </tr>\n" + " <tr>\n" + " <td>Pipeline Name:</td>\n" - + " <td><a href='" + urlPipeline + "'>" + pipelineName + + " <td><a href='" + HtmlUtils.encodeForHtml(urlPipeline) + "'>" + HtmlUtils.encodeForHtml(pipelineName) + "</a></td> \n" + " </tr>\n" + " <tr>\n" + " <td>Project Name:</td>\n" - + " <td>" + projectName + "</td> \n" + + " <td>" + HtmlUtils.encodeForHtml(projectName) + "</td> \n" + " </tr>"; if (!startingStage.isEmpty()) { summaryText = summaryText + " <tr>\n" + " <td>Starting stage:</td>\n" - + " <td>" + startingStage + "</td> \n" + + " <td>" + HtmlUtils.encodeForHtml(startingStage) + "</td> \n" + " </tr>"; }
src/main/java/org/jenkinsci/plugins/electricflow/ElectricFlowUploadArtifactPublisher.java+5 −4 modified@@ -19,6 +19,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.jenkinsci.plugins.electricflow.ui.HtmlUtils; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -211,19 +212,19 @@ private String getSummaryHtml( + "<table cellspacing=\"2\" cellpadding=\"4\">\n" + " <tr>\n" + " <td>Artifact URL:</td>\n" - + " <td><a href ='" + url + "'>" + url + "</a></td> \n" + + " <td><a href ='" + HtmlUtils.encodeForHtml(url) + "'>" + HtmlUtils.encodeForHtml(url) + "</a></td> \n" + " </tr>\n" + " <tr>\n" + " <td>Artifact Name:</td>\n" - + " <td><a href ='" + url + "'>" + artifactName + "</a></td> \n" + + " <td><a href ='" + HtmlUtils.encodeForHtml(url) + "'>" + HtmlUtils.encodeForHtml(artifactName) + "</a></td> \n" + " </tr>\n" + " <tr>\n" + " <td>Artifact Version:</td>\n" - + " <td>" + newArtifactVersion + "</td> \n" + + " <td>" + HtmlUtils.encodeForHtml(newArtifactVersion) + "</td> \n" + " </tr>\n" + " <tr>\n" + " <td>Repository Name:</td>\n" - + " <td>" + repository + "</td> \n" + + " <td>" + HtmlUtils.encodeForHtml(repository) + "</td> \n" + " </tr>\n" + "</table>"; }
src/main/java/org/jenkinsci/plugins/electricflow/SummaryTextAction.java+4 −3 modified@@ -18,6 +18,8 @@ import jenkins.tasks.SimpleBuildStep; +import static org.jenkinsci.plugins.electricflow.ui.HtmlUtils.getHtmlPolicy; + public class SummaryTextAction implements Action, SimpleBuildStep.LastBuildAction @@ -66,9 +68,8 @@ public SummaryTextAction( return this.run; } - public String getSummaryText() - { - return this.summaryText; + public String getSummaryText() { + return getHtmlPolicy().sanitize(this.summaryText); } @Override public String getUrlName()
src/main/java/org/jenkinsci/plugins/electricflow/ui/HtmlUtils.java+21 −0 added@@ -0,0 +1,21 @@ +package org.jenkinsci.plugins.electricflow.ui; + +import org.owasp.encoder.Encode; +import org.owasp.html.HtmlPolicyBuilder; +import org.owasp.html.PolicyFactory; + +public class HtmlUtils { + public static PolicyFactory getHtmlPolicy() { + return new HtmlPolicyBuilder() + .allowElements("h3", "table", "tr", "td", "a", "b", "pre") + .allowAttributes("href").onElements("a") + .allowAttributes("cellspacing", "cellpadding").onElements("table") + .allowStyling() + .allowStandardUrlProtocols() + .toFactory(); + } + + public static String encodeForHtml(String input) { + return Encode.forHtml(input); + } +}
src/main/java/org/jenkinsci/plugins/electricflow/Utils.java+8 −7 modified@@ -27,6 +27,7 @@ import jenkins.model.GlobalConfiguration; import org.jenkinsci.plugins.electricflow.ui.FieldValidationStatus; +import org.jenkinsci.plugins.electricflow.ui.HtmlUtils; import org.jenkinsci.plugins.electricflow.ui.SelectFieldUtils; import org.jenkinsci.plugins.electricflow.ui.SelectItemValidationWrapper; @@ -196,7 +197,7 @@ public static String getParametersHTML( for (String param : parameters) { strBuilder.append(" <tr>\n" + " <td> ") - .append(param) + .append(HtmlUtils.encodeForHtml(param)) .append("</td>\n") .append(" </tr>\n"); } @@ -229,10 +230,10 @@ public static String getParametersHTML( strBuilder.append(" <tr>\n" + " <td> ") - .append(name) + .append(HtmlUtils.encodeForHtml(name)) .append(":</td>\n" + " <td>") - .append(value) + .append(HtmlUtils.encodeForHtml(value)) .append("</td> \n" + " </tr>\n"); } @@ -347,17 +348,17 @@ public static String getValidationComparisonRow(String parameterName, Object old if (!newValue.equals(oldValue)) { rowStyleAttr = "style=\"background-color: #e2db0c;\""; } - return "<tr " + rowStyleAttr + "><td>" + parameterName + "</td><td>" + oldValue + "</td><td>" + newValue + "</td></tr>"; + return "<tr " + rowStyleAttr + "><td>" + HtmlUtils.encodeForHtml(parameterName) + "</td><td>" + HtmlUtils.encodeForHtml(String.valueOf(oldValue)) + "</td><td>" + HtmlUtils.encodeForHtml(String.valueOf(newValue)) + "</td></tr>"; } public static String getValidationComparisonRowOldParam(String parameterName, Object oldValue) { String rowStyleAttr = "style=\"background-color: #fa9a76;\""; - return "<tr " + rowStyleAttr + "><td>" + parameterName + "</td><td>" + oldValue + "</td><td></td></tr>"; + return "<tr " + rowStyleAttr + "><td>" + HtmlUtils.encodeForHtml(parameterName) + "</td><td>" + HtmlUtils.encodeForHtml(String.valueOf(oldValue)) + "</td><td></td></tr>"; } public static String getValidationComparisonRowNewParam(String parameterName, Object newValue) { String rowStyleAttr = "style=\"background-color: #82dc84;\""; - return "<tr " + rowStyleAttr + "><td>" + parameterName + "</td><td></td><td>" + newValue + "</td></tr>"; + return "<tr " + rowStyleAttr + "><td>" + HtmlUtils.encodeForHtml(parameterName) + "</td><td></td><td>" + HtmlUtils.encodeForHtml(String.valueOf(newValue)) + "</td></tr>"; } public static String getValidationComparisonRowsForExtraParameters(String sectionName, Map<String, String> oldParamsMap, Map<String, String> newParamsMap) { @@ -367,7 +368,7 @@ public static String getValidationComparisonRowsForExtraParameters(String sectio StringBuilder rows = new StringBuilder(); rows.append("<tr><td></td><td></td><td></td></tr>"); - rows.append("<tr><td>" + sectionName + "</td><td></td><td></td></tr>"); + rows.append("<tr><td>" + HtmlUtils.encodeForHtml(sectionName) + "</td><td></td><td></td></tr>"); Set<String> oldKeysSet = new HashSet<String>(oldParamsMap.keySet()); oldKeysSet.removeAll(newParamsMap.keySet());
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-fx9p-2qvx-pgjvghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-10335ghsaADVISORY
- www.openwall.com/lists/oss-security/2019/06/11/1ghsamailing-listx_refsource_MLISTWEB
- www.securityfocus.com/bid/108747mitrevdb-entryx_refsource_BID
- github.com/jenkinsci/electricflow-plugin/commit/1a90ee7727f8c6925df3e410837ddf6be28cce53ghsaWEB
- jenkins.io/security/advisory/2019-06-11/ghsax_refsource_CONFIRMWEB
- web.archive.org/web/20200227033720/http://www.securityfocus.com/bid/108747ghsaWEB
News mentions
0No linked articles in our index yet.