Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions .actionlint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
self-hosted-runner:
labels:
- arm-runner
- blacksmith-4vcpu-ubuntu-2404-arm
2 changes: 1 addition & 1 deletion .github/workflows/beacon_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
otp-version: 27.x # Define the OTP version [required]
elixir-version: 1.18.x # Define the elixir version [required]
- name: Cache Mix
uses: actions/cache@v5
uses: useblacksmith/cache@v5
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# First, check if the file exists and examine line 37
if [ -f ".github/workflows/beacon_tests.yml" ]; then
  echo "=== File exists. Checking line 37 context ==="
  sed -n '30,45p' .github/workflows/beacon_tests.yml
else
  echo "File not found"
fi

# List all workflow files for reference
echo ""
echo "=== All workflow files ==="
fd -e yml -e yaml .github/workflows

Repository: supabase/realtime

Length of output: 1267


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Now search for all third-party GitHub Actions with non-SHA pinning
# This will help identify if useblacksmith/cache@v5 appears and if there are similar issues
echo "=== Searching for non-SHA-pinned third-party actions ==="

mapfile -t WORKFLOWS < <(fd -e yml -e yaml .github/workflows)
if [ ${`#WORKFLOWS`[@]} -gt 0 ]; then
  rg -nP '^\s*-?\s*uses:\s+' "${WORKFLOWS[@]}" | head -50
else
  echo "No workflow files found"
fi

Repository: supabase/realtime

Length of output: 191


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# More specifically, search for useblacksmith/cache usage
echo "=== Searching for useblacksmith/cache usage ==="
rg -rn "useblacksmith/cache" .github/workflows/

Repository: supabase/realtime

Length of output: 453


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Get all workflow files correctly
echo "=== All workflow files ==="
fd . .github/workflows -e yml -e yaml | sort

echo ""
echo "=== Search for all 'uses' statements in workflows ==="
rg "uses:" .github/workflows/ --no-heading -o | sort | uniq

Repository: supabase/realtime

Length of output: 858


🌐 Web query:

useblacksmith/cache GitHub Action security risk mutable tags

💡 Result:

Using useblacksmith/cache@v5 (or any @vN/@main) carries the standard GitHub Actions supply-chain risk of mutable refs: the tag can be force-moved to a different commit, so a repo compromise (or malicious maintainer action) can make your workflow run new code without any change in your workflow file. Blacksmith’s own docs show migration via useblacksmith/cache@v5, which is a mutable major tag by design. [1][2]

This isn’t theoretical: in the tj-actions/changed-files incident (CVE-2025-30066), attackers updated the action and moved version tags to point at a malicious commit, exposing secrets in workflow logs. [3]

