Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
39ab8d1
PoC: Socket Firewall (SFW) in audit mode on the ci workflow
nebasuke Jun 1, 2026
b09f7f0
PoC: use SFW firewall mode (Free has no audit mode)
nebasuke Jun 1, 2026
b9b0ea1
PoC: drop SFW CA discovery — rely on sfw's zero-config trust
nebasuke Jun 1, 2026
39c41cd
TEMP: skip dependencies cache restore to force cold fetch through SFW
nebasuke Jun 2, 2026
7e8c418
PoC: trust SFW CA system-wide for hermit's raw curl
nebasuke Jun 2, 2026
8754c25
PoC: discover SFW CA via NODE_EXTRA_CA_CERTS, not a filename guess
nebasuke Jun 2, 2026
091476e
PoC: merge SFW + system CA roots inside the wrap (option A)
nebasuke Jun 2, 2026
a20342e
Route dependency fetches through SFW across all CI workflows
nebasuke Jun 2, 2026
813e12b
Trim SFW comments — mark only the deliberately-unwrapped calls
nebasuke Jun 2, 2026
e388a3f
Drop dead non-Linux handling from setup-sfw action
nebasuke Jun 2, 2026
7b4533f
Drop the setup-sfw 'mode' input
nebasuke Jun 2, 2026
54e9210
Drop inline 'not SFW-wrapped' markers
nebasuke Jun 2, 2026
ae6f9a0
Revert "TEMP: skip dependencies cache restore to force cold fetch thr…
nebasuke Jun 2, 2026
05f965b
Drop the firewall-mode comment from setup-sfw
nebasuke Jun 2, 2026
8eb19eb
Drop the Setup SFW route comment from ci.yml
nebasuke Jun 2, 2026
01fbb9f
Drop the SFW_PREFIX explainer comment from ci.yml
nebasuke Jun 2, 2026
11b7933
Strengthen setup-sfw: REQUESTS_CA_BUNDLE + document SFW_PREFIX contract
nebasuke Jun 2, 2026
994ba5d
Rename Setup SFW steps; hard-fail SFW before publish
nebasuke Jun 2, 2026
1c7d6fd
Soft-fail SFW; hard-fail only on the release path
nebasuke Jun 2, 2026
25e4c7a
Require SFW by default; jobs opt out via the optional input
nebasuke Jun 10, 2026
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
71 changes: 71 additions & 0 deletions .github/actions/setup-sfw/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: "Setup Socket Firewall (SFW)"
description: >
Installs Socket Firewall Free and exports SFW_PREFIX, a wrapper that callers
prefix onto dependency commands to run installs behind the firewall (it also
fixes sfw's CA handling — see the wrapper step).
Fails the job if SFW can't be installed, so dependency installs never silently
run unprotected. Jobs where SFW is best-effort (a socket.dev outage must not
fail them) opt out with optional, which warns and leaves SFW_PREFIX unset
instead — callers then run unwrapped.

inputs:
optional:
description: >
When "true", a failed SFW install warns and leaves SFW_PREFIX unset
(callers run unprotected) instead of failing the job.
required: false
default: "false"

runs:
using: "composite"
steps:
- name: "Install SFW"
# Failures are decided by the wrapper step below, which knows the
# optional input — keep this step itself from failing the job.
continue-on-error: true
uses: "socketdev/action@937f824ec476dfd164d4a4d9995751427b0be143" # v1
with:
mode: "firewall"

# sfw has no persistent CA file and overrides the CA env vars inside every
# wrap, so the union bundle must be built *inside* the wrap. Write a wrapper
# that does exactly that, then run it as `sfw <wrapper> <command>`: sfw injects
# its temp-CA env into the wrapper, which merges that root with the system
# bundle, re-points the CA vars at the union, and exec's the real command.
- name: "Create SFW CA-merge wrapper"
shell: "bash"
env:
SFW_OPTIONAL: "${{ inputs.optional }}"
run: |
if ! command -v sfw &>/dev/null; then
if [ "${SFW_OPTIONAL}" = "true" ]; then
echo "::warning::sfw not found after install — dependency installs will run UNPROTECTED."
exit 0
fi
echo "::error::sfw not found after install — refusing to run dependency installs unprotected. Pass optional: \"true\" if this job may run without SFW."
exit 1
fi
wrapper="${RUNNER_TEMP}/sfw-wrap"
cat > "${wrapper}" <<'EOF'
#!/usr/bin/env bash
# Invoked as: sfw sfw-wrap <command...>. sfw has injected SSL_CERT_FILE
# (and friends) pointing at a temp bundle that holds only its MITM root.
# Merge that root with the system roots so TLS works for both the hosts
# sfw MITMs (registries) and the ones it passes through (github.com etc.).
set -euo pipefail
if [ -n "${SSL_CERT_FILE:-}" ] && [ -f "${SSL_CERT_FILE}" ]; then
merged="$(mktemp)"
cat /etc/ssl/certs/ca-certificates.crt "${SSL_CERT_FILE}" > "${merged}"
# REQUESTS_CA_BUNDLE: pip/requests can ignore CURL_CA_BUNDLE inside a venv (pipenv) — psf/requests#6660.
export SSL_CERT_FILE="${merged}" CURL_CA_BUNDLE="${merged}" \
CARGO_HTTP_CAINFO="${merged}" GIT_SSL_CAINFO="${merged}" \
NODE_EXTRA_CA_CERTS="${merged}" REQUESTS_CA_BUNDLE="${merged}"
unset SSL_CERT_DIR
fi
exec "$@"
Comment thread
nebasuke marked this conversation as resolved.
EOF
chmod +x "${wrapper}"
# Consumed UNQUOTED by callers (`${SFW_PREFIX:-} <cmd>`) so the shell splits
# it into two argv entries — `sfw` and the wrapper path. Quoting it breaks that;
# when unset (optional + failed install) it expands to nothing and <cmd> runs unwrapped.
echo "SFW_PREFIX=sfw ${wrapper}" >> "$GITHUB_ENV"
7 changes: 6 additions & 1 deletion .github/workflows/benchmark_archive.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,13 @@ jobs:
- name: "Restore Cargo Target Cache"
uses: "./.github/actions/cache/cargo-target/restore"

- name: "Setup SFW (optional)"
uses: "./.github/actions/setup-sfw"
with:
optional: "true"

- name: "infra setup cargo"
run: "./scripts/bin/infra setup cargo"
run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo"

- name: "infra perf archive"
continue-on-error: true
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/benchmark_cargo_cmp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,13 @@ jobs:
- name: "Restore Cargo Target Cache"
uses: "./.github/actions/cache/cargo-target/restore"

- name: "Setup SFW (optional)"
uses: "./.github/actions/setup-sfw"
with:
optional: "true"

- name: "infra setup cargo pipenv"
run: "./scripts/bin/infra setup cargo pipenv"
run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv"

- name: "Smoke Test > infra perf cargo --smoke comparison"
if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}"
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/benchmark_cargo_slang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,13 @@ jobs:
- name: "Restore Cargo Target Cache"
uses: "./.github/actions/cache/cargo-target/restore"

