Security hardening¶
Good security practices for using the OpenTestFactory orchestrator features.
Overview¶
This guide explains how to configure security hardening for certain OpenTestFactory orchestrator features. If the OpenTestFactory orchestrator concepts are unfamiliar, see “Understanding OpenTestFactory Orchestrator.”
Understanding the risk of script injections¶
When creating workflows, generator plugins, and provider plugins, you should always consider whether your code might execute untrusted input from attackers. This can occur when an attacker adds malicious commands and scripts to a context. When your workflow runs, those strings might be interpreted as code which is then executed on the execution environment.
Attackers can add their own malicious content to the opentf
context, which should be treated
as potentially untrusted input. These contexts typically end with body
, label
, message
, name
,
and title
.
You should ensure that these values do not flow directly into workflows, functions, API calls, or anywhere else where they could be interpreted as executable code. By adopting the same defensive programming posture you would use for any other privileged application code, you can help security harden your use of the OpenTestFactory orchestrator. For information on some of the steps an attacker could take, see “Security hardening for OpenTestFactory orchestrator.”
In addition, there are other less obvious sources of potentially untrusted input, such as
branch names and email addresses, which can be quite flexible in terms of their permitted content.
For example, zzz";echo${IFS}"hello";#
would be a valid branch name and would be a possible attack
vector for a target repository.
The following sections explain how you can help mitigate the risk of script injection.
Example of a script injection attack
A script injection attack can occur directly within a workflow’s inline script. In the following example, an action uses an expression to test the validity of a workflow title, but also adds the risk of script injection:
- name: Check workflow title
run: |
title="${{ opentf.workflow }}"
if [[ $title =~ ^octocat ]]; then
echo "Workflow title starts with 'octocat'"
exit 0
else
echo "Workflow title did not start with 'octocat'"
exit 1
fi
This example is vulnerable to script injection because the run
command executes within a temporary
shell script on the runner. Before the shell script is run, the expressions inside ${{ }}
are
evaluated and then substituted with the resulting values, which can make it vulnerable to shell
command injection.
To inject commands into this workflow, the attacker could create a workflow with a title of
a"; ls $OPENTF_WORKSPACE"
:
metadata:
name: 'a"; ls -al $OPENTF_WORKSPACE"'
In this example, the "
character is used to interrupt the title="${{ opentf.workflow }}"
statement, allowing the ls
command to be executed on the execution environment. You can see the output
of the ls
command in the log:
Workflow 65643782-28ef-43b9-b8b8-545c01a34775 is running.
Workflow a"; ls -al $OPENTF_WORKSPACE"
(running in namespace 'default')
[2024-07-30T18:25:10] [job a0e45dea-7dfe-470b-9f1d-3eb176c43d81] Requesting execution environment providing ['linux'] in namespace 'default' for job 'job_injection'
[2024-07-30T18:25:10] [job a0e45dea-7dfe-470b-9f1d-3eb176c43d81] Running command: title="a"; ls -...
[2024-07-30T18:25:15] [job a0e45dea-7dfe-470b-9f1d-3eb176c43d81] total 0
[2024-07-30T18:25:15] [job a0e45dea-7dfe-470b-9f1d-3eb176c43d81] drwxrwxrwx 1 user user 4096 Jul 30 18:25 .
[2024-07-30T18:25:15] [job a0e45dea-7dfe-470b-9f1d-3eb176c43d81] drwxrwxrwx 1 user user 4096 Jul 30 18:25 ..
[2024-07-30T18:25:15] [job a0e45dea-7dfe-470b-9f1d-3eb176c43d81] Workflow title did not start with 'octocat'
Good practices for mitigating script injection attacks¶
There are a number of different approaches available to help you mitigate the risk of script injection:
Using a function instead of an inline script (recommended)¶
The recommended approach is to create a Python function that processes the context value as an argument. This approach is not vulnerable to the injection attack, since the context value is not used to generate a shell script, but is instead passed to the function as an argument:
uses: fakeaction/checktitle@v3
with:
title: ${{ opentf.workflow }}
Using an intermediate environment variable¶
For inline scripts, the preferred approach to handling untrusted input is to set the value of the expression to an intermediate environment variable.
The following example uses Bash to process the opentf.workflow
value as an environment variable:
- name: Check workflow title
variables:
TITLE:
value: ${{ opentf.workflow }}
verbatim: true
run: |
if [[ "$TITLE" =~ ^octocat ]]; then
echo "Workflow title starts with 'octocat'"
exit 0
else
echo "Workflow title did not start with 'octocat'"
exit 1
fi
In this example, the attempted script injection is unsuccessful, which is reflected by the following lines in the log:
Workflow title did not start with 'octocat'
With this approach, the value of the ${{ opentf.workflow }}
expression is
stored in memory and used as a variable, and doesn’t interact with the script generation
process. In addition, consider using double quote shell variables to avoid word
splitting, but this is one of
many general recommendations for writing shell scripts,
and is not specific to OpenTestFactory workflows.
Potential impact of a compromised execution environment¶
These sections consider some of the steps an attacker can take if they’re able to run malicious commands on an OpenTestFactory orchestrator execution environment.