Mitigation (best practice):

  • Pin the action to an immutable commit SHA, not @v5/@main. (You can still keep a human-readable comment like # v5.x.y and periodically bump the SHA in a controlled PR.) This avoids silent tag retargeting. [4]

Sources
[1] Blacksmith docs – “Actions” (shows useblacksmith/cache@v5 usage) (docs.blacksmith.sh)
[2] GitHub Docs – tags can be force-moved (git tag -f …, git push -f --tags) (docs.github.com)
[3] Report on tj-actions compromise + tag retargeting (CVE-2025-30066) (safetydetectives.com)
[4] Discussion of mutable refs vs commit-SHA pinning risk in GitHub Actions (orca.security)

Citations:


Pin useblacksmith/cache to an immutable commit SHA before merge.

Line 37 uses useblacksmith/cache@v5 (mutable tag). This is a CI supply-chain risk; a retag can change executed code without PR changes (precedent: CVE-2025-30066 with tj-actions/changed-files). Please pin to a full 40-char commit SHA here and in other workflows (tests.yml, prod_linter.yml, rust.yml, lint.yml).

🔒 Minimal fix pattern
-        uses: useblacksmith/cache@v5
+        uses: useblacksmith/cache@<verified-40-char-commit-sha>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/beacon_tests.yml at line 37, Replace the mutable tag
useblacksmith/cache@v5 with an immutable 40-character commit SHA (e.g.,
useblacksmith/cache@<full-commit-sha>) to eliminate supply-chain risk; update
the same pin in all other workflow files that reference useblacksmith/cache
(tests.yml, prod_linter.yml, rust.yml, lint.yml) so every workflow uses the
exact same full commit SHA rather than a short tag.

with:
path: |
beacon/deps
Expand Down
68 changes: 65 additions & 3 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,78 @@ on:
branches:
- main

env:
POSTGRES_IMAGE: supabase/postgres:17.6.1.074

jobs:
build:
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: useblacksmith/setup-docker-builder@v1

- name: Build Docker image
run: docker build .
uses: useblacksmith/build-push-action@v2
with:
push: false

build-burrito:
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- burrito_target: linux_amd64
runner: blacksmith-4vcpu-ubuntu-2404
- burrito_target: linux_arm64
runner: blacksmith-4vcpu-ubuntu-2404-arm

steps:
- name: Checkout code
uses: actions/checkout@v6

- name: Set up Docker Buildx
uses: useblacksmith/setup-docker-builder@v1

- name: Cache Docker images
uses: actions/cache@v5
id: docker-cache
with:
path: /tmp/docker-images
key: docker-images-zstd-${{ env.POSTGRES_IMAGE }}
- name: Load Docker images from cache
if: steps.docker-cache.outputs.cache-hit == 'true'
run: zstd -d --stdout /tmp/docker-images/postgres.tar.zst | docker image load
- name: Pull and save Docker images
if: steps.docker-cache.outputs.cache-hit != 'true'
run: |
docker pull ${{ env.POSTGRES_IMAGE }}
mkdir -p /tmp/docker-images
docker image save ${{ env.POSTGRES_IMAGE }} | zstd -T0 -o /tmp/docker-images/postgres.tar.zst

- name: Build realtime image with cache
uses: useblacksmith/build-push-action@v2
with:
load: true
build-args: BURRITO_TARGET=${{ matrix.burrito_target }}
tags: realtime-realtime:latest

- name: Build and start Burrito binary
env:
BURRITO_TARGET: ${{ matrix.burrito_target }}
run: docker compose -p realtime -f docker-compose.burrito.yml up --wait

- name: Test healthcheck endpoint
run: curl -f http://localhost:4001/healthcheck

- name: Print logs on failure
if: failure()
run: docker compose -p realtime -f docker-compose.burrito.yml logs

- name: Tear down
if: always()
run: docker compose -p realtime -f docker-compose.burrito.yml down
27 changes: 0 additions & 27 deletions .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,32 +33,5 @@ jobs:

steps:
- uses: actions/checkout@v6
- name: Cache Docker images
uses: actions/cache@v5
id: docker-cache
with:
path: /tmp/docker-images
key: docker-images-integration-zstd-${{ env.POSTGRES_IMAGE }}-${{ env.DENO_IMAGE }}
- name: Load Docker images from cache
if: steps.docker-cache.outputs.cache-hit == 'true'
run: |
zstd -d --stdout /tmp/docker-images/postgres.tar.zst | docker image load &
PID1=$!
zstd -d --stdout /tmp/docker-images/deno.tar.zst | docker image load &
PID2=$!
wait $PID1 || exit $?
wait $PID2 || exit $?
- name: Pull and save Docker images
if: steps.docker-cache.outputs.cache-hit != 'true'
run: |
docker pull ${{ env.POSTGRES_IMAGE }} &
PID1=$!
docker pull ${{ env.DENO_IMAGE }} &
PID2=$!
wait $PID1 || exit $?
wait $PID2 || exit $?
mkdir -p /tmp/docker-images
docker image save ${{ env.POSTGRES_IMAGE }} | zstd -T0 -o /tmp/docker-images/postgres.tar.zst
docker image save ${{ env.DENO_IMAGE }} | zstd -T0 -o /tmp/docker-images/deno.tar.zst
- name: Run integration test
run: docker compose -f docker-compose.tests.yml up --abort-on-container-exit --exit-code-from test-runner
4 changes: 2 additions & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
otp-version: 27.x # Define the OTP version [required]
elixir-version: 1.18.x # Define the elixir version [required]
- name: Cache Mix
uses: actions/cache@v5
uses: useblacksmith/cache@v5
with:
path: |
deps
Expand All @@ -59,7 +59,7 @@ jobs:
- name: Run sobelow
run: mix sobelow --config .sobelow-conf
- name: Retrieve PLT Cache
uses: actions/cache@v5
uses: useblacksmith/cache@v5
id: plt-cache
with:
path: priv/plts
Expand Down
98 changes: 98 additions & 0 deletions .github/workflows/prod_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,104 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ACTION }}

