VYPR
High severityNVD Advisory· Published Mar 12, 2024· Updated Aug 2, 2024

Insecure Variable Substitution in Vela

CVE-2024-28236

Description

Vela CI/CD vulnerable to secret exposure via variable substitution in insensitive fields, bypassing log masking even with 'no commands' restriction.

AI Insight

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

Vela CI/CD vulnerable to secret exposure via variable substitution in insensitive fields, bypassing log masking even with 'no commands' restriction.

Vulnerability

Description Vela, a CI/CD framework written in Go, allows variable substitution in fields such as parameters, image, and entrypoint. This feature can be misused to inject secrets into a plugin or image. By employing common substitution string manipulation, an attacker can bypass log masking and expose secrets without using the commands block [1]. The issue primarily affects secrets restricted by the 'no commands' option, rendering that restriction ineffective [1][3].

Exploitation

Method To exploit this vulnerability, a pipeline author must supply secrets to a plugin that is designed to print those parameters in logs. Plugin parameters are not intended for sensitive values and are often printed for informational or debugging purposes [3]. Examples include using variable substitution in the parameters field, image tag, or entrypoint (e.g., steps: - name: example image: secrets: [ example_secret ] parameters: example: $${EXAMPLE_SECRET}) [3].

Impact

Successful exploitation can lead to unintended use of the secret value and increased risk of exposure during image execution. The log masking provided by Vela is not sufficient to prevent exposure when secrets are injected via substitution [1]. Users who rely on the 'no commands' option and image restriction may have a false sense of security, as these can be bypassed [1].

Mitigation

The issue has been addressed in Vela version 0.23.2 [2]. Users are advised to upgrade. As a workaround, users should not provide sensitive values to plugins that can potentially expose them, especially in parameters not intended for sensitive data, and ensure plugins follow best practices to avoid logging sensitive information [1]. The fix involves separating secrets that allow substitution from those that do not, as seen in the commit that introduces separate maps for secrets with and without substitution permission [2].

AI Insight generated on May 20, 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
github.com/go-vela/workerGo
< 0.23.20.23.2

Affected products

2

Patches

1
e1572743b008

Merge pull request from GHSA-pwx5-6wxg-px5h