- name: "Setup SFW (optional)"
uses: "./.github/actions/setup-sfw"
with:
optional: "true"

- name: "infra setup cargo pipenv"
run: "./scripts/bin/infra setup cargo pipenv"
run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv"

- name: "Smoke Test > infra perf cargo --smoke slang"
if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}"
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/benchmark_cargo_slang_v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,13 @@ jobs:
- name: "Restore Cargo Target Cache"
uses: "./.github/actions/cache/cargo-target/restore"

- name: "Setup SFW (optional)"
uses: "./.github/actions/setup-sfw"
with:
optional: "true"

- name: "infra setup cargo pipenv"
run: "./scripts/bin/infra setup cargo pipenv"
run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv"

- name: "Smoke Test > infra perf cargo --smoke slang-v2"
if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}"
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/benchmark_npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,16 @@ jobs:
- name: "Restore Cargo Target Cache"
uses: "./.github/actions/cache/cargo-target/restore"

- name: "Setup SFW (optional)"
uses: "./.github/actions/setup-sfw"
with:
optional: "true"

- name: "infra setup cargo npm"
run: "./scripts/bin/infra setup cargo npm"
run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo npm"

- name: "infra check npm"
run: "./scripts/bin/infra check npm"
run: "${SFW_PREFIX:-} ./scripts/bin/infra check npm"

- name: "Smoke Test > infra perf npm --smoke"
if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}"
Expand Down
32 changes: 24 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,20 @@ jobs:
if: "${{ github.ref_name != 'main' && github.event_name != 'merge_group' }}"
uses: "./.github/actions/cache/cargo-target/restore"

