diff --git a/README.md b/README.md index f7d1fc2..47f9b5b 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,9 @@ jobs: ## Safety Strategy -- The `safety-strategy` input determines how much access Codex receives on the runner. Choosing the right option is critical, especially when sensitive secrets (like your OpenAI API key) are present. +The `safety-strategy` input determines how much access Codex receives on the runner. Choosing the right option is critical, especially when sensitive secrets (like your OpenAI API key) are present. + +See [Protecting your `OPENAI_API_KEY`](./docs/security.md#protecting-your-openai_api_key) on the Security page for important details on this topic. - **`drop-sudo` (default)** — On Linux and macOS runners, the action revokes the default user’s `sudo` membership before invoking Codex. Codex then runs as that user without superuser privileges. This change lasts for the rest of the job, so subsequent steps cannot rely on `sudo`. This is usually the safest choice on GitHub-hosted runners. - **`unprivileged-user`** — Runs Codex as the user provided via `codex-user`. Use this if you manage your own runner with a pre-created unprivileged account. Ensure the user can read the repository checkout and any files Codex needs. diff --git a/action.yml b/action.yml index d1d33fa..c4d44d8 100644 --- a/action.yml +++ b/action.yml @@ -30,7 +30,7 @@ inputs: codex-version: description: "Version of `@openai/codex` to install." required: false - default: "0.43.0-alpha.18" + default: "0.45.0-alpha.4" codex-args: description: "Additional args to pass through to `codex exec`. If this value starts with `[`, it will be parsed as a JSON array; otherwise, it will be parsed as a shell-like string." required: false diff --git a/docs/security.md b/docs/security.md index 9b339b1..4636ad8 100644 --- a/docs/security.md +++ b/docs/security.md @@ -1,5 +1,13 @@ # Security +## Protecting your `OPENAI_API_KEY` + +No doubt your `OPENAI_API_KEY` is an important secret that you do not want to share with the world. **Be sure to use either `drop-sudo` or `unprivileged-user` to ensure it stays secret!** + +To underscore the importance of specifying either `drop-sudo` or `unprivileged-user` as the `safety-strategy` for `openai/codex-action`, we provide [an example](../examples/test-sandbox-protections.yml) of how **the combination of read-only access to the filesystem and `sudo` can be used to expose your `OPENAI_API_KEY`**. This often surprises developers, as many expect the combination of "read-only access" and no network to be a sufficient safeguard, but this is not the case in the presence of passwordless `sudo` (which is the default on GitHub-hosted runners). Notably, Linux's [procfs](https://en.wikipedia.org/wiki/Procfs) makes a considerable amount of information available via file-read operations to a user with appropriate privileges. + +In the unfortunate event that your API key has leaked, see [this article](https://help.openai.com/en/articles/9047852-how-can-i-delete-my-api-key) that explains how delete/revoke an API key using the [OpenAI Platform's API keys page](https://platform.openai.com/api-keys). + ## Recommendation: run `openai/codex-action` as the last step in a job Particularly if you run Codex with loose permissions, there are no guarantees what the state of the host is when the `openai/codex-action` completes. For example: diff --git a/examples/test-sandbox-protections.yml b/examples/test-sandbox-protections.yml new file mode 100644 index 0000000..9fc0523 --- /dev/null +++ b/examples/test-sandbox-protections.yml @@ -0,0 +1,66 @@ +# This example demonstrates the importance of using `drop-sudo` or `read-only` +# safety strategies to prevent exfiltration of sensitive data like API keys. + +name: Test sandbox protections +on: + workflow_dispatch: + +jobs: + assess-exfiltration-risk: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + safety-strategy: + # `drop-sudo` is the default `safety-strategy`, which prevents the + # malicious behavior allowed by the other strategies in this example. + - drop-sudo + # `read-only` cannot write files or talk to the network, but as we + # will see, it still allows some dangerous operations. + - read-only + - unsafe + steps: + - uses: actions/checkout@v5 + + - name: Run Codex (${{ matrix.safety-strategy }}) + uses: openai/codex-action@main + with: + # An OpenAI API key often starts with `sk-proj-` and is followed + # by random characters matching [A-Za-z-0-9_-]. + openai-api-key: sk-proj-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ + safety-strategy: ${{ matrix.safety-strategy }} + + - name: Try to dump the key from the codex-responses-api-proxy process + run: | + # Find the PID for the codex-responses-api-proxy process. + SERVER_INFO_FILE="$HOME/.codex/${{ github.run_id }}.json" + PID=$(jq .pid < "$SERVER_INFO_FILE") + + # Using standard filesystem read operations (albeit privileged ones), + # try to dump the memory of the process to disk to search for the key. + sudo bash -s -- "$PID" <<'SCAN_MEM' + pid="$1" + mem="/proc/$pid/mem" + i=0 + while read -r rng perms _; do + [[ $perms != r* ]] && continue + start=$((0x${rng%-*})) + end=$((0x${rng#*-})) + dd if="$mem" of="/tmp/$pid-$i.bin" bs=4096 iflag=skip_bytes,count_bytes \ + skip="$start" count="$((end - start))" status=none || true + i=$((i + 1)) + done < "/proc/$pid/maps" + SCAN_MEM + + # Use ordinary grep to search for the key pattern in the dumped memory files. + matches=$(grep -aPo '(?