binary_linux_amd64:
needs: release
runs-on: blacksmith-4vcpu-ubuntu-2404
if: needs.release.outputs.published == 'true'
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
with:
ref: v${{ needs.release.outputs.version }}

- uses: erlef/setup-beam@v1
with:
otp-version: 27.x
elixir-version: 1.18.x

- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-unknown-linux-musl

- run: mix deps.get --only prod

- name: Build Burrito binary
env:
MIX_ENV: prod
BURRITO_TARGET: linux_amd64
SECRET_KEY_BASE: ${{ secrets.SECRET_KEY_BASE }}
run: mix release

- name: Upload binary to GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ACTION }}
VERSION: ${{ needs.release.outputs.version }}
run: gh release upload "v${VERSION}" burrito_out/realtime_linux_amd64 --clobber

binary_linux_arm64:
needs: release
runs-on: arm-runner
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find actionlint configuration files
echo "=== Actionlint config files ==="
fd -HI 'actionlint' . --type f
fd -HI 'actionlint\.ya?ml$' .

# Check GitHub Actions workflows directory structure
echo -e "\n=== Workflow files in .github/workflows ==="
ls -la .github/workflows/ 2>/dev/null || echo "No .github/workflows found"

# Find all runs-on declarations in workflows
echo -e "\n=== All runs-on declarations ==="
rg -n 'runs-on:' .github/workflows --color=never -A 1 -B 1

# Check for any runner configuration files
echo -e "\n=== Files mentioning runner configuration ==="
rg -l 'runner' .github/ --type yml --type yaml 2>/dev/null | head -20

# Search for arm-runner and blacksmith definitions
echo -e "\n=== Search for arm-runner and blacksmith labels ==="
rg 'arm-runner|blacksmith' . --type yml --type yaml --color=never

Repository: supabase/realtime

Length of output: 5286


🏁 Script executed:

#!/bin/bash
# Check if these are standard GitHub-hosted runners
echo "=== Standard GitHub runners documentation check ==="
rg 'ubuntu|macos|windows' .github/workflows --type yml | head -20

# Look for any GitHub runner setup or configuration
echo -e "\n=== Check for runner setup/registration ==="
rg 'runs-on' .github/workflows -A 2 -B 2 --type yml | grep -E '(runs-on|self-hosted|labels)' | head -30

# Check if there's a .github/actionlint.yaml file or similar
echo -e "\n=== Check .github directory contents ==="
find .github -type f -name '*actionlint*' -o -name '*runner*' 2>/dev/null

Repository: supabase/realtime

Length of output: 296


🏁 Script executed:

#!/bin/bash
# Read the actionlint.yaml configuration
cat .github/actionlint.yaml

Repository: supabase/realtime

Length of output: 160


🏁 Script executed:

#!/bin/bash
# Check the docker-build.yml to see what the matrix.runner variable contains
echo "=== docker-build.yml content around matrix.runner ==="
cat .github/workflows/docker-build.yml | grep -A 10 -B 5 'matrix.runner'

Repository: supabase/realtime

Length of output: 513


Unregistered runner labels will cause actionlint validation to fail.