- name: "Setup SFW (optional)"
uses: "./.github/actions/setup-sfw"
with:
optional: "true"

#
# Run all CI steps in order: _SLANG_INFRA_CI_STEPS_ORDERED_ (keep in sync)
#

- name: "infra setup"
run: "./scripts/bin/infra setup"
run: "${SFW_PREFIX:-} ./scripts/bin/infra setup"

- name: "infra check"
run: "./scripts/bin/infra check"
run: "${SFW_PREFIX:-} ./scripts/bin/infra check"

- name: "infra test"
run: "./scripts/bin/infra test"
Expand All @@ -75,14 +80,15 @@ jobs:

# On main pushes, warm the dependency and cargo-target caches with the perf
# toolchain (bencher_cli, iai-callgrind-runner, and the bench binaries) so
# subsequent PR perf runs don't compile them from scratch.
# subsequent PR perf runs don't compile them from scratch. Wrapped: these
# cargo-install from crates.io (no bencher upload, so no MITM concern).
- name: "infra perf cargo --smoke slang-v2"
if: "${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref_name == 'main' }}"
run: "./scripts/bin/infra perf cargo --smoke --no-apt-deps slang-v2"
run: "${SFW_PREFIX:-} ./scripts/bin/infra perf cargo --smoke --no-apt-deps slang-v2"

- name: "infra perf npm --smoke"
if: "${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref_name == 'main' }}"
run: "./scripts/bin/infra perf npm --smoke"
run: "${SFW_PREFIX:-} ./scripts/bin/infra perf npm --smoke"

- name: "Save Cargo Target Cache"
if: "${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref_name == 'main' }}"
Expand Down Expand Up @@ -114,8 +120,13 @@ jobs:
if: "${{ github.ref_name != 'main' }}"
uses: "./.github/actions/cache/cargo-cooldown/restore"

- name: "Setup SFW (optional)"
uses: "./.github/actions/setup-sfw"
with:
optional: "true"

- name: "Cargo cooldown check"
run: "./scripts/bin/with-hermit cargo cooldown-check"
run: "${SFW_PREFIX:-} ./scripts/bin/with-hermit cargo cooldown-check"

- name: "Save Cargo Cooldown Cache"
if: "${{ (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && github.ref_name == 'main' }}"
Expand All @@ -133,16 +144,21 @@ jobs:
with:
persist-credentials: false

- name: "Setup SFW (optional)"
uses: "./.github/actions/setup-sfw"
with:
optional: "true"

- name: "Renovate schema check"
run: "./bin/npx --yes --package renovate -- renovate-config-validator renovate.json"
run: "${SFW_PREFIX:-} ./bin/npx --yes --package renovate -- renovate-config-validator renovate.json"

- name: "Renovate extraction dry-run"
env:
# Without a token, github-tags/github-releases lookups (e.g. bencher_cli) hit the
# anonymous 60 req/hr rate limit shared across the runner IP.
GITHUB_COM_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
LOG_LEVEL: "debug"
run: "./bin/npx --yes renovate --platform=local --dry-run=full"
run: "${SFW_PREFIX:-} ./bin/npx --yes renovate --platform=local --dry-run=full"

# The full CI runs on the bare runner so it can reuse the cargo-target cache;
# reproducing that cache-mount setup inside a devcontainer would slow CI down
Expand Down
36 changes: 31 additions & 5 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,13 @@ jobs:
- name: "Restore Cargo Target Cache"
uses: "./.github/actions/cache/cargo-target/restore"

# Release path: SFW is required (the action's default) — fail rather than
# install deps unprotected.
- name: "Setup SFW"
uses: "./.github/actions/setup-sfw"

- name: "infra setup"
run: "./scripts/bin/infra setup"
run: "${SFW_PREFIX:-} ./scripts/bin/infra setup"

# Consume the changesets, and create a git stash, to be popped by the next step:
- name: "infra publish changesets"
Expand Down Expand Up @@ -184,8 +189,16 @@ jobs:
if: "${{ needs.changesets.outputs.publishNeeded != 'true' }}"
uses: "./.github/actions/cache/cargo-target/restore"

