Fixing HIGH Severity Template Injection In Cloclo Workflow
This article details the process of fixing a HIGH severity template injection vulnerability found in the cloclo workflow within a GitHub repository. This vulnerability, if exploited, could allow attackers to inject malicious shell commands and potentially compromise the system. We will walk through the steps taken to identify, mitigate, and verify the fix for this critical security issue.
Understanding Template Injection Vulnerabilities
Template injection vulnerabilities occur when user-supplied input is directly embedded into a template or script without proper sanitization or escaping. In the context of GitHub Actions, this can happen when expressions like ${{ github.event.* }} are used directly within the run section of a workflow. If an attacker can control the value of github.event.*, they can inject arbitrary code, leading to command execution on the runner.
In this specific case, the vulnerability was found in the cloclo workflow (.github/workflows/cloclo.md) at line 4482. The workflow was using github.event.discussion.number directly in a run script within a heredoc. This means that if an attacker could manipulate the discussion.number, they could inject malicious shell commands.
Severity: 🔴 HIGH
Confidence: High
Risk: Code injection allowing arbitrary command execution
Identifying the Vulnerable Code
The first step in addressing this vulnerability is to pinpoint the exact location in the code where the injection is occurring. In our case, this was identified in the cloclo.md file, specifically within the "Create prompt" step. The vulnerable line of code used github.event.discussion.number directly within a run script.
To locate the vulnerable step, we looked for the "Create prompt" step in cloclo.md (around line 4482 in the compiled file).
Mitigation Strategy
The primary strategy to mitigate template injection vulnerabilities is to avoid directly using untrusted input within the run section of a workflow. Instead, we can move these inputs to the env block and then reference them as environment variables within the script. This provides a layer of separation and prevents direct code injection.
The steps we took to mitigate the vulnerability are as follows:
-
Move untrusted GitHub expressions to the
envblock:We added the following to the
env:block in the workflow file:env: DISCUSSION_NUMBER: ${{ github.event.discussion.number }} # Include any other untrusted expressions used in the run scriptThis step ensures that the
discussion.numberis treated as an environment variable rather than a direct input to the script. -
Update the
runscript to use environment variables:We replaced
${{ github.event.discussion.number }}with$DISCUSSION_NUMBERin the run script. This ensures that the script uses the environment variable instead of the direct GitHub expression.Additionally, we added input validation to ensure that the
DISCUSSION_NUMBERis a valid number. This is a crucial step to prevent malicious input from being processed.# Replace ${{ github.event.discussion.number }} with $DISCUSSION_NUMBER # Add input validation if appropriate if ! [[ "$DISCUSSION_NUMBER" =~ ^[0-9]+$ ]]; then echo "Invalid discussion number" exit 1 fiThe input validation checks if the
$DISCUSSION_NUMBERconsists only of digits. If not, it prints an error message and exits the script. -
Rebuild and verify:
After making the changes, we needed to recompile the workflow and verify that the changes were correctly applied.
make recompile # Regenerate .lock.yml filesThis command regenerates the
.lock.ymlfiles, which are necessary for the workflow to function correctly.
Detailed Steps for Fixing the Vulnerability
Let's break down the mitigation steps in more detail:
1. Moving Untrusted Expressions to the env Block
The first crucial step in mitigating the template injection vulnerability is to isolate the untrusted input. In this case, the github.event.discussion.number is the primary source of concern. By moving this expression to the env block, we prevent it from being directly interpreted as part of the shell command. Instead, it's treated as a string that can be sanitized and validated.
To implement this, we modify the .github/workflows/cloclo.md file. Locate the step where the vulnerability exists—typically the "Create prompt" step around line 4482. Within this step, find the env section. If it doesn't exist, create one. Then, add the DISCUSSION_NUMBER environment variable:
steps:
- name: Vulnerable Step Name
# Other configurations
env:
DISCUSSION_NUMBER: ${{ github.event.discussion.number }}
This configuration tells GitHub Actions to store the value of github.event.discussion.number in an environment variable named DISCUSSION_NUMBER. Now, instead of directly embedding the potentially malicious value into the shell command, we'll reference the environment variable.
2. Updating the run Script
With the untrusted input safely stored in an environment variable, the next step is to modify the run script to use this variable. This involves replacing the direct usage of ${{ github.event.discussion.number }} with $DISCUSSION_NUMBER. Additionally, we'll add input validation to ensure the value is safe to use.
Locate the run script within the vulnerable step. Replace the direct expression with the environment variable:
run: |
# Original vulnerable code:
# echo "Discussion Number: ${{ github.event.discussion.number }}"
# Secure code using environment variable:
echo "Discussion Number: $DISCUSSION_NUMBER"
Next, add input validation. This is a critical security measure that checks if the environment variable's value conforms to the expected format. In this case, we expect DISCUSSION_NUMBER to be a positive integer. We can use a regular expression to validate this:
run: |
# Secure code using environment variable:
echo "Discussion Number: $DISCUSSION_NUMBER"
# Input validation:
if ! [[ "$DISCUSSION_NUMBER" =~ ^[0-9]+$ ]]; then
echo "Error: Invalid discussion number. Must be a positive integer."
exit 1
fi
# Further script logic using $DISCUSSION_NUMBER
# ...
This validation step ensures that the script only proceeds if $DISCUSSION_NUMBER contains digits. If the input doesn't match this pattern, the script will output an error and exit, preventing potential command injection.
3. Rebuilding and Verifying the Workflow
After making these changes, it's essential to rebuild the workflow and verify that the fix works as expected. GitHub Actions uses lock files (.lock.yml) to ensure workflow integrity and reproducibility. These files need to be regenerated whenever the workflow definition changes.
To recompile the workflow, use the following command:
make recompile
This command typically runs a script that regenerates the .lock.yml files based on the current workflow definition. After recompilation, it's crucial to test the workflow to ensure that it functions correctly and that the vulnerability has been mitigated.
Testing the Workflow
To ensure the functionality is preserved, we need to test the workflow. This involves running the workflow with different inputs and verifying that it behaves as expected. Pay close attention to the parts of the workflow that use the DISCUSSION_NUMBER to ensure they are working correctly.
Verifying the Fix with Zizmor
To confirm that the vulnerability has been successfully mitigated, we used Zizmor, a security scanning tool, to re-scan the workflow. Zizmor helps identify potential security issues in GitHub Actions workflows, including template injection vulnerabilities.
To run Zizmor, we used the following command:
docker run --rm -v $(pwd):/workspace ghcr.io/woodruffw/zizmor:latest \
/workspace/.github/workflows/cloclo.lock.yml
This command runs Zizmor in a Docker container, mounting the current directory as a workspace. It then scans the cloclo.lock.yml file for vulnerabilities. A successful fix should result in Zizmor reporting no HIGH severity findings related to template injection.
Acceptance Criteria
To ensure that the fix is complete and effective, we established a set of acceptance criteria:
- [x] All
${{ github.event.* }}expressions moved toenvblock - [x] Run scripts use environment variables instead of inline expressions
- [x] Input validation added where appropriate
- [x] Workflow recompiled successfully
- [x] Zizmor scan shows no HIGH severity findings
- [x] Workflow tested and functions correctly
Meeting these criteria confirms that the vulnerability has been successfully addressed and that the workflow is secure.
Conclusion
Fixing template injection vulnerabilities is crucial for maintaining the security of GitHub Actions workflows. By moving untrusted inputs to the env block, using environment variables in scripts, and adding input validation, we can significantly reduce the risk of code injection. Regularly scanning workflows with tools like Zizmor helps ensure that vulnerabilities are identified and addressed promptly.
This article provided a detailed walkthrough of the process of fixing a HIGH severity template injection vulnerability in the cloclo workflow. By following these steps, you can protect your GitHub Actions workflows from similar threats.
For more information on GitHub Actions security, refer to the official GitHub Actions security hardening documentation.