VYPR
Moderate severityNVD Advisory· Published Jan 12, 2022· Updated Aug 3, 2024

CVE-2022-23109

CVE-2022-23109

Description

Jenkins HashiCorp Vault Plugin 3.7.0 and earlier fails to mask Vault credentials in Pipeline build logs and step descriptions when Pipeline: Groovy Plugin 2.85+ is installed.

AI Insight

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

Jenkins HashiCorp Vault Plugin 3.7.0 and earlier fails to mask Vault credentials in Pipeline build logs and step descriptions when Pipeline: Groovy Plugin 2.85+ is installed.

Vulnerability

Jenkins HashiCorp Vault Plugin 3.7.0 and earlier does not mask Vault credentials in Pipeline build logs or in Pipeline step descriptions when Pipeline: Groovy Plugin 2.85 or later is installed. The bug affects the plugin's log masking functionality, which is supposed to prevent secrets from appearing in build output but fails under these conditions [1]. Versions before 3.8.0 are affected [3].

Exploitation

An attacker must have access to view Pipeline build logs or step descriptions (e.g., via Jenkins UI or API) and must have a Pipeline job that uses Vault credentials. No special network position or additional authentication beyond standard job visibility is required. The attacker can simply view the build output where credentials are printed in plaintext [1].

Impact

Successful exploitation leads to unauthorized disclosure of Vault credentials (tokens, secret IDs, etc.) that are used by the Jenkins job. This can allow the attacker to access Vault secrets that the Jenkins instance has access to, potentially leading to further compromise of systems or data managed by Vault [1].

Mitigation

Update Jenkins HashiCorp Vault Plugin to version 3.8.0 or later, which contains the fix to properly mask credentials in Pipeline build logs and step descriptions [3]. The fix was released on 2022-01-12 as part of the Jenkins Security Advisory [1]. Users unable to update immediately should ensure that Pipeline: Groovy Plugin is not installed or is kept at a version below 2.85, though this may not be a complete workaround [1][4].

AI Insight generated on May 21, 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
com.datapipe.jenkins.plugins:hashicorp-vault-pluginMaven
< 3.8.03.8.0

Affected products

2

Patches

1
c7ad9779320b

