From 989827a233f72c90ed5792d8f956c46e3cba564b Mon Sep 17 00:00:00 2001 From: Khaleel Al-Adhami Date: Tue, 12 May 2026 16:09:37 -0700 Subject: [PATCH] security hygiene on actions --- .github/scripts/publish/parse_tag.sh | 14 +++++++--- .github/workflows/auto_release_internal.yml | 10 +++++-- .github/workflows/build_packages.yml | 5 +++- .github/workflows/check_node_latest.yml | 3 +- .../workflows/check_outdated_dependencies.yml | 10 +++++-- .github/workflows/codeql.yml | 2 ++ .github/workflows/dependency-review.yml | 3 ++ .github/workflows/dispatch_release.yml | 28 +++++++++++++++---- .github/workflows/integration_app_harness.yml | 4 ++- .github/workflows/integration_tests.yml | 10 +++++-- .github/workflows/performance.yml | 3 +- .github/workflows/pre-commit.yml | 9 ++++-- .github/workflows/publish.yml | 4 +++ .../workflows/reflex_init_in_docker_test.yml | 5 ++-- .github/workflows/unit_tests.yml | 4 ++- 15 files changed, 88 insertions(+), 26 deletions(-) diff --git a/.github/scripts/publish/parse_tag.sh b/.github/scripts/publish/parse_tag.sh index c5cfc1017ae..290cc49fa51 100755 --- a/.github/scripts/publish/parse_tag.sh +++ b/.github/scripts/publish/parse_tag.sh @@ -3,12 +3,18 @@ set -euo pipefail : "${TAG:?}" -# Tag format: v1.2.3 for reflex, reflex-lucide-v0.1.0 for sub-packages -if [[ "$TAG" =~ ^v([0-9].*)$ ]]; then +# Tag format: v1.2.3 for reflex, reflex-lucide-v0.1.0 for sub-packages. +# Package and version are restricted to a safe character set so the captured +# groups can be interpolated into shell and written to $GITHUB_OUTPUT without +# escaping concerns (a tag can in principle come from any actor able to +# publish a release). +PKG_RE='[A-Za-z0-9_-]+' +VER_RE='[0-9][A-Za-z0-9.+-]*' +if [[ "$TAG" =~ ^v(${VER_RE})$ ]]; then PACKAGE="reflex" BUILD_DIR="." VERSION="${BASH_REMATCH[1]}" -elif [[ "$TAG" =~ ^(.+)-v([0-9].*)$ ]]; then +elif [[ "$TAG" =~ ^(${PKG_RE})-v(${VER_RE})$ ]]; then PACKAGE="${BASH_REMATCH[1]}" VERSION="${BASH_REMATCH[2]}" if [ -d "packages/$PACKAGE" ]; then @@ -18,7 +24,7 @@ elif [[ "$TAG" =~ ^(.+)-v([0-9].*)$ ]]; then exit 1 fi else - echo "Error: Tag '$TAG' does not match expected format (v* or -v*)" + echo "Error: Tag '$TAG' does not match expected format (v or -v)" exit 1 fi diff --git a/.github/workflows/auto_release_internal.yml b/.github/workflows/auto_release_internal.yml index dc0a6635f47..8236ba73d09 100644 --- a/.github/workflows/auto_release_internal.yml +++ b/.github/workflows/auto_release_internal.yml @@ -18,18 +18,20 @@ on: - reflex-site-shared permissions: - contents: write - actions: write + contents: read jobs: detect: runs-on: ubuntu-latest + permissions: + contents: read outputs: packages: ${{ steps.detect.outputs.packages }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 2 + persist-credentials: false - id: detect env: EVENT_NAME: ${{ github.event_name }} @@ -40,6 +42,9 @@ jobs: needs: detect if: needs.detect.outputs.packages != '[]' runs-on: ubuntu-latest + permissions: + contents: write + actions: write strategy: matrix: package: ${{ fromJson(needs.detect.outputs.packages) }} @@ -52,6 +57,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - name: Compute next version id: version env: diff --git a/.github/workflows/build_packages.yml b/.github/workflows/build_packages.yml index 62f78d89ccc..39e76a6b002 100644 --- a/.github/workflows/build_packages.yml +++ b/.github/workflows/build_packages.yml @@ -20,6 +20,8 @@ jobs: builds: ${{ steps.discover.outputs.builds }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - id: discover run: | set -euo pipefail @@ -43,9 +45,10 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: - python-version: 3.14 + python-version: "3.14" run-uv-sync: false - name: Build ${{ matrix.name }} run: uv build --directory "${{ matrix.dir }}" diff --git a/.github/workflows/check_node_latest.yml b/.github/workflows/check_node_latest.yml index a964091b061..8d90962e0a4 100644 --- a/.github/workflows/check_node_latest.yml +++ b/.github/workflows/check_node_latest.yml @@ -27,9 +27,10 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: - python-version: 3.13 + python-version: "3.13" run-uv-sync: true - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 diff --git a/.github/workflows/check_outdated_dependencies.yml b/.github/workflows/check_outdated_dependencies.yml index fedf5ace7a5..74524e56d43 100644 --- a/.github/workflows/check_outdated_dependencies.yml +++ b/.github/workflows/check_outdated_dependencies.yml @@ -18,9 +18,10 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: - python-version: 3.13 + python-version: "3.13" run-uv-sync: true - name: Check outdated backend dependencies @@ -48,10 +49,13 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: - python-version: 3.13 + python-version: "3.13" + - name: Install Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - name: Install Requirements for reflex-docs working-directory: ./docs/app @@ -65,7 +69,7 @@ jobs: - name: Check outdated frontend dependencies working-directory: ./docs/app/.web run: | - raw_outdated=$(/home/runner/.local/share/reflex/bun/bin/bun outdated) + raw_outdated=$(bun outdated) outdated=$(echo "$raw_outdated" | grep -vE '\|\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\|' || true) echo "Outdated:" echo "$outdated" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 23194956baa..e972f0b8339 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -58,6 +58,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false # Add any setup steps before running the `github/codeql-action/init` action. # This includes steps like installing compilers or runtimes (`actions/setup-node` diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index b123e804295..223e7c61ba5 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -10,8 +10,11 @@ jobs: steps: - name: "Checkout Repository" uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: "Dependency Review" uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 with: + fail-on-severity: high allow-licenses: Apache-2.0, BSD-2-Clause, BSD-3-Clause, HPND, ISC, MIT, MPL-2.0, Unlicense, Python-2.0, Python-2.0.1, Apache-2.0 AND MIT, BSD-2-Clause AND BSD-3-Clause, Apache-2.0 AND BSD-3-Clause allow-dependencies-licenses: "pkg:pypi/lazy-loader" diff --git a/.github/workflows/dispatch_release.yml b/.github/workflows/dispatch_release.yml index e8d28ec9e4f..a329004d142 100644 --- a/.github/workflows/dispatch_release.yml +++ b/.github/workflows/dispatch_release.yml @@ -1,8 +1,11 @@ name: Dispatch release -# Note: any action starting with `release-` is gated on the `dispatch-release-approval` -# environment. Configure required reviewers for that environment in repo Settings → -# Environments to actually enforce the second approval; otherwise the gate is a no-op. +# REQUIRED CONFIGURATION: any action starting with `release-` is gated on the +# `dispatch-release-approval` environment. The gate only enforces a second +# approval if that environment has required reviewers configured in repo +# Settings → Environments. Without reviewers configured, the gate job +# auto-succeeds and provides NO protection — verify the environment is set up +# before relying on this workflow for production releases. on: workflow_dispatch: @@ -89,16 +92,19 @@ on: default: false permissions: - contents: write - actions: write + contents: read jobs: detect: runs-on: ubuntu-latest + permissions: + contents: read outputs: packages: ${{ steps.detect.outputs.packages }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - id: detect env: hatch_reflex_pyi: ${{ inputs.hatch_reflex_pyi }} @@ -122,6 +128,8 @@ jobs: plan: needs: detect runs-on: ubuntu-latest + permissions: + contents: read outputs: releases: ${{ steps.plan.outputs.releases }} steps: @@ -129,6 +137,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 - id: plan env: @@ -140,9 +149,13 @@ jobs: needs: plan if: ${{ startsWith(inputs.action, 'release-') }} runs-on: ubuntu-latest + permissions: + contents: read environment: dispatch-release-approval steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Show release plan env: RELEASES: ${{ needs.plan.outputs.releases }} @@ -153,6 +166,9 @@ jobs: needs: [plan, gate] if: ${{ always() && needs.plan.result == 'success' && (needs.gate.result == 'success' || needs.gate.result == 'skipped') }} runs-on: ubuntu-latest + permissions: + contents: write + actions: write strategy: matrix: include: ${{ fromJson(needs.plan.outputs.releases) }} @@ -162,6 +178,8 @@ jobs: cancel-in-progress: false steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false - name: Create GitHub release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/integration_app_harness.yml b/.github/workflows/integration_app_harness.yml index 0e1e65b89d2..bb599268960 100644 --- a/.github/workflows/integration_app_harness.yml +++ b/.github/workflows/integration_app_harness.yml @@ -1,7 +1,7 @@ name: integration-app-harness concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.id }} + group: ${{ github.workflow }}-${{ github.event.pull_request.id || github.sha }} cancel-in-progress: true on: @@ -48,6 +48,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} @@ -84,6 +85,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index f84295598cb..b93db4b9016 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -11,7 +11,7 @@ on: - "**/*.md" concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.id }} + group: ${{ github.workflow }}-${{ github.event.pull_request.id || github.sha }} cancel-in-progress: true permissions: @@ -30,7 +30,6 @@ env: PYTHONIOENCODING: "utf8" REFLEX_TELEMETRY_ENABLED: false NODE_OPTIONS: "--max_old_space_size=8192" - PR_TITLE: ${{ github.event.pull_request.title }} jobs: example-counter-and-nba-proxy: @@ -51,6 +50,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} @@ -61,6 +61,7 @@ jobs: with: repository: reflex-dev/reflex-examples path: reflex-examples + persist-credentials: false - name: Install requirements for counter example working-directory: ./reflex-examples/counter run: | @@ -117,6 +118,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false # Install sfw BEFORE any dependency installation so all packages are scanned. - name: Install Socket.dev Firewall (free) @@ -176,9 +178,10 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: - python-version: 3.14 + python-version: "3.14" run-uv-sync: true - name: Create app directory @@ -219,6 +222,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index 8cd13916597..571811116ba 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -14,7 +14,6 @@ on: env: REFLEX_TELEMETRY_ENABLED: false NODE_OPTIONS: "--max_old_space_size=8192" - PR_TITLE: ${{ github.event.pull_request.title }} APP_HARNESS_HEADLESS: 1 PYTHONUNBUFFERED: 1 @@ -27,6 +26,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 @@ -54,6 +54,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index d2d44c10e0e..3e30d9790eb 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -3,7 +3,7 @@ permissions: contents: read concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.id }} + group: ${{ github.workflow }}-${{ github.event.pull_request.id || github.sha }} cancel-in-progress: true on: @@ -24,13 +24,18 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: - python-version: 3.14 + python-version: "3.14" run-uv-sync: true + # `uv sync` mutates tracked files (e.g. pyi_hashes.json), which would + # cause pre-commit to flag changes that aren't from the PR. Re-checkout + # with clean: false to restore source files while keeping the .venv. - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: clean: false fetch-tags: true fetch-depth: 0 + persist-credentials: false - run: uv run pre-commit run --all-files --show-diff-on-failure diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f5bac1ace9d..8385c0302b5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -11,6 +11,9 @@ on: description: "Release tag (e.g. v1.2.3 or reflex-lucide-v0.1.0)" required: true +permissions: + contents: read + jobs: publish: runs-on: ubuntu-latest @@ -26,6 +29,7 @@ jobs: ref: ${{ github.event.release.tag_name || inputs.tag }} fetch-tags: true fetch-depth: 0 + persist-credentials: false - name: Install uv uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 diff --git a/.github/workflows/reflex_init_in_docker_test.yml b/.github/workflows/reflex_init_in_docker_test.yml index 122e4814666..d210bac2589 100644 --- a/.github/workflows/reflex_init_in_docker_test.yml +++ b/.github/workflows/reflex_init_in_docker_test.yml @@ -3,7 +3,7 @@ permissions: contents: read concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.id }} + group: ${{ github.workflow }}-${{ github.event.pull_request.id || github.sha }} cancel-in-progress: true on: @@ -27,6 +27,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - shell: bash run: | @@ -34,4 +35,4 @@ jobs: # cwd is repo root docker build -f tests/integration/init-test/Dockerfile -t reflex-init-test tests/integration/init-test - docker run --rm -v $(pwd):/reflex-repo/ reflex-init-test /reflex-repo/tests/integration/init-test/in_docker_test_script.sh + docker run --rm -v "$(pwd):/reflex-repo" reflex-init-test /reflex-repo/tests/integration/init-test/in_docker_test_script.sh diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index 389858016e0..d24923c456c 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -1,7 +1,7 @@ name: unit-tests concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.id }} + group: ${{ github.workflow }}-${{ github.event.pull_request.id || github.sha }} cancel-in-progress: true on: @@ -50,6 +50,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} @@ -94,6 +95,7 @@ jobs: with: fetch-tags: true fetch-depth: 0 + persist-credentials: false - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }}