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
117 changes: 115 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,23 @@ on:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Setup Socket Firewall
uses: workos/setup-socket-firewall@ca93dd8aa351f54f4729fe3377a9be23c631c25d
with:
token: ${{ secrets.PUBLIC_SOCKET_FIREWALL_TOKEN }}
allow-external-fork-fallback: true
configure-bun: true

- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
Expand Down Expand Up @@ -86,6 +98,15 @@ jobs:
node: [22, 24]
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Setup Socket Firewall
uses: workos/setup-socket-firewall@ca93dd8aa351f54f4729fe3377a9be23c631c25d
with:
token: ${{ secrets.PUBLIC_SOCKET_FIREWALL_TOKEN }}
allow-external-fork-fallback: true
configure-bun: true

- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
Expand All @@ -109,8 +130,54 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Setup Socket Firewall
id: sfw
uses: workos/setup-socket-firewall@ca93dd8aa351f54f4729fe3377a9be23c631c25d
env:
XDG_CONFIG_HOME: ${{ runner.temp }}/sfw-docker-bun
with:
token: ${{ secrets.PUBLIC_SOCKET_FIREWALL_TOKEN }}
allow-external-fork-fallback: true
configure-bun: true

- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
- name: Build image with Socket Firewall
if: steps.sfw.outputs.active == 'true'
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
context: .
push: false
load: true
platforms: linux/amd64
tags: emulate:ci
build-args: |
SFW_REQUIRED=true
add-hosts: |
registry.npmjs.org:127.0.0.1
registry.yarnpkg.com:127.0.0.1
secret-files: |
sfw_bunfig=${{ runner.temp }}/sfw-docker-bun/.bunfig.toml

- name: Teardown Socket Firewall
if: ${{ always() && steps.sfw.outputs.active == 'true' }}
uses: workos/setup-socket-firewall/teardown@ca93dd8aa351f54f4729fe3377a9be23c631c25d

- name: Remove private Docker install config
if: ${{ always() }}
env:
SFW_BUN_CONFIG_FILE: ${{ runner.temp }}/sfw-docker-bun/.bunfig.toml
run: |
rm -f "$SFW_BUN_CONFIG_FILE"
if [ -e "$SFW_BUN_CONFIG_FILE" ]; then
echo "::error::Private Docker install config was not removed."
exit 1
fi

- name: Build image (external fork fallback)
if: steps.sfw.outputs.active == 'false'
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
context: .
push: false
Expand All @@ -135,8 +202,54 @@ jobs:
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Setup Socket Firewall
id: sfw
uses: workos/setup-socket-firewall@ca93dd8aa351f54f4729fe3377a9be23c631c25d
env:
XDG_CONFIG_HOME: ${{ runner.temp }}/sfw-docker-bun
with:
token: ${{ secrets.PUBLIC_SOCKET_FIREWALL_TOKEN }}
allow-external-fork-fallback: true
configure-bun: true

- name: Build image with Socket Firewall
if: steps.sfw.outputs.active == 'true'
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
context: .
push: false
load: true
platforms: linux/arm64
tags: emulate:ci
build-args: |
SFW_REQUIRED=true
add-hosts: |
registry.npmjs.org:127.0.0.1
registry.yarnpkg.com:127.0.0.1
secret-files: |
sfw_bunfig=${{ runner.temp }}/sfw-docker-bun/.bunfig.toml

- name: Teardown Socket Firewall
if: ${{ always() && steps.sfw.outputs.active == 'true' }}
uses: workos/setup-socket-firewall/teardown@ca93dd8aa351f54f4729fe3377a9be23c631c25d

- name: Remove private Docker install config
if: ${{ always() }}
env:
SFW_BUN_CONFIG_FILE: ${{ runner.temp }}/sfw-docker-bun/.bunfig.toml
run: |
rm -f "$SFW_BUN_CONFIG_FILE"
if [ -e "$SFW_BUN_CONFIG_FILE" ]; then
echo "::error::Private Docker install config was not removed."
exit 1
fi

- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
- name: Build image (external fork fallback)
if: steps.sfw.outputs.active == 'false'
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
context: .
push: false
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,23 @@ on:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Setup Socket Firewall
uses: workos/setup-socket-firewall@ca93dd8aa351f54f4729fe3377a9be23c631c25d
with:
token: ${{ secrets.PUBLIC_SOCKET_FIREWALL_TOKEN }}
allow-external-fork-fallback: true
configure-bun: true

- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
Expand Down
91 changes: 82 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ on:
type: string
required: true
secrets:
PUBLIC_SOCKET_FIREWALL_TOKEN:
description: Public Socket Firewall token for release dependency downloads
required: false
SDK_BOT_PRIVATE_KEY:
description: Private key for the org SDK bot GitHub App, used to mint a short-lived token scoped to workos/homebrew-tap
required: false
Expand Down Expand Up @@ -56,22 +59,43 @@ jobs:
with:
ref: ${{ inputs.tag_name }}

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: 22

- name: Setup Socket Firewall
id: sfw
uses: workos/setup-socket-firewall@ca93dd8aa351f54f4729fe3377a9be23c631c25d
with:
token: ${{ secrets.PUBLIC_SOCKET_FIREWALL_TOKEN }}
configure-bun: true

- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.14

- name: Install
run: bun install --frozen-lockfile

- name: Check out release tooling
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.workflow_sha }}
path: .release-tools
persist-credentials: false
sparse-checkout: scripts/build-binaries.sh
sparse-checkout-cone-mode: false

- name: Build standalone binary
run: >-
bun build
--compile
--no-compile-autoload-dotenv
--no-compile-autoload-bunfig
--target="${{ matrix.target }}"
./src/cli.ts
--outfile "dist/${{ matrix.asset }}"
env:
TAG_NAME: ${{ inputs.tag_name }}
TARGET: ${{ matrix.target }}
ASSET: ${{ matrix.asset }}
run: .release-tools/scripts/build-binaries.sh "$TAG_NAME" "$TARGET" "$ASSET"

- name: Teardown Socket Firewall
if: ${{ always() && steps.sfw.outputs.active == 'true' }}
uses: workos/setup-socket-firewall/teardown@ca93dd8aa351f54f4729fe3377a9be23c631c25d

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
Expand Down Expand Up @@ -202,9 +226,20 @@ jobs:
node-version: 24
registry-url: 'https://registry.npmjs.org'

- name: Setup Socket Firewall
id: sfw
uses: workos/setup-socket-firewall@ca93dd8aa351f54f4729fe3377a9be23c631c25d
with:
token: ${{ secrets.PUBLIC_SOCKET_FIREWALL_TOKEN }}
configure-bun: true

- name: Install
run: bun install --frozen-lockfile

- name: Teardown Socket Firewall
if: ${{ always() && steps.sfw.outputs.active == 'true' }}
uses: workos/setup-socket-firewall/teardown@ca93dd8aa351f54f4729fe3377a9be23c631c25d

- name: Build
run: bun run build

Expand Down Expand Up @@ -279,12 +314,50 @@ jobs:
- uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0

- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
- name: Setup Socket Firewall
id: sfw
uses: workos/setup-socket-firewall@ca93dd8aa351f54f4729fe3377a9be23c631c25d
env:
XDG_CONFIG_HOME: ${{ runner.temp }}/sfw-docker-bun
with:
token: ${{ secrets.PUBLIC_SOCKET_FIREWALL_TOKEN }}
configure-bun: true

- name: Require Socket Firewall for container publication
if: steps.sfw.outputs.active != 'true'
run: |
echo "::error::Socket Firewall must be active before container publication."
exit 1

- name: Publish image with Socket Firewall
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6.19.2
with:
context: .
platforms: linux/amd64,linux/arm64
push: true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 security Image Secrets Publish Unchecked

The release job now pushes the authenticated multi-platform image directly after removing the pre-publication scan of its layers and metadata. If BuildKit or a future Dockerfile change embeds the Socket Firewall token or configuration in an image layer, the workflow will publish that secret to GHCR without detecting it. Removing the runner configuration afterward cannot remove material already stored in the published image.

How this was verified: The workflow passes the private Bun configuration into the Docker build and pushes its output before performing any inspection capable of detecting the credential in image layers or metadata.

Prompt To Fix With AI
This is a comment left during a code review.
Path: .github/workflows/release.yml
Line: 324

Comment:
**Image Secrets Publish Unchecked**

