Skip to content

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:

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.

Accessing secrets

Exfiltrating data from an execution environment

Stealing the job’s token