From 7fafe47bc48097ebe932e2c399af7bdb27075155 Mon Sep 17 00:00:00 2001 From: Ganeshkumar Ashokavardhanan Date: Wed, 29 Jul 2026 10:34:01 -0700 Subject: [PATCH] fix(security): use env indirection for expressions in workflow shells (CWE-94) Several `run:` blocks interpolated ${{ }} expressions directly into shell commands. GitHub substitutes these expressions as literal text into the script before the shell runs, so a value containing shell metacharacters is evaluated as code rather than treated as data. Some of the interpolated values originate outside this repository, so they should not be treated as trusted. This is a well-known Actions anti-pattern; the recommended remedy is to pass values through `env:` and reference them as quoted shell variables. - main.yaml / ci.yaml: pass all expressions through `env:` and reference them as quoted shell variables, so values are always treated as data rather than code. Applied to every build job, since the pattern was repeated throughout. - main.yaml / ci.yaml: validate the driver version and URL read from driver_config.yml in the "Load config" steps and fail the job on anything unexpected, which also prevents newline-based injection of extra $GITHUB_OUTPUT entries. - auto_update.py: validate the driver version and URL before writing them to driver_config.yml, so unexpected values are rejected at the point they enter the repo rather than at the point they are consumed. The URL must be https on the expected download host. - update_grid_driver.yaml: apply the same env indirection to the azure/cli inline script and quote $GITHUB_OUTPUT writes. Validation uses whole-string matching (`\A...\Z` in Python, bash `[[ =~ ]]` in the workflows) because `$` in Python's re matches before a trailing newline and `grep -E` matches line by line, so an initial version using `^...$` was not equivalent. The patterns are deliberately narrow: if the upstream driver location ever changes shape, the job fails loudly and a human reviews the change. Every version and URL currently published upstream satisfies them. No job, step, action version or build argument was changed; only the way values reach the shell. actionlint reports no issues and no `${{ }}` remains inside any `run:` or `inlineScript` block. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8cd619fe-2501-4bcc-97ea-4a895088ffc2 --- .github/workflows/ci.yaml | 97 ++++++++++++++----- .github/workflows/main.yaml | 108 ++++++++++++++++------ .github/workflows/update_grid_driver.yaml | 13 ++- auto_update.py | 33 ++++++- 4 files changed, 191 insertions(+), 60 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 172f20a5..096d2573 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -21,10 +21,16 @@ jobs: fetch-depth: 0 - name: Load CUDA config id: load_config + env: + CONFIG_KEY: ${{ matrix.config_key }} run: | - cuda_version=$(yq e '.${{ matrix.config_key }}.version' driver_config.yml) + cuda_version=$(yq e ".${CONFIG_KEY}.version" driver_config.yml) + if [[ ! "$cuda_version" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then + echo "::error::Refusing unexpected CUDA driver version: $cuda_version" + exit 1 + fi echo "CUDA_VERSION=$cuda_version" - echo "cuda_version=$cuda_version" >> $GITHUB_OUTPUT + echo "cuda_version=$cuda_version" >> "$GITHUB_OUTPUT" - name: Set up QEMU uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx @@ -48,22 +54,31 @@ jobs: ${{ runner.os }}-buildx-${{ matrix.image_repo }}- - name: Generate timestamp id: timestamp - run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT + run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> "$GITHUB_OUTPUT" - uses: paulhatch/semantic-version@v6.0.2 with: bump_each_commit: false version_format: "${{ steps.load_config.outputs.cuda_version }}-${{ steps.timestamp.outputs.timestamp }}" id: semver - name: 'Check version' + env: + VERSION: ${{ steps.semver.outputs.version }} + VERSION_TAG: ${{ steps.semver.outputs.version_tag }} run: | - echo "version is ${{ steps.semver.outputs.version }}" - echo "version is ${{ steps.semver.outputs.version_tag }}" + echo "version is $VERSION" + echo "version is $VERSION_TAG" - name: 'Build and Push' + env: + DRIVER_VERSION: ${{ steps.load_config.outputs.cuda_version }} + IMAGE_REPO: ${{ matrix.image_repo }} + REGISTRY_SERVER: ${{ secrets.AZURE_REGISTRY_SERVER }} + VERSION: ${{ steps.semver.outputs.version }} run: | set -x echo "tag is: " - echo ${{ steps.semver.outputs.version }} - docker buildx build --platform linux/arm64/v8,linux/amd64 --build-arg DRIVER_KIND=cuda --build-arg DRIVER_VERSION=${{ steps.load_config.outputs.cuda_version }} --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t ${{ secrets.AZURE_REGISTRY_SERVER }}/public/aks/${{ matrix.image_repo }}:${{ steps.semver.outputs.version }} . + echo "$VERSION" + image_ref="${REGISTRY_SERVER}/public/aks/${IMAGE_REPO}:${VERSION}" + docker buildx build --platform linux/arm64/v8,linux/amd64 --build-arg DRIVER_KIND=cuda --build-arg "DRIVER_VERSION=${DRIVER_VERSION}" --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t "$image_ref" . docker images - name: Move cache run: | @@ -84,10 +99,16 @@ jobs: fetch-depth: 0 - name: Load CUDA config id: load_config + env: + CONFIG_KEY: ${{ matrix.config_key }} run: | - cuda_version=$(yq e '.${{ matrix.config_key }}.version' driver_config.yml) + cuda_version=$(yq e ".${CONFIG_KEY}.version" driver_config.yml) + if [[ ! "$cuda_version" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then + echo "::error::Refusing unexpected CUDA driver version: $cuda_version" + exit 1 + fi echo "CUDA_VERSION=$cuda_version" - echo "cuda_version=$cuda_version" >> $GITHUB_OUTPUT + echo "cuda_version=$cuda_version" >> "$GITHUB_OUTPUT" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Cache Docker layers @@ -99,22 +120,31 @@ jobs: ${{ runner.os }}-buildx-${{ matrix.image_repo }}- - name: Generate timestamp id: timestamp - run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT + run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> "$GITHUB_OUTPUT" - uses: paulhatch/semantic-version@v6.0.2 with: bump_each_commit: false version_format: "${{ steps.load_config.outputs.cuda_version }}-${{ steps.timestamp.outputs.timestamp }}" id: semver - name: 'Check version' + env: + VERSION: ${{ steps.semver.outputs.version }} + VERSION_TAG: ${{ steps.semver.outputs.version_tag }} run: | - echo "version is ${{ steps.semver.outputs.version }}" - echo "version is ${{ steps.semver.outputs.version_tag }}" + echo "version is $VERSION" + echo "version is $VERSION_TAG" - name: 'Build and Push' + env: + DRIVER_VERSION: ${{ steps.load_config.outputs.cuda_version }} + IMAGE_REPO: ${{ matrix.image_repo }} + REGISTRY_SERVER: ${{ secrets.AZURE_REGISTRY_SERVER }} + VERSION: ${{ steps.semver.outputs.version }} run: | set -x echo "tag is: " - echo ${{ steps.semver.outputs.version }} - docker buildx build --build-arg DRIVER_KIND=cuda --build-arg DRIVER_VERSION=${{ steps.load_config.outputs.cuda_version }} --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t ${{ secrets.AZURE_REGISTRY_SERVER }}/public/aks/${{ matrix.image_repo }}:${{ steps.semver.outputs.version }} . + echo "$VERSION" + image_ref="${REGISTRY_SERVER}/public/aks/${IMAGE_REPO}:${VERSION}" + docker buildx build --build-arg DRIVER_KIND=cuda --build-arg "DRIVER_VERSION=${DRIVER_VERSION}" --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t "$image_ref" . docker images - name: Move cache run: | @@ -135,13 +165,23 @@ jobs: fetch-depth: 0 - name: Load GRID config id: load_config + env: + CONFIG_KEY: ${{ matrix.config_key }} run: | - grid_version=$(yq e '.${{ matrix.config_key }}.version' driver_config.yml) - grid_url=$(yq e '.${{ matrix.config_key }}.url' driver_config.yml) + grid_version=$(yq e ".${CONFIG_KEY}.version" driver_config.yml) + grid_url=$(yq e ".${CONFIG_KEY}.url" driver_config.yml) + if [[ ! "$grid_version" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then + echo "::error::Refusing unexpected GRID driver version: $grid_version" + exit 1 + fi + if [[ ! "$grid_url" =~ ^https://[A-Za-z0-9._~:/?#@%+=-]+$ ]]; then + echo "::error::Refusing unexpected GRID driver URL: $grid_url" + exit 1 + fi echo "GRID_VERSION=$grid_version" echo "GRID_URL=$grid_url" - echo "grid_version=$grid_version" >> $GITHUB_OUTPUT - echo "grid_url=$grid_url" >> $GITHUB_OUTPUT + echo "grid_version=$grid_version" >> "$GITHUB_OUTPUT" + echo "grid_url=$grid_url" >> "$GITHUB_OUTPUT" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Cache Docker layers @@ -153,25 +193,34 @@ jobs: ${{ runner.os }}-buildx-${{ matrix.image_repo }}-${{ steps.load_config.outputs.grid_version }} - name: Generate timestamp id: timestamp - run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT + run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> "$GITHUB_OUTPUT" - uses: paulhatch/semantic-version@v6.0.2 with: bump_each_commit: false version_format: "${{ steps.load_config.outputs.grid_version }}-${{ steps.timestamp.outputs.timestamp }}" id: semver - name: 'Check version' + env: + VERSION: ${{ steps.semver.outputs.version }} + VERSION_TAG: ${{ steps.semver.outputs.version_tag }} run: | - echo "version is ${{ steps.semver.outputs.version }}" - echo "version is ${{ steps.semver.outputs.version_tag }}" + echo "version is $VERSION" + echo "version is $VERSION_TAG" - name: 'Build and Push' + env: + DRIVER_URL: ${{ steps.load_config.outputs.grid_url }} + DRIVER_VERSION: ${{ steps.load_config.outputs.grid_version }} + IMAGE_REPO: ${{ matrix.image_repo }} + REGISTRY_SERVER: ${{ secrets.AZURE_REGISTRY_SERVER }} + VERSION: ${{ steps.semver.outputs.version }} run: | set -x echo "tag is: " - echo ${{ steps.semver.outputs.version }} - docker buildx build --build-arg DRIVER_URL=${{ steps.load_config.outputs.grid_url }} --build-arg DRIVER_KIND=grid --build-arg DRIVER_VERSION=${{ steps.load_config.outputs.grid_version }} --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t ${{ secrets.AZURE_REGISTRY_SERVER }}/public/aks/${{ matrix.image_repo }}:${{ steps.semver.outputs.version }} . + echo "$VERSION" + image_ref="${REGISTRY_SERVER}/public/aks/${IMAGE_REPO}:${VERSION}" + docker buildx build --build-arg "DRIVER_URL=${DRIVER_URL}" --build-arg DRIVER_KIND=grid --build-arg "DRIVER_VERSION=${DRIVER_VERSION}" --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t "$image_ref" . docker images - name: Move cache run: | rm -rf /tmp/.buildx-cache mv /tmp/.buildx-cache-new /tmp/.buildx-cache - diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index aea9e8d7..ece9c142 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -24,10 +24,16 @@ jobs: fetch-depth: 0 - name: Load CUDA config id: load_config + env: + CONFIG_KEY: ${{ matrix.config_key }} run: | - cuda_version=$(yq e '.${{ matrix.config_key }}.version' driver_config.yml) + cuda_version=$(yq e ".${CONFIG_KEY}.version" driver_config.yml) + if [[ ! "$cuda_version" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then + echo "::error::Refusing unexpected CUDA driver version: $cuda_version" + exit 1 + fi echo "CUDA_VERSION=$cuda_version" - echo "cuda_version=$cuda_version" >> $GITHUB_OUTPUT + echo "cuda_version=$cuda_version" >> "$GITHUB_OUTPUT" - name: Set up QEMU uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx @@ -51,16 +57,19 @@ jobs: ${{ runner.os }}-buildx-${{ matrix.image_repo }}- - name: Generate timestamp id: timestamp - run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT + run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> "$GITHUB_OUTPUT" - uses: paulhatch/semantic-version@v6.0.2 with: bump_each_commit: false version_format: "${{ steps.load_config.outputs.cuda_version }}-${{ steps.timestamp.outputs.timestamp }}" id: semver - name: 'Check version' + env: + VERSION: ${{ steps.semver.outputs.version }} + VERSION_TAG: ${{ steps.semver.outputs.version_tag }} run: | - echo "version is ${{ steps.semver.outputs.version }}" - echo "version is ${{ steps.semver.outputs.version_tag }}" + echo "version is $VERSION" + echo "version is $VERSION_TAG" - name: 'Azure CLI login' uses: azure/login@v3 with: @@ -68,14 +77,20 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: 'Build and Push' + env: + DRIVER_VERSION: ${{ steps.load_config.outputs.cuda_version }} + IMAGE_REPO: ${{ matrix.image_repo }} + REGISTRY_SERVER: ${{ secrets.AZURE_REGISTRY_SERVER }} + VERSION: ${{ steps.semver.outputs.version }} run: | set -x echo "tag is: " - echo ${{ steps.semver.outputs.version }} - docker buildx build --platform linux/arm64/v8,linux/amd64 --build-arg DRIVER_KIND=cuda --build-arg DRIVER_VERSION=${{ steps.load_config.outputs.cuda_version }} --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t ${{ secrets.AZURE_REGISTRY_SERVER }}/public/aks/${{ matrix.image_repo }}:${{ steps.semver.outputs.version }} . + echo "$VERSION" + image_ref="${REGISTRY_SERVER}/public/aks/${IMAGE_REPO}:${VERSION}" + docker buildx build --platform linux/arm64/v8,linux/amd64 --build-arg DRIVER_KIND=cuda --build-arg "DRIVER_VERSION=${DRIVER_VERSION}" --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t "$image_ref" . docker images - az acr login -n ${{ secrets.AZURE_REGISTRY_SERVER }} - docker push ${{ secrets.AZURE_REGISTRY_SERVER }}/public/aks/${{ matrix.image_repo }}:${{ steps.semver.outputs.version }} + az acr login -n "$REGISTRY_SERVER" + docker push "$image_ref" - name: Move cache run: | rm -rf /tmp/.buildx-cache @@ -95,10 +110,16 @@ jobs: fetch-depth: 0 - name: Load CUDA config id: load_config + env: + CONFIG_KEY: ${{ matrix.config_key }} run: | - cuda_version=$(yq e '.${{ matrix.config_key }}.version' driver_config.yml) + cuda_version=$(yq e ".${CONFIG_KEY}.version" driver_config.yml) + if [[ ! "$cuda_version" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then + echo "::error::Refusing unexpected CUDA driver version: $cuda_version" + exit 1 + fi echo "CUDA_VERSION=$cuda_version" - echo "cuda_version=$cuda_version" >> $GITHUB_OUTPUT + echo "cuda_version=$cuda_version" >> "$GITHUB_OUTPUT" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Cache Docker layers @@ -110,16 +131,19 @@ jobs: ${{ runner.os }}-buildx-${{ matrix.image_repo }}- - name: Generate timestamp id: timestamp - run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT + run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> "$GITHUB_OUTPUT" - uses: paulhatch/semantic-version@v6.0.2 with: bump_each_commit: false version_format: "${{ steps.load_config.outputs.cuda_version }}-${{ steps.timestamp.outputs.timestamp }}" id: semver - name: 'Check version' + env: + VERSION: ${{ steps.semver.outputs.version }} + VERSION_TAG: ${{ steps.semver.outputs.version_tag }} run: | - echo "version is ${{ steps.semver.outputs.version }}" - echo "version is ${{ steps.semver.outputs.version_tag }}" + echo "version is $VERSION" + echo "version is $VERSION_TAG" - name: 'Azure CLI login' uses: azure/login@v3 with: @@ -127,14 +151,20 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: 'Build and Push' + env: + DRIVER_VERSION: ${{ steps.load_config.outputs.cuda_version }} + IMAGE_REPO: ${{ matrix.image_repo }} + REGISTRY_SERVER: ${{ secrets.AZURE_REGISTRY_SERVER }} + VERSION: ${{ steps.semver.outputs.version }} run: | set -x echo "tag is: " - echo ${{ steps.semver.outputs.version }} - docker buildx build --build-arg DRIVER_KIND=cuda --build-arg DRIVER_VERSION=${{ steps.load_config.outputs.cuda_version }} --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t ${{ secrets.AZURE_REGISTRY_SERVER }}/public/aks/${{ matrix.image_repo }}:${{ steps.semver.outputs.version }} . + echo "$VERSION" + image_ref="${REGISTRY_SERVER}/public/aks/${IMAGE_REPO}:${VERSION}" + docker buildx build --build-arg DRIVER_KIND=cuda --build-arg "DRIVER_VERSION=${DRIVER_VERSION}" --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t "$image_ref" . docker images - az acr login -n ${{ secrets.AZURE_REGISTRY_SERVER }} - docker push ${{ secrets.AZURE_REGISTRY_SERVER }}/public/aks/${{ matrix.image_repo }}:${{ steps.semver.outputs.version }} + az acr login -n "$REGISTRY_SERVER" + docker push "$image_ref" - name: Move cache run: | rm -rf /tmp/.buildx-cache @@ -154,18 +184,28 @@ jobs: fetch-depth: 0 - name: Load GRID config id: load_config + env: + CONFIG_KEY: ${{ matrix.config_key }} run: | - grid_version=$(yq e '.${{ matrix.config_key }}.version' driver_config.yml) - grid_url=$(yq e '.${{ matrix.config_key }}.url' driver_config.yml) + grid_version=$(yq e ".${CONFIG_KEY}.version" driver_config.yml) + grid_url=$(yq e ".${CONFIG_KEY}.url" driver_config.yml) + if [[ ! "$grid_version" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then + echo "::error::Refusing unexpected GRID driver version: $grid_version" + exit 1 + fi + if [[ ! "$grid_url" =~ ^https://[A-Za-z0-9._~:/?#@%+=-]+$ ]]; then + echo "::error::Refusing unexpected GRID driver URL: $grid_url" + exit 1 + fi echo "GRID_VERSION=$grid_version" echo "GRID_URL=$grid_url" - echo "grid_version=$grid_version" >> $GITHUB_OUTPUT - echo "grid_url=$grid_url" >> $GITHUB_OUTPUT + echo "grid_version=$grid_version" >> "$GITHUB_OUTPUT" + echo "grid_url=$grid_url" >> "$GITHUB_OUTPUT" - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Generate timestamp id: timestamp - run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT + run: echo "timestamp=$(date +'%Y%m%d%H%M%S')" >> "$GITHUB_OUTPUT" - uses: paulhatch/semantic-version@v6.0.2 - name: Cache Docker layers uses: actions/cache@v5 @@ -180,9 +220,12 @@ jobs: version_format: "${{ steps.load_config.outputs.grid_version }}-${{ steps.timestamp.outputs.timestamp }}" id: semver - name: 'Check version' + env: + VERSION: ${{ steps.semver.outputs.version }} + VERSION_TAG: ${{ steps.semver.outputs.version_tag }} run: | - echo "version is ${{ steps.semver.outputs.version }}" - echo "version is ${{ steps.semver.outputs.version_tag }}" + echo "version is $VERSION" + echo "version is $VERSION_TAG" - name: 'Azure CLI login' uses: azure/login@v3 with: @@ -190,14 +233,21 @@ jobs: tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} - name: 'Build and Push' + env: + DRIVER_URL: ${{ steps.load_config.outputs.grid_url }} + DRIVER_VERSION: ${{ steps.load_config.outputs.grid_version }} + IMAGE_REPO: ${{ matrix.image_repo }} + REGISTRY_SERVER: ${{ secrets.AZURE_REGISTRY_SERVER }} + VERSION: ${{ steps.semver.outputs.version }} run: | set -x echo "tag is: " - echo ${{ steps.semver.outputs.version }} - docker buildx build --build-arg DRIVER_URL=${{ steps.load_config.outputs.grid_url }} --build-arg DRIVER_KIND=grid --build-arg DRIVER_VERSION=${{ steps.load_config.outputs.grid_version }} --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t ${{ secrets.AZURE_REGISTRY_SERVER }}/public/aks/${{ matrix.image_repo }}:${{ steps.semver.outputs.version }} . + echo "$VERSION" + image_ref="${REGISTRY_SERVER}/public/aks/${IMAGE_REPO}:${VERSION}" + docker buildx build --build-arg "DRIVER_URL=${DRIVER_URL}" --build-arg DRIVER_KIND=grid --build-arg "DRIVER_VERSION=${DRIVER_VERSION}" --cache-from=type=local,src=/tmp/.buildx-cache --cache-to=type=local,dest=/tmp/.buildx-cache-new --output=type=docker -t "$image_ref" . docker images - az acr login -n ${{ secrets.AZURE_REGISTRY_SERVER }} - docker push ${{ secrets.AZURE_REGISTRY_SERVER }}/public/aks/${{ matrix.image_repo }}:${{ steps.semver.outputs.version }} + az acr login -n "$REGISTRY_SERVER" + docker push "$image_ref" - name: Move cache run: | rm -rf /tmp/.buildx-cache diff --git a/.github/workflows/update_grid_driver.yaml b/.github/workflows/update_grid_driver.yaml index fb785858..23b4360c 100644 --- a/.github/workflows/update_grid_driver.yaml +++ b/.github/workflows/update_grid_driver.yaml @@ -32,11 +32,11 @@ jobs: - name: Check for changes id: git-check run: | - git diff --exit-code driver_config.yml || echo "changes=true" >> $GITHUB_OUTPUT + git diff --exit-code driver_config.yml || echo "changes=true" >> "$GITHUB_OUTPUT" NEW_VERSION=$(git diff driver_config.yml | grep '^\+ version: ' | tail -n1 | cut -d'"' -f2) if [ ! -z "$NEW_VERSION" ]; then - echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT - echo "changes=true" >> $GITHUB_OUTPUT + echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT" + echo "changes=true" >> "$GITHUB_OUTPUT" fi - name: Azure login @@ -51,13 +51,16 @@ jobs: id: get-private-key if: steps.git-check.outputs.changes == 'true' uses: azure/cli@v3 + env: + AZURE_KV_NAME: ${{ secrets.AZURE_KV_NAME }} + APP_PRIVATE_KEY_SECRET_NAME: ${{ secrets.APP_PRIVATE_KEY_SECRET_NAME }} with: azcliversion: latest inlineScript: | # https://github.com/actions/create-github-app-token?tab=readme-ov-file#inputs - private_key=$(az keyvault secret show --vault-name ${{ secrets.AZURE_KV_NAME }} -n ${{ secrets.APP_PRIVATE_KEY_SECRET_NAME }} --query value -o tsv | sed 's/$/\\n/g' | tr -d '\n' | head -c -2) &> /dev/null + private_key=$(az keyvault secret show --vault-name "$AZURE_KV_NAME" -n "$APP_PRIVATE_KEY_SECRET_NAME" --query value -o tsv | sed 's/$/\\n/g' | tr -d '\n' | head -c -2) &> /dev/null echo "::add-mask::$private_key" - echo "private-key=$private_key" >> $GITHUB_OUTPUT + echo "private-key=$private_key" >> "$GITHUB_OUTPUT" - name: Generate GitHub token if: steps.git-check.outputs.changes == 'true' diff --git a/auto_update.py b/auto_update.py index aff54bc4..4a2cf4db 100644 --- a/auto_update.py +++ b/auto_update.py @@ -1,6 +1,35 @@ import os +import re import requests from ruamel.yaml import YAML +from urllib.parse import urlparse + +# Values below are fetched from a remote JSON document and end up in +# driver_config.yml, which CI reads and passes to `docker build`. Treat them as +# untrusted input and reject anything that is not a plain version/URL so that +# shell metacharacters can never reach a workflow step. +DRIVER_VERSION_PATTERN = re.compile(r"\A[0-9]+(\.[0-9]+)+\Z") +DRIVER_URL_PATTERN = re.compile(r"\A[A-Za-z0-9._~:/?#@%+=-]+\Z") +ALLOWED_DRIVER_URL_HOSTS = frozenset({"download.microsoft.com"}) + + +def validate_driver_version(version): + if not isinstance(version, str) or not DRIVER_VERSION_PATTERN.match(version): + raise ValueError(f"Unexpected driver version from upstream: {version!r}") + return version + + +def validate_driver_url(url): + if not isinstance(url, str) or not DRIVER_URL_PATTERN.match(url): + raise ValueError(f"Unexpected driver URL from upstream: {url!r}") + + parsed = urlparse(url) + if parsed.scheme != "https": + raise ValueError(f"Driver URL must use https: {url!r}") + if parsed.hostname not in ALLOWED_DRIVER_URL_HOSTS: + raise ValueError(f"Driver URL host is not allowed: {url!r}") + return url + def get_latest_grid_driver(): # URL of the JSON file containing driver information @@ -15,8 +44,8 @@ def get_latest_grid_driver(): if grid_info: latest_version_info = grid_info['Versions'][0] - latest_version = latest_version_info['DriverVersion'] - latest_url = latest_version_info['Driver'][0]['DirLink'] + latest_version = validate_driver_version(latest_version_info['DriverVersion']) + latest_url = validate_driver_url(latest_version_info['Driver'][0]['DirLink']) return latest_version, latest_url raise Exception("Could not find latest GRID driver version")