# On a real release this job builds the published npm tarball, so SFW is
# required and a failed install stops the job. On PRs/pending pushes the
# build is best-effort (same condition the cache-restore steps above gate on).
- name: "Setup SFW"
uses: "./.github/actions/setup-sfw"
with:
optional: "${{ needs.changesets.outputs.publishNeeded != 'true' }}"

- name: "infra setup"
run: "./scripts/bin/infra setup"
run: "${SFW_PREFIX:-} ./scripts/bin/infra setup"

- name: "Prepare publish artifacts"
id: "prepare"
Expand Down Expand Up @@ -242,8 +255,13 @@ jobs:
- name: "Restore Cargo Target Cache"
uses: "./.github/actions/cache/cargo-target/restore"

- name: "Setup SFW (optional)"
uses: "./.github/actions/setup-sfw"
with:
optional: "true"

- name: "infra setup"
run: "./scripts/bin/infra setup"
run: "${SFW_PREFIX:-} ./scripts/bin/infra setup"

- name: "Download publish artifacts"
uses: "actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c" # v8.0.1
Expand Down Expand Up @@ -402,9 +420,17 @@ jobs:
name: "${{ needs.prepare.outputs.artifact-name }}"
path: "target/publish-artifacts/"

# Compile infra_cli with no registry token in scope.
- name: "Setup SFW (optional)"
uses: "./.github/actions/setup-sfw"
with:
# Best-effort: this job only uploads the artifact built in `prepare`;
# the published bytes were already fetched behind SFW there.
optional: "true"

# Compile infra_cli with no registry token in scope. The infra publish
# steps below upload with auth, left unwrapped.
- name: "Build infra_cli (before any token)"
run: "./scripts/bin/infra --help"
run: "${SFW_PREFIX:-} ./scripts/bin/infra --help"

- name: "infra publish npm"
# No tarball means prepare packed nothing (version already on npm) — nothing to publish.
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/sourcify_single_chain.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,13 @@ jobs:
- name: "Restore Cargo Target Cache"
uses: "./.github/actions/cache/cargo-target/restore"

- name: "Setup SFW (optional)"
uses: "./.github/actions/setup-sfw"
with:
optional: "true"

- name: "Build Test Binary"
run: "./scripts/bin/with-hermit cargo build --locked --bin solidity_testing_sourcify --release"
run: "${SFW_PREFIX:-} ./scripts/bin/with-hermit cargo build --locked --bin solidity_testing_sourcify --release"

- name: "Save Test Binary"
uses: "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" # v7.0.1
Expand Down
30 changes: 30 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,33 @@ The following still need to be updated manually:

1. Rust toolchains: `$RUST_STABLE_VERSION` and `$RUST_NIGHTLY_VERSION` defined in `hermit.hcl` and updated via `rustup install`.
2. Node.js major versions (only even LTS releases): bump the hermit package, then `@types/node` follows automatically.

## Supply-Chain Firewall (Socket)

CI installs dependencies behind [Socket Firewall](https://docs.socket.dev/) (`sfw`)
in _firewall_ mode, which intercepts package-manager traffic (cargo, npm, pip) and
blocks packages Socket flags as malicious. The `setup-sfw` composite action installs
it and exports `SFW_PREFIX`, which prefixes the dependency-fetching steps (`infra
setup`, `infra check`, and the cooldown and renovate checks). It runs **only in CI** —
local `infra` commands are unaffected.

**Required by default.** If Socket Firewall can't be installed (e.g. a socket.dev
outage), the `Setup SFW` step fails the job rather than let dependency installs run
_unprotected_, so we never publish dependencies that were installed unprotected. Jobs
where the firewall is best-effort — PR/benchmark CI, and release jobs that only
consume artifacts already built behind SFW — opt out with `optional: "true"`, which
logs a `::warning::` and continues unprotected instead.

### When a build fails because of Socket

The two failure modes look different:

- **SFW unavailable (not a block).** The `Setup SFW` step fails with
`::error::sfw not found after install …` (or, on `optional: "true"` jobs, logs a
`::warning::` and continues unprotected). This is a Socket/network availability
problem, not a flagged package — re-run once Socket is reachable.
- **A package was blocked.** A wrapped step (usually `infra setup`) fails and the `sfw`
output names the offending package, version, and why Socket flagged it. This is the
firewall doing its job. Note that firewall mode blocks _transitively_ — a flagged
package deep in the dependency tree fails the whole install, even if you didn't add
it directly.