The release job now pushes the authenticated multi-platform image directly after removing the pre-publication scan of its layers and metadata. If BuildKit or a future Dockerfile change embeds the Socket Firewall token or configuration in an image layer, the workflow will publish that secret to GHCR without detecting it. Removing the runner configuration afterward cannot remove material already stored in the published image.

**How this was verified:** The workflow passes the private Bun configuration into the Docker build and pushes its output before performing any inspection capable of detecting the credential in image layers or metadata.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

tags: ${{ steps.tags.outputs.tags }}
build-args: |
SFW_REQUIRED=true
add-hosts: |
registry.npmjs.org:127.0.0.1
registry.yarnpkg.com:127.0.0.1
secret-files: |
sfw_bunfig=${{ runner.temp }}/sfw-docker-bun/.bunfig.toml

- name: Teardown Socket Firewall
if: ${{ always() && steps.sfw.outputs.active == 'true' }}
uses: workos/setup-socket-firewall/teardown@ca93dd8aa351f54f4729fe3377a9be23c631c25d

- name: Remove private Docker install config
if: ${{ always() }}
env:
SFW_BUN_CONFIG_FILE: ${{ runner.temp }}/sfw-docker-bun/.bunfig.toml
run: |
rm -f "$SFW_BUN_CONFIG_FILE"
if [ -e "$SFW_BUN_CONFIG_FILE" ]; then
echo "::error::Private Docker install config was not removed."
exit 1
fi

homebrew:
name: Update Homebrew formula
Expand Down
29 changes: 27 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# syntax=docker/dockerfile:1

# CI/release callers pass Bun's Socket Firewall config as a BuildKit secret and
# set SFW_REQUIRED=true. Local Docker builds omit both and keep using Bun's
# normal public-registry configuration.
ARG SFW_REQUIRED=false

# Build stage: compile TypeScript to dist/ from the bun lockfile.
# bun.lock pins tree-sitter-kotlin (a transitive devDep via @workos/openapi-spec
# -> @workos/oagen) to a git+ssh URL that can't clone inside the image without
Expand All @@ -9,9 +14,19 @@
# other dependency — including typescript@5.9.3 — stays at its locked version,
# keeping the build reproducible.
FROM oven/bun:1.3.14 AS builder
ARG SFW_REQUIRED
WORKDIR /app
COPY package.json bun.lock ./
RUN sed -i \
RUN --mount=type=secret,id=sfw_bunfig,target=/run/secrets/.bunfig.toml \
set -eu; \
if [ "${SFW_REQUIRED:-false}" = "true" ] && [ ! -s /run/secrets/.bunfig.toml ]; then \
echo "Socket Firewall Bun config secret is required for Docker dependency installs." >&2; \
exit 1; \
fi; \
if [ -s /run/secrets/.bunfig.toml ]; then \
export XDG_CONFIG_HOME=/run/secrets; \
fi; \
sed -i \
-e 's|git+ssh://git@github.com/fwcd/tree-sitter-kotlin.git#|github:fwcd/tree-sitter-kotlin#|g' \
-e 's/"sha512-onbog[^"]*"/""/g' \
bun.lock \
Comment on lines +29 to 32

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔍 One dependency bypasses firewall coverage

The lockfile rewrite downloads tree-sitter-kotlin from GitHub rather than the protected registry. Resolve the declared coverage gap before relying on these builds.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Expand All @@ -24,9 +39,19 @@ RUN bun run build
# This stage only needs production deps (no git+ssh transitive devDeps), so
# the bun lockfile works correctly.
FROM oven/bun:1.3.14 AS deps
ARG SFW_REQUIRED
WORKDIR /app
COPY package.json bun.lock ./
RUN bun install --frozen-lockfile --production --ignore-scripts
RUN --mount=type=secret,id=sfw_bunfig,target=/run/secrets/.bunfig.toml \
set -eu; \
if [ "${SFW_REQUIRED:-false}" = "true" ] && [ ! -s /run/secrets/.bunfig.toml ]; then \
echo "Socket Firewall Bun config secret is required for Docker dependency installs." >&2; \
exit 1; \
fi; \
if [ -s /run/secrets/.bunfig.toml ]; then \
export XDG_CONFIG_HOME=/run/secrets; \
fi; \
bun install --frozen-lockfile --production --ignore-scripts

# Runtime stage: minimal Node image with only what the emulator needs.
FROM node:22-alpine
Expand Down
Loading
Loading