[JENKINS-64338] - mask credentials also outside of log (#174)

https://github.com/jenkinsci/hashicorp-vault-pluginDietmar ScheidlApr 7, 2021via ghsa
9 files changed · +443 185
  • pom.xml+0 1 modified
    @@ -194,7 +194,6 @@
         <dependency>
           <groupId>org.jenkins-ci.plugins.workflow</groupId>
           <artifactId>workflow-step-api</artifactId>
    -      <scope>test</scope>
         </dependency>
         <dependency>
           <groupId>org.jenkins-ci.plugins.workflow</groupId>
    
  • src/main/java/com/datapipe/jenkins/vault/VaultAccessor.java+170 0 modified
    @@ -3,11 +3,38 @@
     import com.bettercloud.vault.Vault;
     import com.bettercloud.vault.VaultConfig;
     import com.bettercloud.vault.VaultException;
    +import com.bettercloud.vault.json.Json;
    +import com.bettercloud.vault.json.JsonArray;
    +import com.bettercloud.vault.json.JsonValue;
     import com.bettercloud.vault.response.LogicalResponse;
     import com.bettercloud.vault.response.VaultResponse;
    +import com.bettercloud.vault.rest.RestResponse;
    +import com.cloudbees.plugins.credentials.CredentialsMatchers;
    +import com.cloudbees.plugins.credentials.CredentialsProvider;
    +import com.cloudbees.plugins.credentials.CredentialsUnavailableException;
    +import com.cloudbees.plugins.credentials.matchers.IdMatcher;
    +import com.datapipe.jenkins.vault.configuration.VaultConfigResolver;
    +import com.datapipe.jenkins.vault.configuration.VaultConfiguration;
     import com.datapipe.jenkins.vault.credentials.VaultCredential;
     import com.datapipe.jenkins.vault.exception.VaultPluginException;
    +import com.datapipe.jenkins.vault.model.VaultSecret;
    +import com.datapipe.jenkins.vault.model.VaultSecretValue;
    +import hudson.EnvVars;
    +import hudson.ExtensionList;
    +import hudson.Util;
    +import hudson.model.Run;
    +import hudson.security.ACL;
    +import java.io.PrintStream;
     import java.io.Serializable;
    +import java.nio.charset.StandardCharsets;
    +import java.util.Collections;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Optional;
    +import java.util.stream.Collectors;
    +import jenkins.model.Jenkins;
    +import org.apache.commons.lang.StringUtils;
     
     public class VaultAccessor implements Serializable {
     
    @@ -102,4 +129,147 @@ public VaultResponse revoke(String leaseId) {
                     "could not revoke vault lease (" + leaseId + "):" + e.getMessage());
             }
         }
    +
    +    public static Map<String, String> retrieveVaultSecrets(Run<?,?> run, PrintStream logger, EnvVars envVars, VaultAccessor vaultAccessor, VaultConfiguration initialConfiguration, List<VaultSecret> vaultSecrets) {
    +        Map<String, String> overrides = new HashMap<>();
    +
    +        VaultConfiguration config = pullAndMergeConfiguration(run, initialConfiguration);
    +        String url = config.getVaultUrl();
    +
    +        if (StringUtils.isBlank(url)) {
    +            throw new VaultPluginException(
    +                "The vault url was not configured - please specify the vault url to use.");
    +        }
    +
    +        VaultConfig vaultConfig = config.getVaultConfig();
    +        VaultCredential credential = config.getVaultCredential();
    +        if (credential == null) {
    +            credential = retrieveVaultCredentials(run, config);
    +        }
    +
    +        String prefixPath = StringUtils.isBlank(config.getPrefixPath())
    +            ? ""
    +            : Util.ensureEndsWith(envVars.expand(config.getPrefixPath()), "/");
    +
    +        if (vaultAccessor == null) {
    +            vaultAccessor = new VaultAccessor();
    +        }
    +        vaultAccessor.setConfig(vaultConfig);
    +        vaultAccessor.setCredential(credential);
    +        vaultAccessor.setMaxRetries(config.getMaxRetries());
    +        vaultAccessor.setRetryIntervalMilliseconds(config.getRetryIntervalMilliseconds());
    +        vaultAccessor.init();
    +
    +        for (VaultSecret vaultSecret : vaultSecrets) {
    +            String path = prefixPath + envVars.expand(vaultSecret.getPath());
    +            logger.printf("Retrieving secret: %s%n", path);
    +            Integer engineVersion = Optional.ofNullable(vaultSecret.getEngineVersion())
    +                .orElse(config.getEngineVersion());
    +            try {
    +                LogicalResponse response = vaultAccessor.read(path, engineVersion);
    +                if (responseHasErrors(config, logger, path, response)) {
    +                    continue;
    +                }
    +                Map<String, String> values = response.getData();
    +                for (VaultSecretValue value : vaultSecret.getSecretValues()) {
    +                    String vaultKey = value.getVaultKey();
    +                    String secret = values.get(vaultKey);
    +                    if (StringUtils.isBlank(secret)) {
    +                        throw new IllegalArgumentException(
    +                            "Vault Secret " + vaultKey + " at " + path
    +                                + " is either null or empty. Please check the Secret in Vault.");
    +                    }
    +                    overrides.put(value.getEnvVar(), secret);
    +                }
    +            } catch (VaultPluginException ex) {
    +                VaultException e = (VaultException) ex.getCause();
    +                if (e != null) {
    +                    throw new VaultPluginException(String
    +                        .format("Vault response returned %d for secret path %s",
    +                            e.getHttpStatusCode(), path),
    +                        e);
    +                }
    +                throw ex;
    +            }
    +        }
    +
    +        return overrides;
    +    }
    +
    +    public static VaultCredential retrieveVaultCredentials(Run build, VaultConfiguration config) {
    +        if (Jenkins.getInstanceOrNull() != null) {
    +            String id = config.getVaultCredentialId();
    +            if (StringUtils.isBlank(id)) {
    +                throw new VaultPluginException(
    +                    "The credential id was not configured - please specify the credentials to use.");
    +            }
    +            List<VaultCredential> credentials = CredentialsProvider
    +                .lookupCredentials(VaultCredential.class, build.getParent(), ACL.SYSTEM,
    +                    Collections.emptyList());
    +            VaultCredential credential = CredentialsMatchers
    +                .firstOrNull(credentials, new IdMatcher(id));
    +
    +            if (credential == null) {
    +                throw new CredentialsUnavailableException(id);
    +            }
    +
    +            return credential;
    +        }
    +
    +        return null;
    +    }
    +
    +    public static boolean responseHasErrors(VaultConfiguration configuration, PrintStream logger,
    +        String path, LogicalResponse response) {
    +        RestResponse restResponse = response.getRestResponse();
    +        if (restResponse == null) {
    +            return false;
    +        }
    +        int status = restResponse.getStatus();
    +        if (status == 403) {
    +            logger.printf("Access denied to Vault Secrets at '%s'%n", path);
    +            return true;
    +        } else if (status == 404) {
    +            if (configuration.getFailIfNotFound()) {
    +                throw new VaultPluginException(
    +                    String.format("Vault credentials not found for '%s'", path));
    +            } else {
    +                logger.printf("Vault credentials not found for '%s'%n", path);
    +                return true;
    +            }
    +        } else if (status >= 400) {
    +            String errors = Optional
    +                .of(Json.parse(new String(restResponse.getBody(), StandardCharsets.UTF_8))).map(
    +                    JsonValue::asObject)
    +                .map(j -> j.get("errors")).map(JsonValue::asArray).map(JsonArray::values)
    +                .map(j -> j.stream().map(JsonValue::asString).collect(Collectors.joining("\n")))
    +                .orElse("");
    +            logger.printf("Vault responded with %d error code.%n", status);
    +            if (StringUtils.isNotBlank(errors)) {
    +                logger.printf("Vault responded with errors: %s%n", errors);
    +            }
    +            return true;
    +        }
    +        return false;
    +    }
    +
    +    public static VaultConfiguration pullAndMergeConfiguration(Run<?, ?> build,
    +        VaultConfiguration buildConfiguration) {
    +        VaultConfiguration configuration = buildConfiguration;
    +        for (VaultConfigResolver resolver : ExtensionList.lookup(VaultConfigResolver.class)) {
    +            if (configuration != null) {
    +                configuration = configuration
    +                    .mergeWithParent(resolver.forJob(build.getParent()));
    +            } else {
    +                configuration = resolver.forJob(build.getParent());
    +            }
    +        }
    +        if (configuration == null) {
    +            throw new VaultPluginException(
    +                "No configuration found - please configure the VaultPlugin.");
    +        }
    +        configuration.fixDefaults();
    +
    +        return configuration;
    +    }
     }
    
  • src/main/java/com/datapipe/jenkins/vault/VaultBindingStep.java+191 0 added
    @@ -0,0 +1,191 @@
    +/*
    + * The MIT License (MIT)
    + * <p>
    + * Copyright (c) 2016 Datapipe, Inc.
    + * <p>
    + * Permission is hereby granted, free of charge, to any person obtaining a copy
    + * of this software and associated documentation files (the "Software"), to deal
    + * in the Software without restriction, including without limitation the rights
    + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    + * copies of the Software, and to permit persons to whom the Software is
    + * furnished to do so, subject to the following conditions:
    + * <p>
    + * The above copyright notice and this permission notice shall be included in all
    + * copies or substantial portions of the Software.
    + * <p>
    + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    + * SOFTWARE.
    + */
    +package com.datapipe.jenkins.vault;
    +
    +import com.datapipe.jenkins.vault.configuration.VaultConfiguration;
    +import com.datapipe.jenkins.vault.log.MaskingConsoleLogFilter;
    +import com.datapipe.jenkins.vault.model.VaultSecret;
    +import com.google.common.annotations.VisibleForTesting;
    +import edu.umd.cs.findbugs.annotations.CheckForNull;
    +import hudson.EnvVars;
    +import hudson.Extension;
    +import hudson.console.ConsoleLogFilter;
    +import hudson.model.Run;
    +import hudson.model.TaskListener;
    +import hudson.util.Secret;
    +import java.io.IOException;
    +import java.util.ArrayList;
    +import java.util.Arrays;
    +import java.util.Collections;
    +import java.util.HashMap;
    +import java.util.HashSet;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Set;
    +import javax.annotation.Nonnull;
    +import org.jenkinsci.plugins.workflow.steps.BodyExecutionCallback.TailCall;
    +import org.jenkinsci.plugins.workflow.steps.BodyInvoker;
    +import org.jenkinsci.plugins.workflow.steps.EnvironmentExpander;
    +import org.jenkinsci.plugins.workflow.steps.GeneralNonBlockingStepExecution;
    +import org.jenkinsci.plugins.workflow.steps.Step;
    +import org.jenkinsci.plugins.workflow.steps.StepContext;
    +import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
    +import org.jenkinsci.plugins.workflow.steps.StepExecution;
    +import org.kohsuke.stapler.DataBoundConstructor;
    +import org.kohsuke.stapler.DataBoundSetter;
    +
    +public class VaultBindingStep extends Step {
    +
    +    private VaultConfiguration configuration;
    +    private List<VaultSecret> vaultSecrets;
    +
    +    @DataBoundConstructor
    +    public VaultBindingStep(@CheckForNull List<VaultSecret> vaultSecrets) {
    +        this.vaultSecrets = vaultSecrets;
    +    }
    +
    +    public List<VaultSecret> getVaultSecrets() {
    +        return vaultSecrets;
    +    }
    +
    +    @DataBoundSetter
    +    public void setConfiguration(VaultConfiguration configuration) {
    +        this.configuration = configuration;
    +    }
    +
    +    public VaultConfiguration getConfiguration() {
    +        return configuration;
    +    }
    +
    +    @Override
    +    public StepExecution start(StepContext context) throws Exception {
    +        return new Execution(this, context);
    +    }
    +
    +    protected static class Execution extends GeneralNonBlockingStepExecution {
    +
    +        private static final long serialVersionUID = 1;
    +
    +        private transient VaultBindingStep step;
    +        private transient VaultAccessor vaultAccessor;
    +
    +        public Execution(VaultBindingStep step, StepContext context) {
    +            super(context);
    +            this.step = step;
    +        }
    +
    +        @VisibleForTesting
    +        public void setVaultAccessor(VaultAccessor vaultAccessor) {
    +            this.vaultAccessor = vaultAccessor;
    +        }
    +
    +        @Override
    +        public boolean start() throws Exception {
    +            run(this::doStart);
    +            return false;
    +        }
    +
    +        private void doStart() throws Exception {
    +            Run<?, ?> run = getContext().get(Run.class);
    +            TaskListener listener = getContext().get(TaskListener.class);
    +            EnvVars envVars = getContext().get(EnvVars.class);
    +
    +            Map<String, String> overrides = VaultAccessor
    +                .retrieveVaultSecrets(run, listener.getLogger(), envVars, vaultAccessor,
    +                    step.getConfiguration(), step.getVaultSecrets());
    +
    +            List<String> secretValues = new ArrayList<>();
    +            secretValues.addAll(overrides.values());
    +
    +            getContext().newBodyInvoker()
    +                .withContext(EnvironmentExpander.merge(getContext().get(EnvironmentExpander.class),
    +                    new VaultBindingStep.Overrider(overrides)))
    +                .withContext(BodyInvoker
    +                    .mergeConsoleLogFilters(getContext().get(ConsoleLogFilter.class),
    +                        new MaskingConsoleLogFilter(run.getCharset().name(), secretValues)))
    +                .withCallback(new Callback())
    +                .start();
    +        }
    +    }
    +
    +    private static final class Overrider extends EnvironmentExpander {
    +
    +        private static final long serialVersionUID = 1;
    +
    +        private final Map<String, Secret> overrides = new HashMap<String, Secret>();
    +
    +        Overrider(Map<String, String> overrides) {
    +            for (Map.Entry<String, String> override : overrides.entrySet()) {
    +                this.overrides.put(override.getKey(), Secret.fromString(override.getValue()));
    +            }
    +        }
    +
    +        @Override
    +        public void expand(EnvVars env) throws IOException, InterruptedException {
    +            for (Map.Entry<String, Secret> override : overrides.entrySet()) {
    +                env.override(override.getKey(), override.getValue().getPlainText());
    +            }
    +        }
    +
    +        @Override
    +        public Set<String> getSensitiveVariables() {
    +            return Collections.unmodifiableSet(overrides.keySet());
    +        }
    +    }
    +
    +    private static class Callback extends TailCall {
    +
    +        @Override
    +        protected void finished(StepContext context) throws Exception {
    +
    +        }
    +    }
    +
    +    @Extension
    +    public static final class DescriptorImpl extends StepDescriptor {
    +
    +        @Override
    +        public Set<? extends Class<?>> getRequiredContext() {
    +            return Collections
    +                .unmodifiableSet(
    +                    new HashSet<>(Arrays.asList(TaskListener.class, Run.class, EnvVars.class)));
    +        }
    +
    +        @Override
    +        public boolean takesImplicitBlockArgument() {
    +            return true;
    +        }
    +
    +        @Override
    +        public String getFunctionName() {
    +            return "withVault";
    +        }
    +
    +        @Nonnull
    +        @Override
    +        public String getDisplayName() {
    +            return "Vault Plugin";
    +        }
    +    }
    +}
    
  • src/main/java/com/datapipe/jenkins/vault/VaultBuildWrapper.java+10 140 modified
    @@ -23,24 +23,11 @@
      */
     package com.datapipe.jenkins.vault;
     
    -import com.bettercloud.vault.VaultConfig;
    -import com.bettercloud.vault.VaultException;
    -import com.bettercloud.vault.json.Json;
    -import com.bettercloud.vault.json.JsonArray;
    -import com.bettercloud.vault.json.JsonValue;
    -import com.bettercloud.vault.response.LogicalResponse;
    -import com.bettercloud.vault.rest.RestResponse;
    -import com.cloudbees.plugins.credentials.CredentialsMatchers;
    -import com.cloudbees.plugins.credentials.CredentialsProvider;
    -import com.cloudbees.plugins.credentials.CredentialsUnavailableException;
    -import com.cloudbees.plugins.credentials.matchers.IdMatcher;
     import com.datapipe.jenkins.vault.configuration.VaultConfigResolver;
     import com.datapipe.jenkins.vault.configuration.VaultConfiguration;
    -import com.datapipe.jenkins.vault.credentials.VaultCredential;
     import com.datapipe.jenkins.vault.exception.VaultPluginException;
     import com.datapipe.jenkins.vault.log.MaskingConsoleLogFilter;
     import com.datapipe.jenkins.vault.model.VaultSecret;
    -import com.datapipe.jenkins.vault.model.VaultSecretValue;
     import com.google.common.annotations.VisibleForTesting;
     import edu.umd.cs.findbugs.annotations.CheckForNull;
     import edu.umd.cs.findbugs.annotations.NonNull;
    @@ -49,24 +36,16 @@
     import hudson.ExtensionList;
     import hudson.FilePath;
     import hudson.Launcher;
    -import hudson.Util;
     import hudson.console.ConsoleLogFilter;
     import hudson.model.AbstractProject;
     import hudson.model.Run;
     import hudson.model.TaskListener;
    -import hudson.security.ACL;
     import hudson.tasks.BuildWrapperDescriptor;
     import java.io.PrintStream;
    -import java.nio.charset.StandardCharsets;
     import java.util.ArrayList;
    -import java.util.Collections;
     import java.util.List;
     import java.util.Map;
    -import java.util.Optional;
    -import java.util.stream.Collectors;
     import jenkins.tasks.SimpleBuildWrapper;
    -import org.apache.commons.lang.StringUtils;
    -import org.jenkinsci.Symbol;
     import org.kohsuke.stapler.DataBoundConstructor;
     import org.kohsuke.stapler.DataBoundSetter;
     
    @@ -114,124 +93,16 @@ public void setVaultAccessor(VaultAccessor vaultAccessor) {
             this.vaultAccessor = vaultAccessor;
         }
     
    -    private List<String> retrieveLeaseIds(List<LogicalResponse> logicalResponses) {
    -        List<String> leaseIds = new ArrayList<>();
    -        for (LogicalResponse response : logicalResponses) {
    -            String leaseId = response.getLeaseId();
    -            if (leaseId != null && !leaseId.isEmpty()) {
    -                leaseIds.add(leaseId);
    -            }
    -        }
    -        return leaseIds;
    -    }
    -
    -    protected void provideEnvironmentVariablesFromVault(Context context, Run build, EnvVars envVars) {
    -        VaultConfiguration config = getConfiguration();
    -        String url = config.getVaultUrl();
    -
    -        if (StringUtils.isBlank(url)) {
    -            throw new VaultPluginException(
    -                "The vault url was not configured - please specify the vault url to use.");
    -        }
    -
    -        VaultConfig vaultConfig = config.getVaultConfig();
    -        VaultCredential credential = config.getVaultCredential();
    -        if (credential == null) credential = retrieveVaultCredentials(build);
    -
    -        String prefixPath = StringUtils.isBlank(config.getPrefixPath())
    -            ? ""
    -            : Util.ensureEndsWith(envVars.expand(config.getPrefixPath()), "/");
    -
    -        if (vaultAccessor == null) vaultAccessor = new VaultAccessor();
    -        vaultAccessor.setConfig(vaultConfig);
    -        vaultAccessor.setCredential(credential);
    -        vaultAccessor.setMaxRetries(config.getMaxRetries());
    -        vaultAccessor.setRetryIntervalMilliseconds(config.getRetryIntervalMilliseconds());
    -        vaultAccessor.init();
    -
    -        for (VaultSecret vaultSecret : vaultSecrets) {
    -            String path = prefixPath + envVars.expand(vaultSecret.getPath());
    -            logger.printf("Retrieving secret: %s%n", path);
    -            Integer engineVersion = Optional.ofNullable(vaultSecret.getEngineVersion())
    -                .orElse(configuration.getEngineVersion());
    -            try {
    -                LogicalResponse response = vaultAccessor.read(path, engineVersion);
    -                if (responseHasErrors(path, response)) {
    -                    continue;
    -                }
    -                Map<String, String> values = response.getData();
    -                for (VaultSecretValue value : vaultSecret.getSecretValues()) {
    -                    String vaultKey = value.getVaultKey();
    -                    String secret = values.get(vaultKey);
    -                    if (StringUtils.isBlank(secret)) {
    -                        throw new IllegalArgumentException(
    -                            "Vault Secret " + vaultKey + " at " + path
    -                                + " is either null or empty. Please check the Secret in Vault.");
    -                    } else {
    -                        valuesToMask.add(secret);
    -                    }
    -                    context.env(value.getEnvVar(), secret);
    -                }
    -            } catch (VaultPluginException ex) {
    -                VaultException e = (VaultException) ex.getCause();
    -                if (e != null) {
    -                    throw new VaultPluginException(String
    -                        .format("Vault response returned %d for secret path %s",
    -                            e.getHttpStatusCode(), path),
    -                        e);
    -                }
    -                throw ex;
    -            }
    -        }
    -    }
    +    protected void provideEnvironmentVariablesFromVault(Context context, Run build,
    +        EnvVars envVars) {
    +        Map<String, String> overrides = VaultAccessor
    +            .retrieveVaultSecrets(build, logger, envVars, vaultAccessor,
    +                getConfiguration(), getVaultSecrets());
     
    -    private boolean responseHasErrors(String path, LogicalResponse response) {
    -        RestResponse restResponse = response.getRestResponse();
    -        if (restResponse == null) return false;
    -        int status = restResponse.getStatus();
    -        if (status == 403) {
    -            logger.printf("Access denied to Vault Secrets at '%s'%n", path);
    -            return true;
    -        } else if (status == 404) {
    -            if (configuration.getFailIfNotFound()) {
    -                throw new VaultPluginException(
    -                    String.format("Vault credentials not found for '%s'", path));
    -            } else {
    -                logger.printf("Vault credentials not found for '%s'%n", path);
    -                return true;
    -            }
    -        } else if (status >= 400) {
    -            String errors = Optional
    -                .of(Json.parse(new String(restResponse.getBody(), StandardCharsets.UTF_8))).map(JsonValue::asObject)
    -                .map(j -> j.get("errors")).map(JsonValue::asArray).map(JsonArray::values)
    -                .map(j -> j.stream().map(JsonValue::asString).collect(Collectors.joining("\n")))
    -                .orElse("");
    -            logger.printf("Vault responded with %d error code.%n", status);
    -            if (StringUtils.isNotBlank(errors)) {
    -                logger.printf("Vault responded with errors: %s%n", errors);
    -            }
    -            return true;
    +        for (Map.Entry<String, String> secret : overrides.entrySet()) {
    +            valuesToMask.add(secret.getValue());
    +            context.env(secret.getKey(), secret.getValue());
             }
    -        return false;
    -    }
    -
    -    protected VaultCredential retrieveVaultCredentials(Run build) {
    -        String id = getConfiguration().getVaultCredentialId();
    -        if (StringUtils.isBlank(id)) {
    -            throw new VaultPluginException(
    -                "The credential id was not configured - please specify the credentials to use.");
    -        }
    -        List<VaultCredential> credentials = CredentialsProvider
    -            .lookupCredentials(VaultCredential.class, build.getParent(), ACL.SYSTEM,
    -                Collections.emptyList());
    -        VaultCredential credential = CredentialsMatchers
    -            .firstOrNull(credentials, new IdMatcher(id));
    -
    -        if (credential == null) {
    -            throw new CredentialsUnavailableException(id);
    -        }
    -
    -        return credential;
         }
     
         private void pullAndMergeConfiguration(Run<?, ?> build) {
    @@ -257,11 +128,10 @@ public ConsoleLogFilter createLoggerDecorator(
     
     
         /**
    -     * Descriptor for {@link VaultBuildWrapper}. Used as a singleton. The class is marked as public so
    -     * that it can be accessed from views.
    +     * Descriptor for {@link VaultBuildWrapper}. Used as a singleton. The class is marked as public
    +     * so that it can be accessed from views.
          */
         @Extension
    -    @Symbol("withVault")
         public static final class DescriptorImpl extends BuildWrapperDescriptor {
     
             public DescriptorImpl() {
    
  • src/main/resources/com/datapipe/jenkins/vault/VaultBindingStep/config.jelly+10 0 added
    @@ -0,0 +1,10 @@
    +<?jelly escape-by-default='true'?>
    +<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
    +  <f:section title="Vault Plugin">
    +    <f:property field="configuration"/>
    +  </f:section>
    +  <f:entry>
    +    <f:repeatableProperty field="vaultSecrets" minimum="0" header="Vault Secret"
    +      add="Add a vault secret"/>
    +  </f:entry>
    +</j:jelly>
    
  • src/test/java/com/datapipe/jenkins/vault/it/folder/FolderIT.java+7 10 modified
    @@ -251,16 +251,13 @@ public void jobInFolderShouldNotBeAbleToAccessCredentialsScopedToAnotherFolder()
         public void jenkinsfileShouldOverrideFolderConfig() throws Exception {
             WorkflowJob pipeline = folder1.createProject(WorkflowJob.class, "Pipeline");
             pipeline.setDefinition(new CpsFlowDefinition("node {\n" +
    -            "    wrap([$class: 'VaultBuildWrapperWithMockAccessor', \n" +
    -            "                   configuration: [$class: 'VaultConfiguration', \n" +
    -            "                             vaultCredentialId: '" + GLOBAL_CREDENTIALS_ID_2 + "', \n"
    -            +
    -            "                             vaultUrl: '" + JENKINSFILE_URL + "'], \n" +
    -            "                   vaultSecrets: [\n" +
    -            "                            [$class: 'VaultSecret', path: 'secret/path1', secretValues: [\n"
    -            +
    -            "                            [$class: 'VaultSecretValue', envVar: 'envVar1', vaultKey: 'key1']]]]]) {\n"
    -            +
    +            "    withVaultMock(\n" +
    +            "        configuration: [ \n" +
    +            "            vaultCredentialId: '" + GLOBAL_CREDENTIALS_ID_2 + "', \n" +
    +            "            vaultUrl: '" + JENKINSFILE_URL + "'], \n" +
    +            "        vaultSecrets: [\n" +
    +            "            [path: 'secret/path1', secretValues: [\n" +
    +            "                 [envVar: 'envVar1', vaultKey: 'key1']]]]) {\n" +
                 "            " + getShellString() + " \"echo ${env.envVar1}\"\n" +
                 "      }\n" +
                 "}", true));
    
  • src/test/java/com/datapipe/jenkins/vault/it/VaultConfigurationIT.java+21 10 modified
    @@ -31,7 +31,12 @@
     import java.util.HashMap;
     import java.util.List;
     import java.util.Map;
    +import org.jenkinsci.plugins.workflow.actions.ArgumentsAction;
     import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition;
    +import org.jenkinsci.plugins.workflow.flow.FlowExecution;
    +import org.jenkinsci.plugins.workflow.graph.FlowNode;
    +import org.jenkinsci.plugins.workflow.graphanalysis.DepthFirstScanner;
    +import org.jenkinsci.plugins.workflow.graphanalysis.NodeStepTypePredicate;
     import org.jenkinsci.plugins.workflow.job.WorkflowJob;
     import org.jenkinsci.plugins.workflow.job.WorkflowRun;
     import org.junit.Before;
    @@ -41,8 +46,10 @@
     
     import static hudson.Functions.isWindows;
     import static org.hamcrest.MatcherAssert.assertThat;
    +import static org.hamcrest.Matchers.hasSize;
     import static org.hamcrest.Matchers.is;
     import static org.hamcrest.Matchers.notNullValue;
    +import static org.hamcrest.collection.IsMapContaining.hasEntry;
     import static org.mockito.ArgumentMatchers.anyInt;
     import static org.mockito.Mockito.any;
     import static org.mockito.Mockito.anyString;
    @@ -254,16 +261,13 @@ public void shouldDealWithTokenBasedCredential() throws Exception {
         public void shouldUseJenkinsfileConfiguration() throws Exception {
             WorkflowJob pipeline = jenkins.createProject(WorkflowJob.class, "Pipeline");
             pipeline.setDefinition(new CpsFlowDefinition("node {\n" +
    -            "    wrap([$class: 'VaultBuildWrapperWithMockAccessor', \n" +
    -            "                   configuration: [$class: 'VaultConfiguration', \n" +
    -            "                             vaultCredentialId: '" + GLOBAL_CREDENTIALS_ID_2 + "', \n"
    -            +
    -            "                             vaultUrl: '" + JENKINSFILE_URL + "'], \n" +
    -            "                   vaultSecrets: [\n" +
    -            "                            [$class: 'VaultSecret', path: 'secret/path1', secretValues: [\n"
    -            +
    -            "                            [$class: 'VaultSecretValue', envVar: 'envVar1', vaultKey: 'key1']]]]]) {\n"
    -            +
    +            "    withVaultMock(\n" +
    +            "        configuration: [ \n" +
    +            "            vaultCredentialId: '" + GLOBAL_CREDENTIALS_ID_2 + "', \n" +
    +            "            vaultUrl: '" + JENKINSFILE_URL + "'], \n" +
    +            "        vaultSecrets: [\n" +
    +            "            [path: 'secret/path1', secretValues: [\n" +
    +            "                 [envVar: 'envVar1', vaultKey: 'key1']]]]) {\n" +
                 "            " + getShellString() + " \"echo ${env.envVar1}\"\n" +
                 "      }\n" +
                 "}", true));
    @@ -273,6 +277,13 @@ public void shouldUseJenkinsfileConfiguration() throws Exception {
             jenkins.assertBuildStatus(Result.SUCCESS, build);
             jenkins.assertLogContains("echo ****", build);
             jenkins.assertLogNotContains("some-secret", build);
    +
    +        FlowExecution execution = build.getExecution();
    +        DepthFirstScanner scanner = new DepthFirstScanner();
    +        List<FlowNode> shellSteps = scanner.filteredNodes(execution, new NodeStepTypePredicate(getShellString()));
    +        assertThat(shellSteps, hasSize(1));
    +        assertThat(shellSteps.get(0).getAction(ArgumentsAction.class), is(notNullValue()));
    +        assertThat(shellSteps.get(0).getAction(ArgumentsAction.class).getArguments(), hasEntry("script", "echo ${envVar1}"));
         }
     
         @Test
    
  • src/test/java/com/datapipe/jenkins/vault/VaultBindingStepWithMockAccessor.java+34 17 renamed
    @@ -6,29 +6,36 @@
     import com.datapipe.jenkins.vault.credentials.VaultAppRoleCredential;
     import com.datapipe.jenkins.vault.credentials.VaultCredential;
     import com.datapipe.jenkins.vault.model.VaultSecret;
    -import edu.umd.cs.findbugs.annotations.CheckForNull;
    +import hudson.EnvVars;
     import hudson.Extension;
    -import hudson.model.AbstractProject;
    -import hudson.model.Descriptor;
    -import hudson.tasks.BuildWrapper;
    +import hudson.model.Run;
    +import hudson.model.TaskListener;
    +import java.util.Arrays;
    +import java.util.Collections;
     import java.util.HashMap;
    +import java.util.HashSet;
     import java.util.List;
     import java.util.Map;
    +import java.util.Set;
    +import javax.annotation.Nonnull;
    +import org.jenkinsci.plugins.workflow.steps.StepContext;
    +import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
    +import org.jenkinsci.plugins.workflow.steps.StepExecution;
     import org.kohsuke.stapler.DataBoundConstructor;
     
     import static org.mockito.Mockito.mock;
     import static org.mockito.Mockito.when;
     
    -/*
    -This class is only used for testing the Jenkinsfile - we can not inject our
    - MockAccessor there and therefore need to mimic it's behaviour here.
    - */
    -public class VaultBuildWrapperWithMockAccessor extends VaultBuildWrapper {
    -
    +public class VaultBindingStepWithMockAccessor extends VaultBindingStep {
         @DataBoundConstructor
    -    public VaultBuildWrapperWithMockAccessor(@CheckForNull List<VaultSecret> vaultSecrets) {
    +    public VaultBindingStepWithMockAccessor(List<VaultSecret> vaultSecrets) {
             super(vaultSecrets);
    -        setVaultAccessor(new VaultAccessor() {
    +    }
    +
    +    @Override
    +    public StepExecution start(StepContext context) throws Exception {
    +        Execution execution = new Execution(this, context);
    +        execution.setVaultAccessor(new VaultAccessor() {
     
                 @Override
                 public void setConfig(VaultConfig config) {
    @@ -73,20 +80,30 @@ public LogicalResponse read(String path, Integer engineVersion) {
                     return resp;
                 }
             });
    +        return execution;
         }
     
         @Extension
    -    public static final class DescriptorImpl extends Descriptor<BuildWrapper> {
    +    public static final class DescriptorImpl extends StepDescriptor {
     
    -        public DescriptorImpl() {
    -            super(VaultBuildWrapperWithMockAccessor.class);
    -            load();
    +        @Override
    +        public Set<? extends Class<?>> getRequiredContext() {
    +            return Collections
    +                .unmodifiableSet(
    +                    new HashSet<>(Arrays.asList(TaskListener.class, Run.class, EnvVars.class)));
             }
     
    -        public boolean isApplicable(AbstractProject<?, ?> item) {
    +        @Override
    +        public boolean takesImplicitBlockArgument() {
                 return true;
             }
     
    +        @Override
    +        public String getFunctionName() {
    +            return "withVaultMock";
    +        }
    +
    +        @Nonnull
             @Override
             public String getDisplayName() {
                 return "Vault Mock Plugin";
    
  • src/test/java/com/datapipe/jenkins/vault/VaultBuildWrapperTest.java+0 7 modified
    @@ -3,7 +3,6 @@
     import com.bettercloud.vault.response.LogicalResponse;
     import com.bettercloud.vault.rest.RestResponse;
     import com.datapipe.jenkins.vault.configuration.VaultConfiguration;
    -import com.datapipe.jenkins.vault.credentials.VaultCredential;
     import com.datapipe.jenkins.vault.exception.VaultPluginException;
     import com.datapipe.jenkins.vault.model.VaultSecret;
     import com.datapipe.jenkins.vault.model.VaultSecretValue;
    @@ -106,15 +105,9 @@ public void run(Context context, Run build, EnvVars envVars, PrintStream logger)
                 provideEnvironmentVariablesFromVault(context, build, envVars);
             }
     
    -        @Override
    -        protected VaultCredential retrieveVaultCredentials(Run build) {
    -            return null;
    -        }
    -
             public void verifyCalls() {
                 verify(mockAccessor, times(2)).init();
                 verify(mockAccessor, times(2)).read("not/existing", 2);
             }
         }
    -
     }
    

Vulnerability mechanics

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

References

5

News mentions

1