Moderate severityNVD Advisory· Published Oct 23, 2019· Updated Aug 4, 2024
CVE-2019-10459
CVE-2019-10459
Description
Jenkins Mattermost Notification Plugin 2.7.0 and earlier stored webhook URLs containing a secret token unencrypted in its global configuration file and job config.xml files on the Jenkins master where they could be viewed by users with Extended Read permission, or access to the master file system.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.jenkins-ci.plugins:mattermostMaven | < 2.7.1 | 2.7.1 |
Affected products
1- Range: 2.7.0 and earlier
Patches
1c6e509307812Change type of endpoint to Secret
5 files changed · +59 −44
pom.xml+18 −26 modified@@ -29,9 +29,8 @@ <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <workflow.version>1.11</workflow.version> <hamcrest.version>1.3</hamcrest.version> - <powermock.version>1.6.2</powermock.version> <jenkins.version>2.0</jenkins.version> - <java.level>7</java.level> + <java.level>8</java.level> <jenkins-test-harness.version>2.13</jenkins-test-harness.version> </properties> @@ -157,34 +156,20 @@ <scope>test</scope> </dependency> <dependency> - <groupId>org.mockito</groupId> - <artifactId>mockito-all</artifactId> - <version>1.10.19</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.powermock</groupId> - <artifactId>powermock-core</artifactId> - <version>${powermock.version}</version> - <scope>test</scope> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>1.10.19</version> + <scope>test</scope> </dependency> <dependency> - <groupId>org.powermock</groupId> - <artifactId>powermock-module-junit4</artifactId> - <version>${powermock.version}</version> - <scope>test</scope> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-junit4</artifactId> + <version>1.6.2</version> </dependency> <dependency> - <groupId>org.powermock</groupId> - <artifactId>powermock-api-mockito</artifactId> - <version>${powermock.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.powermock</groupId> - <artifactId>powermock-reflect</artifactId> - <version>${powermock.version}</version> - <scope>test</scope> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito</artifactId> + <version>1.6.2</version> </dependency> </dependencies> @@ -205,6 +190,13 @@ <compatibleSinceVersion>2.4.0</compatibleSinceVersion> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <trimStackTrace>false</trimStackTrace> + </configuration> + </plugin> </plugins> </build>
src/main/java/jenkins/plugins/mattermost/MattermostNotifier.java+22 −13 modified@@ -20,6 +20,7 @@ import hudson.tasks.Publisher; import hudson.util.FormValidation; import hudson.util.ListBoxModel; +import hudson.util.Secret; import jenkins.model.Jenkins; import jenkins.model.JenkinsLocationConfiguration; import net.sf.json.JSONObject; @@ -39,7 +40,7 @@ public class MattermostNotifier extends Notifier { private static final Logger logger = Logger.getLogger(MattermostNotifier.class.getName()); - private String endpoint; + private Secret endpoint; private String buildServerUrl; private String room; private String icon; @@ -65,7 +66,7 @@ public DescriptorImpl getDescriptor() { return (DescriptorImpl)super.getDescriptor(); } - public String getEndpoint() { + public Secret getEndpoint() { return endpoint; } @@ -148,8 +149,8 @@ public String getCustomMessage() { return customMessage; } - public void setEndpoint(@CheckForNull String endpoint) { - this.endpoint = fixNull(endpoint); + public void setEndpoint(String endpoint) { + this.endpoint = Secret.fromString(endpoint); } @DataBoundSetter @@ -243,7 +244,7 @@ public void setCustomMessage(@CheckForNull String customMessage) { } @DataBoundConstructor - public MattermostNotifier(final String endpoint, final String room, final String icon, final String buildServerUrl, + public MattermostNotifier(final Secret endpoint, final String room, final String icon, final String buildServerUrl, final String sendAs, final boolean startNotification, final boolean notifyAborted, final boolean notifyFailure, final boolean notifyNotBuilt, final boolean notifySuccess, final boolean notifyUnstable, final boolean notifyBackToNormal, final boolean notifyRepeatedFailure, final boolean includeTestSummary, CommitInfoChoice commitInfoChoice, @@ -284,9 +285,9 @@ public BuildStepMonitor getRequiredMonitorService() { } public MattermostService newMattermostService(AbstractBuild r, BuildListener listener) { - String endpoint = this.endpoint; + String endpoint = Secret.toString(this.getEndpoint()); if (StringUtils.isEmpty(endpoint)) { - endpoint = getDescriptor().getEndpoint(); + endpoint = Secret.toString(getDescriptor().getEndpoint()); } String room = this.room; if (StringUtils.isEmpty(room)) { @@ -334,7 +335,7 @@ public boolean prebuild(AbstractBuild<?, ?> build, BuildListener listener) { @Extension @Symbol("mattermostNotifier") public static class DescriptorImpl extends BuildStepDescriptor<Publisher> { - private String endpoint; + private Secret endpoint; private String room; private String icon; private String buildServerUrl; @@ -344,12 +345,20 @@ public DescriptorImpl() { load(); } - @DataBoundSetter public void setEndpoint(String endpoint) { + if (endpoint == null) { + this.endpoint = null; + return; + } + this.setEndpoint(Secret.fromString(endpoint)); + } + + @DataBoundSetter + public void setEndpoint(Secret endpoint) { this.endpoint = endpoint; } - public String getEndpoint() { + public Secret getEndpoint() { return endpoint; } @@ -430,7 +439,7 @@ public FormValidation doTestConnection(@QueryParameter("endpoint") final String try { String targetEndpoint = endpoint; if (StringUtils.isEmpty(targetEndpoint)) { - targetEndpoint = this.endpoint; + targetEndpoint = Secret.toString(this.getEndpoint()); } String targetRoom = room; if (StringUtils.isEmpty(targetRoom)) { @@ -635,8 +644,8 @@ public void onLoaded() { } else { logger.info(String.format("Starting migration for \"%s\"", p.getName())); //map settings - if (StringUtils.isBlank(mattermostNotifier.endpoint)) { - mattermostNotifier.endpoint = mattermostJobProperty.getEndpoint(); + if (StringUtils.isBlank(Secret.toString(mattermostNotifier.getEndpoint()))) { + mattermostNotifier.setEndpoint(mattermostJobProperty.getEndpoint()); } if (StringUtils.isBlank(mattermostNotifier.icon)) { mattermostNotifier.icon = mattermostJobProperty.getIcon();
src/main/java/jenkins/plugins/mattermost/workflow/MattermostSendStep.java+2 −1 modified@@ -3,6 +3,7 @@ import hudson.AbortException; import hudson.Extension; import hudson.Util; +import hudson.util.Secret; import hudson.model.TaskListener; import jenkins.model.Jenkins; import jenkins.plugins.mattermost.*; @@ -136,7 +137,7 @@ protected Void run() throws Exception { return null; } MattermostNotifier.DescriptorImpl mattermostDesc = jenkins.getDescriptorByType(MattermostNotifier.DescriptorImpl.class); - String team = step.endpoint != null ? step.endpoint : mattermostDesc.getEndpoint(); + String team = step.getEndpoint() != null ? step.getEndpoint() : Secret.toString(mattermostDesc.getEndpoint()); String channel = step.channel != null ? step.channel : mattermostDesc.getRoom(); String icon = step.icon != null ? step.icon : mattermostDesc.getIcon(); String color = step.color != null ? step.color : "";
src/test/java/jenkins/plugins/mattermost/MattermostNotifierStub.java+3 −1 modified@@ -1,13 +1,15 @@ package jenkins.plugins.mattermost; +import hudson.util.Secret; + public class MattermostNotifierStub extends MattermostNotifier { public MattermostNotifierStub(String host, String room, String icon, String buildServerUrl, String sendAs, boolean startNotification, boolean notifyAborted, boolean notifyFailure, boolean notifyNotBuilt, boolean notifySuccess, boolean notifyUnstable, boolean notifyBackToNormal, boolean notifyRepeatedFailure, boolean includeTestSummary, CommitInfoChoice commitInfoChoice, boolean includeCustomAttachmentMessage, String customAttachmentMessage,boolean includeCustomMessage,String customMessage) { - super(host, room, icon, buildServerUrl, sendAs, startNotification, notifyAborted, notifyFailure, + super(host != null ? Secret.fromString(host) : null, room, icon, buildServerUrl, sendAs, startNotification, notifyAborted, notifyFailure, notifyNotBuilt, notifySuccess, notifyUnstable, notifyBackToNormal, notifyRepeatedFailure, includeTestSummary, commitInfoChoice, includeCustomAttachmentMessage, customAttachmentMessage, includeCustomMessage, customMessage); }
src/test/java/jenkins/plugins/mattermost/workflow/MattermostSendStepTest.java+14 −3 modified@@ -1,16 +1,20 @@ package jenkins.plugins.mattermost.workflow; import hudson.model.TaskListener; +import hudson.util.Secret; import jenkins.model.Jenkins; import jenkins.plugins.mattermost.MattermostNotifier; import jenkins.plugins.mattermost.MattermostService; +import jenkins.security.ConfidentialStore; import org.jenkinsci.plugins.workflow.steps.StepContext; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Matchers; import org.mockito.Mock; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.modules.junit4.PowerMockRunner; import java.io.PrintStream; @@ -25,7 +29,8 @@ * Traditional Unit tests, allows testing null Jenkins,getInstance() */ @RunWith(PowerMockRunner.class) -@PrepareForTest({Jenkins.class, MattermostSendStep.class}) +@PrepareForTest({Jenkins.class, ConfidentialStore.class, MattermostSendStep.class}) +@PowerMockIgnore({"javax.crypto.*" }) // https://github.com/powermock/powermock/issues/294 public class MattermostSendStepTest { @Mock @@ -88,7 +93,13 @@ public void testValuesForGlobalConfig() throws Exception { stepExecution.listener = taskListenerMock; - when(mattermostDescMock.getEndpoint()).thenReturn("globalEndpoint"); + PowerMockito.mockStatic(ConfidentialStore.class); + ConfidentialStore csMock = mock(ConfidentialStore.class); + when(ConfidentialStore.get()).thenReturn(csMock); + when(csMock.randomBytes(Matchers.anyInt())).thenAnswer( it -> new byte[ (Integer)(it.getArguments()[0])] ); + + Secret encryptedEndpoint = Secret.fromString("globalEndpoint"); + when(mattermostDescMock.getEndpoint()).thenReturn(encryptedEndpoint); when(mattermostDescMock.getIcon()).thenReturn("globalIcon"); when(mattermostDescMock.getRoom()).thenReturn("globalChannel"); @@ -128,7 +139,7 @@ public void testNonNullEmptyColor() throws Exception { verify(mattermostServiceMock, times(1)).publish("message", "", ""); assertNull(stepExecution.step.getColor()); } - + @Test public void testNonNullPretext() throws Exception {
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-xcj6-4355-2823ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-10459ghsaADVISORY
- www.openwall.com/lists/oss-security/2019/10/23/2ghsamailing-listx_refsource_MLISTWEB
- github.com/jenkinsci/mattermost-plugin/commit/c6e509307812d93ba295a35dea95016f007de158ghsaWEB
- jenkins.io/security/advisory/2019-10-23/ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.