From b8153ac7c3fbf3b15416d53561e085bf822c6770 Mon Sep 17 00:00:00 2001 From: ffrancis Date: Thu, 30 Jul 2026 17:44:59 +0530 Subject: [PATCH] Harden aws-s3-exchanger shell execution and file handling Move GitHub expressions out of shell blocks and consume inputs through environment variables. Normalize location prefixes, enable strict shell mode, create URL artifact directories, and apply safer file permissions while preserving SDK script executability. Signed-off-by: ffrancis --- .github/actions/aws-s3-exchanger/action.yml | 3 +- .github/actions/build/action.yml | 64 +++++++++++++++++---- .github/workflows/build.yml | 20 ++++++- .github/workflows/process_image.yml | 38 ++++++++---- .github/workflows/test.yml | 7 ++- 5 files changed, 103 insertions(+), 29 deletions(-) diff --git a/.github/actions/aws-s3-exchanger/action.yml b/.github/actions/aws-s3-exchanger/action.yml index bd96cb3..7f2614f 100644 --- a/.github/actions/aws-s3-exchanger/action.yml +++ b/.github/actions/aws-s3-exchanger/action.yml @@ -77,7 +77,7 @@ runs: echo "Downloading files from S3 bucket..." aws s3 cp "s3://${INPUT_S3_BUCKET}/${location_prefix}${INPUT_DOWNLOAD_FILENAME}" . echo "::endgroup::" - chmod 0644 "${INPUT_DOWNLOAD_FILENAME}" + chmod 0755 "${INPUT_DOWNLOAD_FILENAME}" echo "Downloaded ${INPUT_DOWNLOAD_FILENAME} from s3://${INPUT_S3_BUCKET}/${INPUT_LOCATION}" ;; *) @@ -94,3 +94,4 @@ runs: name: presigned_url_${{ env.IMAGE_NAME }}.txt path: ${{ github.workspace }}/presigned_urls/presigned_url_${{ env.IMAGE_NAME }}.txt retention-days: 1 + diff --git a/.github/actions/build/action.yml b/.github/actions/build/action.yml index b35311d..624d63b 100644 --- a/.github/actions/build/action.yml +++ b/.github/actions/build/action.yml @@ -22,14 +22,48 @@ runs: env: IMAGE_NAME: ${{ env.IMAGE_NAME }} + CONFIG_SCRIPT_VALUE: ${{ env.CONFIG_SCRIPT }} + SYNC_SCRIPT_VALUE: ${{ env.SYNC_SCRIPT }} + APPLY_PATCH_SCRIPT_VALUE: ${{ env.APPLY_PATCH_SCRIPT }} + BUILD_ARGS_VALUE: ${{ env.BUILD_ARGS }} + BUILD_SCRIPT_VALUE: ${{ env.BUILD_SCRIPT }} + DOCKER_IMAGE_VALUE: ${{ inputs.docker_image }} run: | # Load the build arguments + set -euo pipefail set +x - export GITHUB_WORKSPACE="${{ github.workspace }}" - [ -f "./${{ env.CONFIG_SCRIPT }}" ] && export CONFIG_SCRIPT="${{ env.CONFIG_SCRIPT }}" - [ -f "./${{ env.SYNC_SCRIPT }}" ] && source "./${{ env.SYNC_SCRIPT }}" - [ -f "./${{ env.APPLY_PATCH_SCRIPT }}" ] && source "./${{ env.APPLY_PATCH_SCRIPT }}" + + is_safe_relative_path() { + local p="$1" + [[ "$p" =~ ^[A-Za-z0-9._/-]+$ ]] && [[ "$p" != /* ]] && [[ "$p" != *".."* ]] + } + + maybe_source_script() { + local script_path="$1" + if [ -z "${script_path}" ]; then + return 0 + fi + if ! is_safe_relative_path "${script_path}"; then + echo "Unsafe script path: ${script_path}" >&2 + exit 1 + fi + if [ -f "./${script_path}" ]; then + # shellcheck disable=SC1090 + source "./${script_path}" + fi + } + + if [ -n "${CONFIG_SCRIPT_VALUE}" ]; then + if ! is_safe_relative_path "${CONFIG_SCRIPT_VALUE}"; then + echo "Unsafe script path: ${CONFIG_SCRIPT_VALUE}" >&2 + exit 1 + fi + [ -f "./${CONFIG_SCRIPT_VALUE}" ] && export CONFIG_SCRIPT="${CONFIG_SCRIPT_VALUE}" + fi + + maybe_source_script "${SYNC_SCRIPT_VALUE}" + maybe_source_script "${APPLY_PATCH_SCRIPT_VALUE}" # Build inside a docker container docker run \ @@ -37,24 +71,32 @@ runs: --user $(id -u):$(id -g) \ -v $PWD/..:$PWD/.. \ -w $PWD \ - -e GITHUB_WORKSPACE="${{ github.workspace }}" \ - -e BUILD_ARGS="${{ env.BUILD_ARGS }}" \ - -e BUILD_SCRIPT="${{ env.BUILD_SCRIPT }}" \ + -e GITHUB_WORKSPACE="${GITHUB_WORKSPACE}" \ + -e BUILD_ARGS="${BUILD_ARGS_VALUE}" \ + -e BUILD_SCRIPT="${BUILD_SCRIPT_VALUE}" \ --privileged \ - ${{ inputs.docker_image }} \ + "${DOCKER_IMAGE_VALUE}" \ bash -c ' + set -euo pipefail echo "Running build script... : ${BUILD_SCRIPT}" ls -l "${BUILD_SCRIPT}" || (echo "Build script not found!" && exit 1) - source ./${BUILD_SCRIPT} + case "${BUILD_SCRIPT}" in + ""|/*|*..*|*[!A-Za-z0-9._/-]*) + echo "Unsafe build script path: ${BUILD_SCRIPT}" && exit 1 + ;; + esac + source "./${BUILD_SCRIPT}" ' - name: Create build.tar id: create_build_tar shell: bash + env: + IMAGE_NAME_VALUE: ${{ env.IMAGE_NAME }} run: | # Create a tarball of the build directory - tar -czf build_${{ env.IMAGE_NAME }}.tar -C ${{ github.workspace }}/build . - echo "Build tar created at ${{ github.workspace }}/build_${{ env.IMAGE_NAME }}.tar" + tar -czf "build_${IMAGE_NAME_VALUE}.tar" -C "${GITHUB_WORKSPACE}/build" . + echo "Build tar created at ${GITHUB_WORKSPACE}/build_${IMAGE_NAME_VALUE}.tar" - name: Upload build.tar id: upload_build_tar diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 06923b1..e3e512d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -112,9 +112,18 @@ jobs: - name: Run SDK Script id: run_sdk shell: bash + env: + SDK_NAME_VALUE: ${{ env.SDK_NAME }} run: | + set -euo pipefail echo "Running SDK Script" - echo "./install" | ./${{ env.SDK_NAME }} + case "${SDK_NAME_VALUE}" in + ""|/*|*..*|*[!A-Za-z0-9._/-]*) + echo "Unsafe SDK script path: ${SDK_NAME_VALUE}" >&2 + exit 1 + ;; + esac + echo "./install" | "./${SDK_NAME_VALUE}" echo "SDK Script ran successfully" - name: Build @@ -122,6 +131,7 @@ jobs: uses: AudioReach/audioreach-workflows/.github/actions/build@master with: docker_image: ${{ steps.get-docker-image.outputs.image_name }} + build_matrix: ${{ inputs.build_matrix }} env: BUILD_ARGS: ${{ matrix.build_matrix.build_args }} BUILD_SCRIPT: ${{ matrix.build_matrix.build_script }} @@ -132,8 +142,12 @@ jobs: - name: Clean Up Workspace if: always() shell: bash + env: + SDK_NAME_VALUE: ${{ env.SDK_NAME }} + IMAGE_NAME_VALUE: ${{ env.IMAGE_NAME }} run: | + set -euo pipefail echo "Cleaning Up Workspace" - rm -rf install/ ${{ env.SDK_NAME }} - rm -rf build/ ${{ github.workspace }}/build_${{ env.IMAGE_NAME }}.tar + rm -rf "install/" "${SDK_NAME_VALUE}" + rm -rf "build/" "${GITHUB_WORKSPACE}/build_${IMAGE_NAME_VALUE}.tar" echo "Workspace cleaned up successfully" diff --git a/.github/workflows/process_image.yml b/.github/workflows/process_image.yml index db75928..0c130b9 100644 --- a/.github/workflows/process_image.yml +++ b/.github/workflows/process_image.yml @@ -79,13 +79,16 @@ jobs: - name: Extract build artifact id: extract_build_artifact shell: bash + env: + WORKSPACE_DIR: ${{ github.workspace }} + IMAGE_NAME_LOCAL: ${{ env.IMAGE_NAME }} run: | #!/bin/bash set -e - mkdir -p ${{ github.workspace }}/build + mkdir -p "${WORKSPACE_DIR}/build" echo "Extracting the build artifact" - tar -xvf ${{ github.workspace }}/build_${{ env.IMAGE_NAME }}.tar -C ${{ github.workspace }}/build + tar -xvf "${WORKSPACE_DIR}/build_${IMAGE_NAME_LOCAL}.tar" -C "${WORKSPACE_DIR}/build" echo "Build artifact extracted successfully" - name: Pull meta-audioreach pre compiled image @@ -103,15 +106,20 @@ jobs: id: extract_image if: ${{ env.IMAGE_NAME != 'raspberrypi4' }} shell: bash + env: + IMAGE_NAME_LOCAL: ${{ env.IMAGE_NAME }} + FILES_TO_COPY: ${{ inputs.files_to_copy }} + DOCKER_IMAGE: ${{ steps.get-docker-image.outputs.image_name }} run: | #!/bin/bash set -e + IMAGE_ARCHIVE="qcom-multimedia-proprietary-image-${IMAGE_NAME_LOCAL}.rootfs.qcomflash.tar.gz" echo "Extracting the image" - tar -xvf qcom-multimedia-proprietary-image-${{ env.IMAGE_NAME }}.rootfs.qcomflash.tar.gz - ROOTFS_IMG=$(tar -tf qcom-multimedia-proprietary-image-${{ env.IMAGE_NAME }}.rootfs.qcomflash.tar.gz | grep 'rootfs.img$') + tar -xvf "$IMAGE_ARCHIVE" + ROOTFS_IMG=$(tar -tf "$IMAGE_ARCHIVE" | grep 'rootfs.img$') ROOTFS_DIR=$(dirname "$ROOTFS_IMG") - EXPECTED_ROOTFS_DIR="qcom-multimedia-proprietary-image-${{ env.IMAGE_NAME }}" + EXPECTED_ROOTFS_DIR="qcom-multimedia-proprietary-image-${IMAGE_NAME_LOCAL}" if [ "$ROOTFS_DIR" != "$EXPECTED_ROOTFS_DIR" ] && [ -d "$ROOTFS_DIR" ]; then rm -rf "$EXPECTED_ROOTFS_DIR" mv "$ROOTFS_DIR" "$EXPECTED_ROOTFS_DIR" @@ -121,7 +129,7 @@ jobs: # Export as GitHub Actions environment variables echo "ROOTFS_IMG=$ROOTFS_IMG" >> $GITHUB_ENV echo "ROOTFS_DIR=$ROOTFS_DIR" >> $GITHUB_ENV - rm -rf qcom-multimedia-proprietary-image-${{ env.IMAGE_NAME }}.rootfs.qcomflash.tar.gz + rm -rf "$IMAGE_ARCHIVE" echo "Image extracted successfully" ls -l "$ROOTFS_DIR/rootfs.img" @@ -162,8 +170,8 @@ jobs: -v $PWD:/workspace \ -v /tmp/rootfs:/tmp/rootfs \ -w /workspace \ - -e "FILES_TO_COPY=${{ inputs.files_to_copy }}" \ - ${{ steps.get-docker-image.outputs.image_name }} \ + -e "FILES_TO_COPY=${FILES_TO_COPY}" \ + "${DOCKER_IMAGE}" \ bash -c ' set -xe set +e @@ -191,22 +199,28 @@ jobs: id: create_tar_image if: ${{ env.IMAGE_NAME != 'raspberrypi4' }} shell: bash + env: + IMAGE_NAME_LOCAL: ${{ env.IMAGE_NAME }} + ROOTFS_DIR_LOCAL: ${{ env.ROOTFS_DIR }} run: | #!/bin/bash set -e echo $PWD echo "Creating tar image for qcomflash directory" - tar -czvf qcom-multimedia-proprietary-image-${{ env.IMAGE_NAME }}.rootfs.qcomflash.tar.gz ${{ env.ROOTFS_DIR }}/ + tar -czvf "qcom-multimedia-proprietary-image-${IMAGE_NAME_LOCAL}.rootfs.qcomflash.tar.gz" "${ROOTFS_DIR_LOCAL}/" # ✅ Moving newly created tar files into presigned_urls path - name: Move tar to presigned_urls directory if: ${{ env.IMAGE_NAME != 'raspberrypi4' }} shell: bash + env: + WORKSPACE_DIR: ${{ github.workspace }} + IMAGE_NAME_LOCAL: ${{ env.IMAGE_NAME }} run: | set -e - mkdir -p ${{ github.workspace }}/presigned_urls + mkdir -p "${WORKSPACE_DIR}/presigned_urls" - FILE="qcom-multimedia-proprietary-image-${{ env.IMAGE_NAME }}.rootfs.qcomflash.tar.gz" + FILE="qcom-multimedia-proprietary-image-${IMAGE_NAME_LOCAL}.rootfs.qcomflash.tar.gz" if [ ! -f "$FILE" ]; then @@ -214,7 +228,7 @@ jobs: exit 1 fi - mv "$FILE" ${{ github.workspace }}/presigned_urls/ + mv "$FILE" "${WORKSPACE_DIR}/presigned_urls/" echo "Moved $FILE to presigned_urls/" - name: Upload tar image diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9fc7488..fe750b7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,10 +81,13 @@ jobs: - name: Get build URL id: get_build_url uses: actions/github-script@v8 + env: + BUILD_URLS_JSON: ${{ inputs.build_urls }} + IMAGE_NAME: ${{ matrix.build_matrix.image_name }} with: script: | - const urls = JSON.parse('${{ inputs.build_urls }}') - const image = '${{ matrix.build_matrix.image_name }}' + const urls = JSON.parse(process.env.BUILD_URLS_JSON) + const image = process.env.IMAGE_NAME const key = `presigned_url_${image}.txt` const matched = urls[key]