arm-runner (prod_build.yml lines 74, 177 and manual_prod_build.yml line 44) and blacksmith-4vcpu-ubuntu-2404-arm (docker-build.yml matrix) are not declared in .github/actionlint.yaml, which registers only blacksmith-4vcpu-ubuntu-2404 and blacksmith-8vcpu-ubuntu-2404. Either add these labels to actionlint config if they are self-hosted runners, or use registered labels.

🧰 Tools
🪛 actionlint (1.7.11)

[error] 74-74: label "arm-runner" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2025-vs2026", "windows-2022", "windows-11-arm", "ubuntu-slim", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xlarge", "macos-latest-large", "macos-26-xlarge", "macos-26-large", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xlarge", "macos-14-large", "macos-14", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows", "blacksmith-4vcpu-ubuntu-2404", "blacksmith-8vcpu-ubuntu-2404". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/prod_build.yml at line 74, The workflow uses unregistered
runner labels (runs-on: arm-runner and the matrix entry
blacksmith-4vcpu-ubuntu-2404-arm) which causes actionlint to fail; either add
those labels to the repository's actionlint configuration (register the
self-hosted labels) or change the workflows to use existing registered labels
(e.g., blacksmith-4vcpu-ubuntu-2404 or blacksmith-8vcpu-ubuntu-2404) wherever
you see runs-on: arm-runner and blacksmith-4vcpu-ubuntu-2404-arm in the affected
workflow files.

if: needs.release.outputs.published == 'true'
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
with:
ref: v${{ needs.release.outputs.version }}

- uses: erlef/setup-beam@v1
with:
otp-version: 27.x
elixir-version: 1.18.x

- uses: dtolnay/rust-toolchain@stable

- run: mix deps.get --only prod

- name: Build Burrito binary
env:
MIX_ENV: prod
BURRITO_TARGET: linux_arm64
SECRET_KEY_BASE: ${{ secrets.SECRET_KEY_BASE }}
run: mix release

- name: Upload binary to GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ACTION }}
VERSION: ${{ needs.release.outputs.version }}
run: gh release upload "v${VERSION}" burrito_out/realtime_linux_arm64 --clobber

binary_macos_arm64:
needs: release
runs-on: macos-15
if: needs.release.outputs.published == 'true'
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
with:
ref: v${{ needs.release.outputs.version }}

- uses: erlef/setup-beam@v1
with:
otp-version: 27.x
elixir-version: 1.18.x

- uses: dtolnay/rust-toolchain@stable

- run: mix deps.get --only prod

- name: Build Burrito binary
env:
MIX_ENV: prod
BURRITO_TARGET: macos_arm64
SECRET_KEY_BASE: ${{ secrets.SECRET_KEY_BASE }}
run: mix release

- name: Upload binary to GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PROJECT_ACTION }}
VERSION: ${{ needs.release.outputs.version }}
run: gh release upload "v${VERSION}" burrito_out/realtime_macos_arm64 --clobber

docker_x86_release:
needs: release
runs-on: blacksmith-4vcpu-ubuntu-2404
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/prod_linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
otp-version: 27.x # Define the OTP version [required]
elixir-version: 1.18.x # Define the elixir version [required]
- name: Cache Mix
uses: actions/cache@v5
uses: useblacksmith/cache@v5
with:
path: deps
key: ${{ runner.os }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
Expand All @@ -36,7 +36,7 @@ jobs:
- name: Credo checks
run: mix credo --strict --mute-exit-status
- name: Retrieve PLT Cache
uses: actions/cache@v5
uses: useblacksmith/cache@v5
id: plt-cache
with:
path: priv/plts
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
components: rustfmt, clippy

- name: Cache Cargo
uses: actions/cache@v5
uses: useblacksmith/cache@v5
with:
path: |
~/.cargo/registry/index
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
otp-version: 27.x # Define the OTP version [required]
elixir-version: 1.18.x # Define the elixir version [required]
- name: Cache Mix
uses: actions/cache@v5
uses: useblacksmith/cache@v5
with:
path: |
deps
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ demo/.env

# Rust/Rustler build artifacts
/native/*/target/
/priv/native/
/priv/native/

# Burrito binary output
/burrito_out/
Loading
Loading