https://github.com/go-vela/workerEaston CrupperMar 12, 2024via ghsa
7 files changed · +61 19
  • executor/linux/build.go+20 4 modified
    @@ -199,8 +199,12 @@ func (c *client) PlanBuild(ctx context.Context) error {
     
     		_log.AppendData(append(sRaw, "\n"...))
     
    -		// add secret to the map
    -		c.Secrets[secret.Name] = s
    +		// add secret to the appropriate map
    +		if s.GetAllowSubstitution() {
    +			c.Secrets[secret.Name] = s
    +		} else {
    +			c.NoSubSecrets[secret.Name] = s
    +		}
     	}
     
     	// escape newlines in secrets loaded on build_start
    @@ -699,6 +703,7 @@ func loadLazySecrets(c *client, _step *pipeline.Container) error {
     	_log := new(library.Log)
     
     	lazySecrets := make(map[string]*library.Secret)
    +	lazyNoSubSecrets := make(map[string]*library.Secret)
     
     	// this requires a small preface and brief description on
     	// how normal secrets make it into a container:
    @@ -797,8 +802,12 @@ func loadLazySecrets(c *client, _step *pipeline.Container) error {
     				return err
     			}
     
    -			// add secret to the temp map
    -			lazySecrets[secret.Name] = s
    +			// add secret to the appropriate temp map
    +			if s.GetAllowSubstitution() {
    +				lazySecrets[secret.Name] = s
    +			} else {
    +				lazyNoSubSecrets[secret.Name] = s
    +			}
     		}
     	}
     
    @@ -836,6 +845,13 @@ func loadLazySecrets(c *client, _step *pipeline.Container) error {
     			return err
     		}
     
    +		c.Logger.Debug("injecting no-sub lazy loaded secrets")
    +		// inject secrets for container
    +		err = injectSecrets(tmpStep, lazyNoSubSecrets)
    +		if err != nil {
    +			return err
    +		}
    +
     		c.Logger.Debug("merge lazy loaded secrets into container")
     		// merge lazy load secrets into original container
     		err = _step.MergeEnv(tmpStep.Environment)
    
  • executor/linux/linux.go+11 6 modified
    @@ -19,12 +19,13 @@ type (
     	// client manages communication with the pipeline resources.
     	client struct {
     		// https://pkg.go.dev/github.com/sirupsen/logrus#Entry
    -		Logger   *logrus.Entry
    -		Vela     *vela.Client
    -		Runtime  runtime.Engine
    -		Secrets  map[string]*library.Secret
    -		Hostname string
    -		Version  string
    +		Logger       *logrus.Entry
    +		Vela         *vela.Client
    +		Runtime      runtime.Engine
    +		Secrets      map[string]*library.Secret
    +		NoSubSecrets map[string]*library.Secret
    +		Hostname     string
    +		Version      string
     
     		// clients for build actions
     		secret *secretSvc
    @@ -67,6 +68,7 @@ func Equal(a, b *client) bool {
     		reflect.DeepEqual(a.Vela, b.Vela) &&
     		reflect.DeepEqual(a.Runtime, b.Runtime) &&
     		reflect.DeepEqual(a.Secrets, b.Secrets) &&
    +		reflect.DeepEqual(a.NoSubSecrets, b.NoSubSecrets) &&
     		a.Hostname == b.Hostname &&
     		a.Version == b.Version &&
     		reflect.DeepEqual(a.init, b.init) &&
    @@ -118,6 +120,9 @@ func New(opts ...Opt) (*client, error) {
     	// instantiate map for non-plugin secrets
     	c.Secrets = make(map[string]*library.Secret)
     
    +	// instantiate map for non-substituted secrets
    +	c.NoSubSecrets = make(map[string]*library.Secret)
    +
     	// instantiate all client services
     	c.secret = &secretSvc{client: c}
     
    
  • executor/linux/secret.go+7 0 modified
    @@ -67,6 +67,13 @@ func (s *secretSvc) create(ctx context.Context, ctn *pipeline.Container) error {
     		return fmt.Errorf("unable to substitute container configuration")
     	}
     
    +	logger.Debug("injecting non-substituted secrets")
    +	// inject no-substitution secrets for container
    +	err = injectSecrets(ctn, s.client.NoSubSecrets)
    +	if err != nil {
    +		return err
    +	}
    +
     	return nil
     }
     
    
  • executor/linux/service.go+7 0 modified
    @@ -55,6 +55,13 @@ func (c *client) CreateService(ctx context.Context, ctn *pipeline.Container) err
     		return fmt.Errorf("unable to substitute container configuration")
     	}
     
    +	logger.Debug("injecting non-substituted secrets")
    +	// inject no-substitution secrets for container
    +	err = injectSecrets(ctn, c.NoSubSecrets)
    +	if err != nil {
    +		return err
    +	}
    +
     	return nil
     }
     
    
  • executor/linux/step.go+7 0 modified
    @@ -65,6 +65,13 @@ func (c *client) CreateStep(ctx context.Context, ctn *pipeline.Container) error
     		return fmt.Errorf("unable to substitute container configuration")
     	}
     
    +	logger.Debug("injecting non-substituted secrets")
    +	// inject no-substitution secrets for container
    +	err = injectSecrets(ctn, c.NoSubSecrets)
    +	if err != nil {
    +		return err
    +	}
    +
     	return nil
     }
     
    
  • go.mod+3 3 modified
    @@ -8,9 +8,9 @@ require (
     	github.com/docker/docker v24.0.9+incompatible
     	github.com/docker/go-units v0.5.0
     	github.com/gin-gonic/gin v1.9.1
    -	github.com/go-vela/sdk-go v0.23.1
    -	github.com/go-vela/server v0.23.1
    -	github.com/go-vela/types v0.23.1
    +	github.com/go-vela/sdk-go v0.23.2-0.20240312184917-e3a34719badf
    +	github.com/go-vela/server v0.23.2-0.20240312184244-a645c822da1d
    +	github.com/go-vela/types v0.23.2-0.20240312183632-2e046fceb8fe
     	github.com/golang-jwt/jwt/v5 v5.2.0
     	github.com/google/go-cmp v0.6.0
     	github.com/joho/godotenv v1.5.1
    
  • go.sum+6 6 modified
    @@ -94,12 +94,12 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg
     github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
     github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
     github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
    -github.com/go-vela/sdk-go v0.23.1 h1:4KxfAF1vR8DvtRraBoWQDIm8f8zxXP806lJR3MmTlC8=
    -github.com/go-vela/sdk-go v0.23.1/go.mod h1:zDsZIePtBdpCZwmwAWqGWuIch/oGliX1zd51PARTHBk=
    -github.com/go-vela/server v0.23.1 h1:Y+mGfB79RjIgQ3IEkPjGB6IneB2So3ZXE4XKY+Z02xc=
    -github.com/go-vela/server v0.23.1/go.mod h1:B+A5lRPOlAVYyXBMGCAJKhPQOlfJuWl3qaRcvhsUqSA=
    -github.com/go-vela/types v0.23.1 h1:st4BeDcYVyaaFqblU1YroztNvmYLBgmfZpWq0En0Sg0=
    -github.com/go-vela/types v0.23.1/go.mod h1:AAqgxIw1aRBgPkE/5juGuiwh/JZuOtL8fcPaEkjFWwQ=
    +github.com/go-vela/sdk-go v0.23.2-0.20240312184917-e3a34719badf h1:8Oka4tMHOdy/DsInyg7c/XPY5wqWWE7Yvzx/u67WBuw=
    +github.com/go-vela/sdk-go v0.23.2-0.20240312184917-e3a34719badf/go.mod h1:XjrVfIDw2SZDFBtJ5vqVse/GFj89MF542N20P8U5a3I=
    +github.com/go-vela/server v0.23.2-0.20240312184244-a645c822da1d h1:VynpkAIMt3KTh9BaICQdpu6c76/hHU3d4/Ab44bmFew=
    +github.com/go-vela/server v0.23.2-0.20240312184244-a645c822da1d/go.mod h1:EsDVTqQHQ9snXG2DhUl9uo4+Cf/b9nMiESCkxSjmP90=
    +github.com/go-vela/types v0.23.2-0.20240312183632-2e046fceb8fe h1:Fb28yre0nrX1GNeyPN8i8rruTlW8MnPVF3Fo5xTuOkg=
    +github.com/go-vela/types v0.23.2-0.20240312183632-2e046fceb8fe/go.mod h1:AAqgxIw1aRBgPkE/5juGuiwh/JZuOtL8fcPaEkjFWwQ=
     github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
     github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
     github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
    

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.