From 39ab8d14b95854cffbf4e8dc91faf60a65939ce3 Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Mon, 1 Jun 2026 16:09:48 +0000 Subject: [PATCH 01/20] PoC: Socket Firewall (SFW) in audit mode on the ci workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adapts solx's setup-sfw composite action (github.com/NomicFoundation/solx, PRs #250/#357/#397), slimmed to the bare-metal Linux path since slang CI is all GitHub-hosted ubuntu (drops the container npm path + platform skips). Wires it into ci.yml in AUDIT mode and wraps 'infra setup' (where the cargo + pnpm fetches happen) with ${SFW_PREFIX:-}. Audit-first so it can't break CI; the goal of the first run is to learn whether the MITM proxy is compatible with slang's fetchers — notably hermit/rustup downloads, which 'infra setup' currently routes through SFW too. --- .github/actions/setup-sfw/action.yml | 126 +++++++++++++++++++++++++++ .github/workflows/ci.yml | 16 +++- 2 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 .github/actions/setup-sfw/action.yml diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml new file mode 100644 index 0000000000..6073172030 --- /dev/null +++ b/.github/actions/setup-sfw/action.yml @@ -0,0 +1,126 @@ +name: "Setup Socket Firewall (SFW)" +description: > + Installs and configures Socket Firewall (SFW) to police package fetches at + install time. Installs the sfw-free MITM proxy via socketdev/action, discovers + SFW's generated CA, and exports a merged CA bundle via CARGO_HTTP_CAINFO / + GIT_SSL_CAINFO / SSL_CERT_FILE so cargo, git, and rustls trust the MITM cert. + Exports SFW_PREFIX (the `sfw` wrapper) for callers to prefix their fetch/build + commands with. On any failure (unsupported platform, missing CA) it leaves + SFW_PREFIX unset and reports available=false, so callers degrade to running + unwrapped rather than breaking the build. + + Linux-only: macOS (LibreSSL) and Windows (Schannel) reject the MITM cert at the + crypto level. slang CI is all Linux, so this is just a defensive guard. + +inputs: + mode: + description: 'SFW mode: "firewall" blocks risky packages, "audit" only warns.' + required: false + default: "firewall" + +outputs: + available: + description: "Whether SFW is available for use (true/false)." + value: "${{ steps.set-prefix.outputs.available }}" + +runs: + using: "composite" + steps: + - name: "Detect platform support" + id: "detect" + shell: "bash" + run: | + if [[ "$RUNNER_OS" != "Linux" ]]; then + echo "::notice::SFW skipped: MITM proxy not compatible with $RUNNER_OS TLS stack" + echo "supported=false" >> "$GITHUB_OUTPUT" + else + echo "supported=true" >> "$GITHUB_OUTPUT" + fi + + - name: "Install SFW" + if: "${{ steps.detect.outputs.supported == 'true' }}" + uses: "socketdev/action@937f824ec476dfd164d4a4d9995751427b0be143" # v1 + with: + mode: "${{ inputs.mode }}" + + # The sfw-free proxy exposes its generated CA via NODE_EXTRA_CA_CERTS when it + # wraps a child process. cargo (libgit2/rustls) needs that CA on an explicit + # path, otherwise git-dep clones and crates.io fetches fail with "unable to + # get local issuer certificate" once the proxy intercepts the TLS. + - name: "Configure cargo/git TLS trust for SFW" + id: "ca-cert" + if: "${{ steps.detect.outputs.supported == 'true' }}" + shell: "bash" + run: | + # Trigger SFW cert generation (may be lazy). + sfw npm ping > /dev/null 2>&1 || true + + # Read NODE_EXTRA_CA_CERTS from inside an sfw-wrapped process, via a temp + # file to avoid sfw's banner lines polluting stdout. + cat > "${RUNNER_TEMP}/sfw-get-cert.js" << 'SCRIPT' + const fs = require("fs"); + const p = process.env.NODE_EXTRA_CA_CERTS || ""; + fs.writeFileSync(process.env.RUNNER_TEMP + "/sfw-cert-path.txt", p); + SCRIPT + sfw node "${RUNNER_TEMP}/sfw-get-cert.js" > /dev/null 2>&1 || true + SFW_CA="" + [ -f "${RUNNER_TEMP}/sfw-cert-path.txt" ] && SFW_CA=$(cat "${RUNNER_TEMP}/sfw-cert-path.txt") + rm -f "${RUNNER_TEMP}/sfw-get-cert.js" "${RUNNER_TEMP}/sfw-cert-path.txt" + + if [ -z "$SFW_CA" ] || [ ! -f "$SFW_CA" ]; then + echo "::warning::SFW CA cert not found — disabling SFW to avoid opaque SSL errors" + echo "ca-failed=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + + echo "SFW CA cert: $SFW_CA" + BUNDLE="${RUNNER_TEMP}/sfw-ca-bundle.pem" + + # Merge the SFW CA into the system bundle (Linux paths only). + FOUND_SYSTEM=false + for f in \ + /etc/ssl/certs/ca-certificates.crt \ + /etc/ssl/certs/ca-bundle.crt \ + /etc/pki/tls/certs/ca-bundle.crt; do + if [ -f "$f" ]; then + cp "$f" "$BUNDLE" + echo "System CA bundle: $f" + FOUND_SYSTEM=true + break + fi + done + + if [ "$FOUND_SYSTEM" = false ]; then + echo "::warning::Could not find system CA bundle — using SFW cert only" + cp "$SFW_CA" "$BUNDLE" + else + echo "" >> "$BUNDLE" + cat "$SFW_CA" >> "$BUNDLE" + fi + + # cargo, git, and rustls-native-certs (SSL_CERT_FILE) all read these. + echo "CARGO_HTTP_CAINFO=$BUNDLE" >> "$GITHUB_ENV" + echo "GIT_SSL_CAINFO=$BUNDLE" >> "$GITHUB_ENV" + echo "SSL_CERT_FILE=$BUNDLE" >> "$GITHUB_ENV" + echo "CA bundle created at $BUNDLE ($(wc -l < "$BUNDLE") lines)" + + - name: "Set SFW prefix" + id: "set-prefix" + shell: "bash" + run: | + if [[ "${{ steps.detect.outputs.supported }}" != "true" ]]; then + echo "available=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + if [[ "${{ steps.ca-cert.outputs.ca-failed }}" == "true" ]]; then + echo "::notice::SFW available but CA cert missing — disabling to prevent SSL errors" + echo "available=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + if command -v sfw &>/dev/null; then + echo "SFW_PREFIX=sfw" >> "$GITHUB_ENV" + echo "available=true" >> "$GITHUB_OUTPUT" + else + echo "::warning::SFW command not found after install" + echo "available=false" >> "$GITHUB_OUTPUT" + fi diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2dcbfb2d64..9a7f78e489 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,12 +57,26 @@ jobs: if: "${{ github.ref_name != 'main' && github.event_name != 'merge_group' }}" uses: "./.github/actions/cache/cargo-target/restore" + # PoC: Socket Firewall in AUDIT mode — warns on risky packages, never + # blocks. Exports SFW_PREFIX (`sfw`), which the wrapped step below uses to + # route its dependency fetches through SFW's malware-scanning MITM proxy. + # Audit-first so it can't break CI while we validate compatibility — in + # particular whether hermit/rustup downloads survive the MITM (see PR). + - name: "Setup SFW (audit)" + uses: "./.github/actions/setup-sfw" + with: + mode: "audit" + # # Run all CI steps in order: _SLANG_INFRA_CI_STEPS_ORDERED_ (keep in sync) # + # `${SFW_PREFIX:-}` wraps the command with `sfw` when SFW is available, and + # expands to nothing otherwise (so a non-Linux runner or SFW failure just + # runs setup unwrapped). PoC wraps only `setup` — that's where the cargo + # crates + pnpm package fetches happen. - name: "infra setup" - run: "./scripts/bin/infra setup" + run: "${SFW_PREFIX:-} ./scripts/bin/infra setup" - name: "infra check" run: "./scripts/bin/infra check" From b09f7f00fc55dc973c78ff7c13f341fb385c70e1 Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Mon, 1 Jun 2026 17:42:36 +0000 Subject: [PATCH 02/20] PoC: use SFW firewall mode (Free has no audit mode) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first run failed: socketdev/action rejects mode=audit (supports firewall/patch/cli), and Socket Firewall Free has no warn-only mode at all — that's Enterprise-only. Free blocks confirmed-malicious packages and only warns on AI-flagged-unconfirmed ones. Switch to mode=firewall (the real, only proxy mode). slang's tree is known-clean, so it shouldn't block anything; the run now validates MITM compatibility (hermit/rustup/cargo/pnpm fetches). Also correct the action's mode description, which inherited solx's inaccurate audit claim. --- .github/actions/setup-sfw/action.yml | 5 ++++- .github/workflows/ci.yml | 15 ++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index 6073172030..02a01ffaaf 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -14,7 +14,10 @@ description: > inputs: mode: - description: 'SFW mode: "firewall" blocks risky packages, "audit" only warns.' + description: > + socketdev/action operation mode: "firewall" (the MITM proxy), "patch", or + "cli". Socket Firewall Free has no warn-only mode — "firewall" blocks + confirmed-malicious packages and only warns on AI-flagged-unconfirmed ones. required: false default: "firewall" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9a7f78e489..b4159dad28 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,15 +57,16 @@ jobs: if: "${{ github.ref_name != 'main' && github.event_name != 'merge_group' }}" uses: "./.github/actions/cache/cargo-target/restore" - # PoC: Socket Firewall in AUDIT mode — warns on risky packages, never - # blocks. Exports SFW_PREFIX (`sfw`), which the wrapped step below uses to - # route its dependency fetches through SFW's malware-scanning MITM proxy. - # Audit-first so it can't break CI while we validate compatibility — in - # particular whether hermit/rustup downloads survive the MITM (see PR). - - name: "Setup SFW (audit)" + # PoC: Socket Firewall (Free) via socketdev/action. Free has no warn-only + # mode — it blocks confirmed-malicious packages and only warns (without + # blocking) on AI-flagged-unconfirmed ones. slang's tree is known-clean, + # so this shouldn't block anything; the run validates MITM compatibility, + # notably whether hermit/rustup downloads survive it (see PR). Exports + # SFW_PREFIX (`sfw`) for the wrapped step below. + - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" with: - mode: "audit" + mode: "firewall" # # Run all CI steps in order: _SLANG_INFRA_CI_STEPS_ORDERED_ (keep in sync) From b9b0ea1c712e8ac15a9ffa51a5483da8fefd585e Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Mon, 1 Jun 2026 18:37:11 +0000 Subject: [PATCH 03/20] =?UTF-8?q?PoC:=20drop=20SFW=20CA=20discovery=20?= =?UTF-8?q?=E2=80=94=20rely=20on=20sfw's=20zero-config=20trust?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first firewall run silently no-op'd: the CA-discovery step (ported from solx's container-path fix, PR #357) found no NODE_EXTRA_CA_CERTS on slang's bare-metal runner and disabled SFW, so 'infra setup' ran unwrapped despite green CI. sfw-free is documented as zero-config and natively wraps cargo (+ npm/ pnpm/pip), so it should set up its own proxy + TLS trust for the wrapped process — no manual CARGO_HTTP_CAINFO bridging. Strip the CA dance to just install + export SFW_PREFIX, and let a real TLS failure surface loudly instead of silently disabling. The next run answers two things: (1) does zero-config trust cover cargo/pnpm, and (2) does wrapping the 'infra' parent propagate the proxy to its child package managers. --- .github/actions/setup-sfw/action.yml | 116 +++------------------------ 1 file changed, 12 insertions(+), 104 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index 02a01ffaaf..078e28c740 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -1,129 +1,37 @@ name: "Setup Socket Firewall (SFW)" description: > - Installs and configures Socket Firewall (SFW) to police package fetches at - install time. Installs the sfw-free MITM proxy via socketdev/action, discovers - SFW's generated CA, and exports a merged CA bundle via CARGO_HTTP_CAINFO / - GIT_SSL_CAINFO / SSL_CERT_FILE so cargo, git, and rustls trust the MITM cert. - Exports SFW_PREFIX (the `sfw` wrapper) for callers to prefix their fetch/build - commands with. On any failure (unsupported platform, missing CA) it leaves - SFW_PREFIX unset and reports available=false, so callers degrade to running - unwrapped rather than breaking the build. - - Linux-only: macOS (LibreSSL) and Windows (Schannel) reject the MITM cert at the - crypto level. slang CI is all Linux, so this is just a defensive guard. + Installs Socket Firewall (SFW) Free via socketdev/action and exports + SFW_PREFIX (`sfw`) so callers can wrap their dependency-fetching commands with + it. SFW is documented as zero-config — it wraps the package manager + (npm/pnpm/pip/cargo) and sets up its own proxy + TLS trust for the wrapped + process tree — so this action does NO manual CA handling. Linux-only (the MITM + is incompatible with macOS/Windows TLS stacks); on other runners SFW_PREFIX is + left unset and callers run unwrapped. inputs: mode: description: > - socketdev/action operation mode: "firewall" (the MITM proxy), "patch", or + socketdev/action operation mode: "firewall" (the proxy), "patch", or "cli". Socket Firewall Free has no warn-only mode — "firewall" blocks confirmed-malicious packages and only warns on AI-flagged-unconfirmed ones. required: false default: "firewall" -outputs: - available: - description: "Whether SFW is available for use (true/false)." - value: "${{ steps.set-prefix.outputs.available }}" - runs: using: "composite" steps: - - name: "Detect platform support" - id: "detect" - shell: "bash" - run: | - if [[ "$RUNNER_OS" != "Linux" ]]; then - echo "::notice::SFW skipped: MITM proxy not compatible with $RUNNER_OS TLS stack" - echo "supported=false" >> "$GITHUB_OUTPUT" - else - echo "supported=true" >> "$GITHUB_OUTPUT" - fi - - name: "Install SFW" - if: "${{ steps.detect.outputs.supported == 'true' }}" + if: "${{ runner.os == 'Linux' }}" uses: "socketdev/action@937f824ec476dfd164d4a4d9995751427b0be143" # v1 with: mode: "${{ inputs.mode }}" - # The sfw-free proxy exposes its generated CA via NODE_EXTRA_CA_CERTS when it - # wraps a child process. cargo (libgit2/rustls) needs that CA on an explicit - # path, otherwise git-dep clones and crates.io fetches fail with "unable to - # get local issuer certificate" once the proxy intercepts the TLS. - - name: "Configure cargo/git TLS trust for SFW" - id: "ca-cert" - if: "${{ steps.detect.outputs.supported == 'true' }}" + - name: "Export SFW prefix" + if: "${{ runner.os == 'Linux' }}" shell: "bash" run: | - # Trigger SFW cert generation (may be lazy). - sfw npm ping > /dev/null 2>&1 || true - - # Read NODE_EXTRA_CA_CERTS from inside an sfw-wrapped process, via a temp - # file to avoid sfw's banner lines polluting stdout. - cat > "${RUNNER_TEMP}/sfw-get-cert.js" << 'SCRIPT' - const fs = require("fs"); - const p = process.env.NODE_EXTRA_CA_CERTS || ""; - fs.writeFileSync(process.env.RUNNER_TEMP + "/sfw-cert-path.txt", p); - SCRIPT - sfw node "${RUNNER_TEMP}/sfw-get-cert.js" > /dev/null 2>&1 || true - SFW_CA="" - [ -f "${RUNNER_TEMP}/sfw-cert-path.txt" ] && SFW_CA=$(cat "${RUNNER_TEMP}/sfw-cert-path.txt") - rm -f "${RUNNER_TEMP}/sfw-get-cert.js" "${RUNNER_TEMP}/sfw-cert-path.txt" - - if [ -z "$SFW_CA" ] || [ ! -f "$SFW_CA" ]; then - echo "::warning::SFW CA cert not found — disabling SFW to avoid opaque SSL errors" - echo "ca-failed=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - - echo "SFW CA cert: $SFW_CA" - BUNDLE="${RUNNER_TEMP}/sfw-ca-bundle.pem" - - # Merge the SFW CA into the system bundle (Linux paths only). - FOUND_SYSTEM=false - for f in \ - /etc/ssl/certs/ca-certificates.crt \ - /etc/ssl/certs/ca-bundle.crt \ - /etc/pki/tls/certs/ca-bundle.crt; do - if [ -f "$f" ]; then - cp "$f" "$BUNDLE" - echo "System CA bundle: $f" - FOUND_SYSTEM=true - break - fi - done - - if [ "$FOUND_SYSTEM" = false ]; then - echo "::warning::Could not find system CA bundle — using SFW cert only" - cp "$SFW_CA" "$BUNDLE" - else - echo "" >> "$BUNDLE" - cat "$SFW_CA" >> "$BUNDLE" - fi - - # cargo, git, and rustls-native-certs (SSL_CERT_FILE) all read these. - echo "CARGO_HTTP_CAINFO=$BUNDLE" >> "$GITHUB_ENV" - echo "GIT_SSL_CAINFO=$BUNDLE" >> "$GITHUB_ENV" - echo "SSL_CERT_FILE=$BUNDLE" >> "$GITHUB_ENV" - echo "CA bundle created at $BUNDLE ($(wc -l < "$BUNDLE") lines)" - - - name: "Set SFW prefix" - id: "set-prefix" - shell: "bash" - run: | - if [[ "${{ steps.detect.outputs.supported }}" != "true" ]]; then - echo "available=false" >> "$GITHUB_OUTPUT" - exit 0 - fi - if [[ "${{ steps.ca-cert.outputs.ca-failed }}" == "true" ]]; then - echo "::notice::SFW available but CA cert missing — disabling to prevent SSL errors" - echo "available=false" >> "$GITHUB_OUTPUT" - exit 0 - fi if command -v sfw &>/dev/null; then echo "SFW_PREFIX=sfw" >> "$GITHUB_ENV" - echo "available=true" >> "$GITHUB_OUTPUT" else - echo "::warning::SFW command not found after install" - echo "available=false" >> "$GITHUB_OUTPUT" + echo "::warning::sfw not found after install — callers will run unwrapped" fi From 39c41cde5c134499a01304287adb4cbff2e8f13f Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 08:04:53 +0000 Subject: [PATCH 04/20] TEMP: skip dependencies cache restore to force cold fetch through SFW Revert before merge. The restore-keys prefix fallback defeats key bumping, so skipping the restore is the only way to make hermit/pnpm/cargo download cold and actually exercise the firewall MITM path. --- .github/workflows/ci.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4159dad28..b906c80981 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,8 +46,16 @@ jobs: # Cache is updated in this workflow, and reused in subsequent workflows. # Always start with a fresh cache when running on the main branch. + # + # TEMP (SFW PoC — revert before merge): force `if: false` so the + # dependencies cache is NOT restored. That cache holds ~/.cache/hermit/, + # the pnpm store, and ~/.cargo/registry/; its restore-keys prefix + # (dependencies-${{ runner.os }}-v1-) means a key bump still hits, so the + # only way to force a cold fetch through the firewall is to skip the + # restore entirely. With it skipped, hermit/rustup/pnpm/cargo all download + # cold through SFW, which is what this PoC actually needs to validate. - name: "Restore Dependencies Cache" - if: "${{ github.ref_name != 'main' }}" + if: "${{ false && github.ref_name != 'main' }}" uses: "./.github/actions/cache/dependencies/restore" # Restore compiled Rust artifacts from a previous build on main for non-main runs, From 7e8c41817d17d87b05d94c40d345dc84c364d689 Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 08:28:55 +0000 Subject: [PATCH 05/20] PoC: trust SFW CA system-wide for hermit's raw curl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dropped-CA run failed at the hermit bootstrap: a raw curl rejects the MITM cert because sfw only injects its CA into the package managers it wraps, not the system trust store. Re-add minimal, docs-aligned CA handling — locate socketFirewallCa.crt and install it via /usr/local/share/ca-certificates + update-ca-certificates — which covers curl/git/cargo. No per-tool env bridging. The find pipeline is guarded with '|| true' because composite-action steps run under 'bash -e -o pipefail', where find's nonzero exit on unreadable dirs would otherwise abort the step before the result is checked. --- .github/actions/setup-sfw/action.yml | 36 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index 078e28c740..4c0f8cf8ed 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -2,11 +2,13 @@ name: "Setup Socket Firewall (SFW)" description: > Installs Socket Firewall (SFW) Free via socketdev/action and exports SFW_PREFIX (`sfw`) so callers can wrap their dependency-fetching commands with - it. SFW is documented as zero-config — it wraps the package manager - (npm/pnpm/pip/cargo) and sets up its own proxy + TLS trust for the wrapped - process tree — so this action does NO manual CA handling. Linux-only (the MITM - is incompatible with macOS/Windows TLS stacks); on other runners SFW_PREFIX is - left unset and callers run unwrapped. + it. SFW auto-trusts only the package managers it wraps (npm/pnpm/pip/cargo); + a raw `curl` — like the hermit bootstrap that runs first in `infra setup` — + reads the system trust store and rejects the MITM cert. So this action also + installs SFW's CA system-wide the way Socket documents for Linux (drop it in + /usr/local/share/ca-certificates and run update-ca-certificates), which covers + curl/git/cargo. Linux-only (the MITM is incompatible with macOS/Windows TLS + stacks); on other runners SFW_PREFIX is left unset and callers run unwrapped. inputs: mode: @@ -26,6 +28,30 @@ runs: with: mode: "${{ inputs.mode }}" + # socketdev/action injects its CA only into the package managers sfw wraps, + # not into the system trust store. `infra setup` first bootstraps hermit via + # a raw curl, which then fails TLS verification against the MITM cert. Trust + # the CA system-wide using Socket's documented Linux method so curl (and git + # + cargo/rustls) accept it. + - name: "Trust SFW CA system-wide" + if: "${{ runner.os == 'Linux' }}" + shell: "bash" + run: | + command -v sfw &>/dev/null || { echo "::warning::sfw not found — skipping CA trust"; exit 0; } + # Generation is lazy; a wrapped command triggers it. The cert's directory + # isn't documented, so locate it by its documented filename. The trailing + # `|| true` keeps find's nonzero exit (it descends unreadable dirs) from + # tripping this step's pipefail+errexit before we can check the result. + sfw npm ping >/dev/null 2>&1 || true + ca="$(find / -name "socketFirewallCa.crt" 2>/dev/null | head -n1 || true)" + if [ -z "$ca" ]; then + echo "::error::SFW CA (socketFirewallCa.crt) not found after install" + exit 1 + fi + echo "SFW CA: $ca" + sudo cp "$ca" /usr/local/share/ca-certificates/socketFirewallCa.crt + sudo update-ca-certificates + - name: "Export SFW prefix" if: "${{ runner.os == 'Linux' }}" shell: "bash" From 8754c25b521a54b220d56f919c8810893b0fdd9f Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 08:51:49 +0000 Subject: [PATCH 06/20] PoC: discover SFW CA via NODE_EXTRA_CA_CERTS, not a filename guess MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit find / turned up no socketFirewallCa.crt — that name is from Socket's enterprise OpenSSL examples, not what the free binary emits. The free proxy advertises its CA only through NODE_EXTRA_CA_CERTS on wrapped processes. Read that path from a wrapped shell and install the file system-wide (same documented method). Diagnostics (injected env + candidate cert files) now run only on the failure branch, so the success path skips the ~4-min full-fs find. --- .github/actions/setup-sfw/action.yml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index 4c0f8cf8ed..40ed4f8304 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -38,18 +38,21 @@ runs: shell: "bash" run: | command -v sfw &>/dev/null || { echo "::warning::sfw not found — skipping CA trust"; exit 0; } - # Generation is lazy; a wrapped command triggers it. The cert's directory - # isn't documented, so locate it by its documented filename. The trailing - # `|| true` keeps find's nonzero exit (it descends unreadable dirs) from - # tripping this step's pipefail+errexit before we can check the result. - sfw npm ping >/dev/null 2>&1 || true - ca="$(find / -name "socketFirewallCa.crt" 2>/dev/null | head -n1 || true)" - if [ -z "$ca" ]; then - echo "::error::SFW CA (socketFirewallCa.crt) not found after install" + # sfw (free) doesn't drop the CA at a fixed path; it advertises the cert + # to processes it wraps via NODE_EXTRA_CA_CERTS. Read that path from a + # wrapped shell, then install the file system-wide (the documented Linux + # method) so curl/git/cargo trust it — node/pnpm already get the env var. + ca="$(sfw bash -c 'printf "%s" "${NODE_EXTRA_CA_CERTS:-}"' 2>/dev/null || true)" + if [ -z "$ca" ] || [ ! -f "$ca" ]; then + echo "::error::could not resolve SFW CA from NODE_EXTRA_CA_CERTS (got: '${ca:-}')" + echo "== env sfw injects into wrapped processes ==" + sfw bash -c 'env' 2>/dev/null | grep -iE "ca|cert|ssl|proxy" | sort || true + echo "== candidate cert files on disk ==" + find / \( -iname "*.crt" -o -iname "*.pem" \) 2>/dev/null | grep -iE "socket|sfw|firewall" || true exit 1 fi echo "SFW CA: $ca" - sudo cp "$ca" /usr/local/share/ca-certificates/socketFirewallCa.crt + sudo cp "$ca" /usr/local/share/ca-certificates/socket-firewall.crt sudo update-ca-certificates - name: "Export SFW prefix" From 091476ecbc10ecfcbe244393b8e5057d34c2672d Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 09:24:10 +0000 Subject: [PATCH 07/20] PoC: merge SFW + system CA roots inside the wrap (option A) Diagnostics showed sfw has no persistent CA file: inside each wrap it points SSL_CERT_FILE/CARGO_HTTP_CAINFO/etc. at a per-invocation /tmp/sfw-XXX bundle holding ONLY its MITM root, and hijacks SSL_CERT_DIR. That breaks raw TLS to hosts sfw passes through (github.com for hermit, static.rust-lang.org for rustup): 'unable to get local issuer certificate' for lack of public roots. Replace the (unworkable) system-trust install with a wrapper run as 'sfw ': inside the wrap it merges sfw's root with the system bundle, re-points the CA vars at the union, clears SSL_CERT_DIR, then exec's the command. SFW_PREFIX points at it, so ci.yml is unchanged. --- .github/actions/setup-sfw/action.yml | 77 ++++++++++++++-------------- 1 file changed, 38 insertions(+), 39 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index 40ed4f8304..ecaa18f6cd 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -1,14 +1,16 @@ name: "Setup Socket Firewall (SFW)" description: > Installs Socket Firewall (SFW) Free via socketdev/action and exports - SFW_PREFIX (`sfw`) so callers can wrap their dependency-fetching commands with - it. SFW auto-trusts only the package managers it wraps (npm/pnpm/pip/cargo); - a raw `curl` — like the hermit bootstrap that runs first in `infra setup` — - reads the system trust store and rejects the MITM cert. So this action also - installs SFW's CA system-wide the way Socket documents for Linux (drop it in - /usr/local/share/ca-certificates and run update-ca-certificates), which covers - curl/git/cargo. Linux-only (the MITM is incompatible with macOS/Windows TLS - stacks); on other runners SFW_PREFIX is left unset and callers run unwrapped. + SFW_PREFIX so callers can wrap their dependency-fetching commands with it. + Inside a wrap, sfw points the CA vars (SSL_CERT_FILE, CARGO_HTTP_CAINFO, …) at + a temp bundle holding ONLY its MITM root and hijacks SSL_CERT_DIR — so raw TLS + to hosts sfw passes through (github.com for the hermit bootstrap, + static.rust-lang.org for rustup) fails with "unable to get local issuer + certificate" for lack of the public roots. SFW_PREFIX therefore points at a + wrapper that, inside the wrap, merges sfw's root with the system bundle and + re-points the CA vars at the union before exec'ing the command. Linux-only + (the MITM is incompatible with macOS/Windows TLS stacks); on other runners + SFW_PREFIX is left unset and callers run unwrapped. inputs: mode: @@ -28,39 +30,36 @@ runs: with: mode: "${{ inputs.mode }}" - # socketdev/action injects its CA only into the package managers sfw wraps, - # not into the system trust store. `infra setup` first bootstraps hermit via - # a raw curl, which then fails TLS verification against the MITM cert. Trust - # the CA system-wide using Socket's documented Linux method so curl (and git - # + cargo/rustls) accept it. - - name: "Trust SFW CA system-wide" + # 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 `: 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" if: "${{ runner.os == 'Linux' }}" shell: "bash" run: | - command -v sfw &>/dev/null || { echo "::warning::sfw not found — skipping CA trust"; exit 0; } - # sfw (free) doesn't drop the CA at a fixed path; it advertises the cert - # to processes it wraps via NODE_EXTRA_CA_CERTS. Read that path from a - # wrapped shell, then install the file system-wide (the documented Linux - # method) so curl/git/cargo trust it — node/pnpm already get the env var. - ca="$(sfw bash -c 'printf "%s" "${NODE_EXTRA_CA_CERTS:-}"' 2>/dev/null || true)" - if [ -z "$ca" ] || [ ! -f "$ca" ]; then - echo "::error::could not resolve SFW CA from NODE_EXTRA_CA_CERTS (got: '${ca:-}')" - echo "== env sfw injects into wrapped processes ==" - sfw bash -c 'env' 2>/dev/null | grep -iE "ca|cert|ssl|proxy" | sort || true - echo "== candidate cert files on disk ==" - find / \( -iname "*.crt" -o -iname "*.pem" \) 2>/dev/null | grep -iE "socket|sfw|firewall" || true - exit 1 - fi - echo "SFW CA: $ca" - sudo cp "$ca" /usr/local/share/ca-certificates/socket-firewall.crt - sudo update-ca-certificates - - - name: "Export SFW prefix" - if: "${{ runner.os == 'Linux' }}" - shell: "bash" - run: | - if command -v sfw &>/dev/null; then - echo "SFW_PREFIX=sfw" >> "$GITHUB_ENV" - else + if ! command -v sfw &>/dev/null; then echo "::warning::sfw not found after install — callers will run unwrapped" + exit 0 + fi + wrapper="${RUNNER_TEMP}/sfw-wrap" + cat > "${wrapper}" <<'EOF' + #!/usr/bin/env bash + # Invoked as: sfw sfw-wrap . 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}" + export SSL_CERT_FILE="${merged}" CURL_CA_BUNDLE="${merged}" \ + CARGO_HTTP_CAINFO="${merged}" GIT_SSL_CAINFO="${merged}" \ + NODE_EXTRA_CA_CERTS="${merged}" + unset SSL_CERT_DIR fi + exec "$@" + EOF + chmod +x "${wrapper}" + echo "SFW_PREFIX=sfw ${wrapper}" >> "$GITHUB_ENV" From a20342eaba402f799bc43ae8238168e0bf9b22df Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 10:26:08 +0000 Subject: [PATCH 08/20] Route dependency fetches through SFW across all CI workflows Extend the firewall beyond ci's 'infra setup' to every dependency-fetching / build step, each via the CA-merge wrapper (${SFW_PREFIX:-}): - ci: also wrap check/test/lint (build.rs/npx can fetch); wrap the cargo cooldown check and both renovate 'npx --yes' steps (npm fetches). - benchmark_*: wrap the 'infra setup*' steps (perf steps build+upload to bencher, left unwrapped). - publish: wrap only 'infra setup'; the 'infra publish' upload steps must not go through the MITM (auth + registry writes). - sourcify: wrap the cargo build; the test run hits chain RPCs / Sourcify API, not registries, so it stays unwrapped. Excluded: validate-devcontainer (bare-metal socketdev/action doesn't work in the container; would need the npm-install sfw path this action dropped). --- .github/workflows/benchmark_archive.yml | 9 ++++- .github/workflows/benchmark_cargo_cmp.yml | 9 ++++- .github/workflows/benchmark_cargo_slang.yml | 9 ++++- .../workflows/benchmark_cargo_slang_v2.yml | 9 ++++- .github/workflows/benchmark_npm.yml | 11 +++++-- .github/workflows/ci.yml | 33 +++++++++++++------ .github/workflows/sourcify_single_chain.yml | 10 +++++- 7 files changed, 73 insertions(+), 17 deletions(-) diff --git a/.github/workflows/benchmark_archive.yml b/.github/workflows/benchmark_archive.yml index 64a615d875..8041b0a53f 100644 --- a/.github/workflows/benchmark_archive.yml +++ b/.github/workflows/benchmark_archive.yml @@ -36,8 +36,15 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" + # Wrap the dependency-fetching setup step through SFW (the perf step below + # only archives results, so it's left unwrapped). + - name: "Setup SFW (firewall)" + uses: "./.github/actions/setup-sfw" + with: + mode: "firewall" + - 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 diff --git a/.github/workflows/benchmark_cargo_cmp.yml b/.github/workflows/benchmark_cargo_cmp.yml index 45b454d398..7cf9688f09 100644 --- a/.github/workflows/benchmark_cargo_cmp.yml +++ b/.github/workflows/benchmark_cargo_cmp.yml @@ -69,8 +69,15 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" + # Wrap the dependency-fetching setup step through SFW (the perf steps below + # build/run benchmarks and upload to bencher, so they're left unwrapped). + - name: "Setup SFW (firewall)" + uses: "./.github/actions/setup-sfw" + with: + mode: "firewall" + - 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') }}" diff --git a/.github/workflows/benchmark_cargo_slang.yml b/.github/workflows/benchmark_cargo_slang.yml index 6d14ecd871..a7d61b394e 100644 --- a/.github/workflows/benchmark_cargo_slang.yml +++ b/.github/workflows/benchmark_cargo_slang.yml @@ -69,8 +69,15 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" + # Wrap the dependency-fetching setup step through SFW (the perf steps below + # build/run benchmarks and upload to bencher, so they're left unwrapped). + - name: "Setup SFW (firewall)" + uses: "./.github/actions/setup-sfw" + with: + mode: "firewall" + - 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') }}" diff --git a/.github/workflows/benchmark_cargo_slang_v2.yml b/.github/workflows/benchmark_cargo_slang_v2.yml index 085022c956..ee0893b4e7 100644 --- a/.github/workflows/benchmark_cargo_slang_v2.yml +++ b/.github/workflows/benchmark_cargo_slang_v2.yml @@ -68,8 +68,15 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" + # Wrap the dependency-fetching setup step through SFW (the perf steps below + # build/run benchmarks and upload to bencher, so they're left unwrapped). + - name: "Setup SFW (firewall)" + uses: "./.github/actions/setup-sfw" + with: + mode: "firewall" + - 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') }}" diff --git a/.github/workflows/benchmark_npm.yml b/.github/workflows/benchmark_npm.yml index eba0ed5f3b..a26abdd6c2 100644 --- a/.github/workflows/benchmark_npm.yml +++ b/.github/workflows/benchmark_npm.yml @@ -69,11 +69,18 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" + # Wrap the dependency-fetching steps through SFW (the perf steps below + # build/run benchmarks and upload to bencher, so they're left unwrapped). + - name: "Setup SFW (firewall)" + uses: "./.github/actions/setup-sfw" + with: + mode: "firewall" + - 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') }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b906c80981..83482557e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,21 +80,22 @@ jobs: # Run all CI steps in order: _SLANG_INFRA_CI_STEPS_ORDERED_ (keep in sync) # - # `${SFW_PREFIX:-}` wraps the command with `sfw` when SFW is available, and - # expands to nothing otherwise (so a non-Linux runner or SFW failure just - # runs setup unwrapped). PoC wraps only `setup` — that's where the cargo - # crates + pnpm package fetches happen. + # `${SFW_PREFIX:-}` wraps the command with the sfw CA-merge wrapper when SFW + # is available, and expands to nothing otherwise (so a non-Linux runner or + # SFW failure just runs unwrapped). Every infra step is wrapped: setup pulls + # crates/pnpm/toolchains, and check/test/lint can still hit the network via + # build.rs or npx, so the whole pipeline runs behind the firewall. - name: "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" + run: "${SFW_PREFIX:-} ./scripts/bin/infra test" - name: "infra lint" - run: "./scripts/bin/infra lint" + run: "${SFW_PREFIX:-} ./scripts/bin/infra lint" # On main pushes, warm the dependency and cargo-target caches with the perf # toolchain (bencher_cli, iai-callgrind-runner, and the bench binaries) so @@ -137,8 +138,14 @@ jobs: if: "${{ github.ref_name != 'main' }}" uses: "./.github/actions/cache/cargo-cooldown/restore" + # Route the cargo cooldown check (which fetches crate metadata) through SFW. + - name: "Setup SFW (firewall)" + uses: "./.github/actions/setup-sfw" + with: + mode: "firewall" + - 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' }}" @@ -156,8 +163,14 @@ jobs: with: persist-credentials: false + # `npx --yes renovate` pulls renovate (and its deps) from npm — wrap it. + - name: "Setup SFW (firewall)" + uses: "./.github/actions/setup-sfw" + with: + mode: "firewall" + - 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: @@ -165,7 +178,7 @@ jobs: # 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 diff --git a/.github/workflows/sourcify_single_chain.yml b/.github/workflows/sourcify_single_chain.yml index 75fc3e277b..dbaf01598b 100644 --- a/.github/workflows/sourcify_single_chain.yml +++ b/.github/workflows/sourcify_single_chain.yml @@ -71,8 +71,16 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" + # Wrap the build (which fetches crates) through SFW. The test run in the + # other job hits chain RPCs / the Sourcify API, not package registries, so + # it is intentionally left unwrapped. + - name: "Setup SFW (firewall)" + uses: "./.github/actions/setup-sfw" + with: + mode: "firewall" + - 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 From 813e12bf3bd6bd2ac8a63ce376fa22eec2e21d4b Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 10:40:53 +0000 Subject: [PATCH 09/20] =?UTF-8?q?Trim=20SFW=20comments=20=E2=80=94=20mark?= =?UTF-8?q?=20only=20the=20deliberately-unwrapped=20calls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the repeated 'wrap setup' comments on the Setup SFW steps; instead put a one-line marker on each intentionally-unwrapped call (benchmark perf, publish upload steps, sourcify test run) so the missing ${SFW_PREFIX:-} reads as deliberate, not an oversight. Also tighten the ci.yml anchor comment to one line and cut the action's description down to what it does (the CA-merge rationale lives on the wrapper step, not the description). --- .github/actions/setup-sfw/action.yml | 15 ++++----------- .github/workflows/benchmark_archive.yml | 3 +-- .github/workflows/benchmark_cargo_cmp.yml | 3 +-- .github/workflows/benchmark_cargo_slang.yml | 3 +-- .github/workflows/benchmark_cargo_slang_v2.yml | 3 +-- .github/workflows/benchmark_npm.yml | 3 +-- .github/workflows/ci.yml | 9 +-------- .github/workflows/sourcify_single_chain.yml | 4 +--- 8 files changed, 11 insertions(+), 32 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index ecaa18f6cd..202e3d508c 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -1,16 +1,9 @@ name: "Setup Socket Firewall (SFW)" description: > - Installs Socket Firewall (SFW) Free via socketdev/action and exports - SFW_PREFIX so callers can wrap their dependency-fetching commands with it. - Inside a wrap, sfw points the CA vars (SSL_CERT_FILE, CARGO_HTTP_CAINFO, …) at - a temp bundle holding ONLY its MITM root and hijacks SSL_CERT_DIR — so raw TLS - to hosts sfw passes through (github.com for the hermit bootstrap, - static.rust-lang.org for rustup) fails with "unable to get local issuer - certificate" for lack of the public roots. SFW_PREFIX therefore points at a - wrapper that, inside the wrap, merges sfw's root with the system bundle and - re-points the CA vars at the union before exec'ing the command. Linux-only - (the MITM is incompatible with macOS/Windows TLS stacks); on other runners - SFW_PREFIX is left unset and callers run unwrapped. + Installs Socket Firewall Free (Linux-only) 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). Left unset + on non-Linux runners, so callers run unwrapped. inputs: mode: diff --git a/.github/workflows/benchmark_archive.yml b/.github/workflows/benchmark_archive.yml index 8041b0a53f..82fcb39d80 100644 --- a/.github/workflows/benchmark_archive.yml +++ b/.github/workflows/benchmark_archive.yml @@ -36,8 +36,6 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - # Wrap the dependency-fetching setup step through SFW (the perf step below - # only archives results, so it's left unwrapped). - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" with: @@ -46,6 +44,7 @@ jobs: - name: "infra setup cargo" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo" + # Not SFW-wrapped: archives benchmark results, not a dependency install. - name: "infra perf archive" continue-on-error: true env: diff --git a/.github/workflows/benchmark_cargo_cmp.yml b/.github/workflows/benchmark_cargo_cmp.yml index 7cf9688f09..963ffcfcfe 100644 --- a/.github/workflows/benchmark_cargo_cmp.yml +++ b/.github/workflows/benchmark_cargo_cmp.yml @@ -69,8 +69,6 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - # Wrap the dependency-fetching setup step through SFW (the perf steps below - # build/run benchmarks and upload to bencher, so they're left unwrapped). - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" with: @@ -79,6 +77,7 @@ jobs: - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" + # Not SFW-wrapped: builds/runs benchmarks and uploads to bencher, not a dependency install. - name: "Smoke Test > infra perf cargo --smoke comparison" if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}" run: "./scripts/bin/infra perf cargo --smoke comparison" diff --git a/.github/workflows/benchmark_cargo_slang.yml b/.github/workflows/benchmark_cargo_slang.yml index a7d61b394e..9c6b72288c 100644 --- a/.github/workflows/benchmark_cargo_slang.yml +++ b/.github/workflows/benchmark_cargo_slang.yml @@ -69,8 +69,6 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - # Wrap the dependency-fetching setup step through SFW (the perf steps below - # build/run benchmarks and upload to bencher, so they're left unwrapped). - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" with: @@ -79,6 +77,7 @@ jobs: - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" + # Not SFW-wrapped: builds/runs benchmarks and uploads to bencher, not a dependency install. - name: "Smoke Test > infra perf cargo --smoke slang" if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}" run: "./scripts/bin/infra perf cargo --smoke slang" diff --git a/.github/workflows/benchmark_cargo_slang_v2.yml b/.github/workflows/benchmark_cargo_slang_v2.yml index ee0893b4e7..436fe99251 100644 --- a/.github/workflows/benchmark_cargo_slang_v2.yml +++ b/.github/workflows/benchmark_cargo_slang_v2.yml @@ -68,8 +68,6 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - # Wrap the dependency-fetching setup step through SFW (the perf steps below - # build/run benchmarks and upload to bencher, so they're left unwrapped). - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" with: @@ -78,6 +76,7 @@ jobs: - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" + # Not SFW-wrapped: builds/runs benchmarks and uploads to bencher, not a dependency install. - name: "Smoke Test > infra perf cargo --smoke slang-v2" if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}" run: "./scripts/bin/infra perf cargo --smoke slang-v2" diff --git a/.github/workflows/benchmark_npm.yml b/.github/workflows/benchmark_npm.yml index a26abdd6c2..4b3bc229bf 100644 --- a/.github/workflows/benchmark_npm.yml +++ b/.github/workflows/benchmark_npm.yml @@ -69,8 +69,6 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - # Wrap the dependency-fetching steps through SFW (the perf steps below - # build/run benchmarks and upload to bencher, so they're left unwrapped). - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" with: @@ -82,6 +80,7 @@ jobs: - name: "infra check npm" run: "${SFW_PREFIX:-} ./scripts/bin/infra check npm" + # Not SFW-wrapped: builds/runs benchmarks and uploads to bencher, not a dependency install. - name: "Smoke Test > infra perf npm --smoke" if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}" run: "./scripts/bin/infra perf npm --smoke" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83482557e9..b579718d4b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,12 +65,7 @@ jobs: if: "${{ github.ref_name != 'main' && github.event_name != 'merge_group' }}" uses: "./.github/actions/cache/cargo-target/restore" - # PoC: Socket Firewall (Free) via socketdev/action. Free has no warn-only - # mode — it blocks confirmed-malicious packages and only warns (without - # blocking) on AI-flagged-unconfirmed ones. slang's tree is known-clean, - # so this shouldn't block anything; the run validates MITM compatibility, - # notably whether hermit/rustup downloads survive it (see PR). Exports - # SFW_PREFIX (`sfw`) for the wrapped step below. + # Route the dependency installs in the steps below through Socket Firewall. - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" with: @@ -138,7 +133,6 @@ jobs: if: "${{ github.ref_name != 'main' }}" uses: "./.github/actions/cache/cargo-cooldown/restore" - # Route the cargo cooldown check (which fetches crate metadata) through SFW. - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" with: @@ -163,7 +157,6 @@ jobs: with: persist-credentials: false - # `npx --yes renovate` pulls renovate (and its deps) from npm — wrap it. - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" with: diff --git a/.github/workflows/sourcify_single_chain.yml b/.github/workflows/sourcify_single_chain.yml index dbaf01598b..08a5ef6f12 100644 --- a/.github/workflows/sourcify_single_chain.yml +++ b/.github/workflows/sourcify_single_chain.yml @@ -71,9 +71,6 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - # Wrap the build (which fetches crates) through SFW. The test run in the - # other job hits chain RPCs / the Sourcify API, not package registries, so - # it is intentionally left unwrapped. - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" with: @@ -181,6 +178,7 @@ jobs: with: name: "solidity_testing_sourcify_${{ inputs.chain_id }}" + # Not SFW-wrapped: hits chain RPCs / the Sourcify API, not package registries. - name: "Run Tests" env: CHECK_BINDER: "${{ inputs.check_binder }}" From e388a3fe6b3164d838037272df42e7bab0f24c4c Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 10:52:51 +0000 Subject: [PATCH 10/20] Drop dead non-Linux handling from setup-sfw action MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit slang CI is all ubuntu-24.04, so the 'runner.os == Linux' guards and the 'unset on non-Linux → run unwrapped' fallback never fired. Remove them and the matching description clause; if a non-Linux runner is ever added the action will fail loudly (socketdev/action + the bash/openssl wrapper are Linux-only). --- .github/actions/setup-sfw/action.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index 202e3d508c..df76759422 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -1,9 +1,8 @@ name: "Setup Socket Firewall (SFW)" description: > - Installs Socket Firewall Free (Linux-only) 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). Left unset - on non-Linux runners, so callers run unwrapped. + 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). inputs: mode: @@ -18,7 +17,6 @@ runs: using: "composite" steps: - name: "Install SFW" - if: "${{ runner.os == 'Linux' }}" uses: "socketdev/action@937f824ec476dfd164d4a4d9995751427b0be143" # v1 with: mode: "${{ inputs.mode }}" @@ -29,7 +27,6 @@ runs: # 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" - if: "${{ runner.os == 'Linux' }}" shell: "bash" run: | if ! command -v sfw &>/dev/null; then From 7b4533f408f78fa6aa957f04033f195903e31cd0 Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 11:52:08 +0000 Subject: [PATCH 11/20] Drop the setup-sfw 'mode' input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every call site passed mode: "firewall" — the action's default. Remove the input, hardcode "firewall" on the socketdev/action step, and drop the 'with: mode' block from all 12 call sites. --- .github/actions/setup-sfw/action.yml | 13 +++---------- .github/workflows/benchmark_archive.yml | 2 -- .github/workflows/benchmark_cargo_cmp.yml | 2 -- .github/workflows/benchmark_cargo_slang.yml | 2 -- .github/workflows/benchmark_cargo_slang_v2.yml | 2 -- .github/workflows/benchmark_npm.yml | 2 -- .github/workflows/ci.yml | 6 ------ .github/workflows/sourcify_single_chain.yml | 2 -- 8 files changed, 3 insertions(+), 28 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index df76759422..d83801730d 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -4,22 +4,15 @@ description: > prefix onto dependency commands to run installs behind the firewall (it also fixes sfw's CA handling — see the wrapper step). -inputs: - mode: - description: > - socketdev/action operation mode: "firewall" (the proxy), "patch", or - "cli". Socket Firewall Free has no warn-only mode — "firewall" blocks - confirmed-malicious packages and only warns on AI-flagged-unconfirmed ones. - required: false - default: "firewall" - runs: using: "composite" steps: + # "firewall" is the only useful Free mode (no warn-only): it blocks + # confirmed-malicious packages and warns on AI-flagged-unconfirmed ones. - name: "Install SFW" uses: "socketdev/action@937f824ec476dfd164d4a4d9995751427b0be143" # v1 with: - mode: "${{ inputs.mode }}" + 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 diff --git a/.github/workflows/benchmark_archive.yml b/.github/workflows/benchmark_archive.yml index 82fcb39d80..5614534770 100644 --- a/.github/workflows/benchmark_archive.yml +++ b/.github/workflows/benchmark_archive.yml @@ -38,8 +38,6 @@ jobs: - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" - with: - mode: "firewall" - name: "infra setup cargo" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo" diff --git a/.github/workflows/benchmark_cargo_cmp.yml b/.github/workflows/benchmark_cargo_cmp.yml index 963ffcfcfe..0f2e6fdce7 100644 --- a/.github/workflows/benchmark_cargo_cmp.yml +++ b/.github/workflows/benchmark_cargo_cmp.yml @@ -71,8 +71,6 @@ jobs: - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" - with: - mode: "firewall" - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" diff --git a/.github/workflows/benchmark_cargo_slang.yml b/.github/workflows/benchmark_cargo_slang.yml index 9c6b72288c..71d72112a6 100644 --- a/.github/workflows/benchmark_cargo_slang.yml +++ b/.github/workflows/benchmark_cargo_slang.yml @@ -71,8 +71,6 @@ jobs: - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" - with: - mode: "firewall" - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" diff --git a/.github/workflows/benchmark_cargo_slang_v2.yml b/.github/workflows/benchmark_cargo_slang_v2.yml index 436fe99251..3db1f5db2c 100644 --- a/.github/workflows/benchmark_cargo_slang_v2.yml +++ b/.github/workflows/benchmark_cargo_slang_v2.yml @@ -70,8 +70,6 @@ jobs: - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" - with: - mode: "firewall" - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" diff --git a/.github/workflows/benchmark_npm.yml b/.github/workflows/benchmark_npm.yml index 4b3bc229bf..89ff622f5e 100644 --- a/.github/workflows/benchmark_npm.yml +++ b/.github/workflows/benchmark_npm.yml @@ -71,8 +71,6 @@ jobs: - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" - with: - mode: "firewall" - name: "infra setup cargo npm" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo npm" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b579718d4b..8aa4c96616 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,8 +68,6 @@ jobs: # Route the dependency installs in the steps below through Socket Firewall. - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" - with: - mode: "firewall" # # Run all CI steps in order: _SLANG_INFRA_CI_STEPS_ORDERED_ (keep in sync) @@ -135,8 +133,6 @@ jobs: - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" - with: - mode: "firewall" - name: "Cargo cooldown check" run: "${SFW_PREFIX:-} ./scripts/bin/with-hermit cargo cooldown-check" @@ -159,8 +155,6 @@ jobs: - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" - with: - mode: "firewall" - name: "Renovate schema check" run: "${SFW_PREFIX:-} ./bin/npx --yes --package renovate -- renovate-config-validator renovate.json" diff --git a/.github/workflows/sourcify_single_chain.yml b/.github/workflows/sourcify_single_chain.yml index 08a5ef6f12..d837b95745 100644 --- a/.github/workflows/sourcify_single_chain.yml +++ b/.github/workflows/sourcify_single_chain.yml @@ -73,8 +73,6 @@ jobs: - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" - with: - mode: "firewall" - name: "Build Test Binary" run: "${SFW_PREFIX:-} ./scripts/bin/with-hermit cargo build --locked --bin solidity_testing_sourcify --release" From 54e9210209427017aaeadf42ddac2f3d4f12b430 Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 11:56:45 +0000 Subject: [PATCH 12/20] Drop inline 'not SFW-wrapped' markers The unwrapped-step rationale lives in the PR description; the scattered inline markers were redundant noise. --- .github/workflows/benchmark_archive.yml | 1 - .github/workflows/benchmark_cargo_cmp.yml | 1 - .github/workflows/benchmark_cargo_slang.yml | 1 - .github/workflows/benchmark_cargo_slang_v2.yml | 1 - .github/workflows/benchmark_npm.yml | 1 - .github/workflows/sourcify_single_chain.yml | 1 - 6 files changed, 6 deletions(-) diff --git a/.github/workflows/benchmark_archive.yml b/.github/workflows/benchmark_archive.yml index 5614534770..4d5866419c 100644 --- a/.github/workflows/benchmark_archive.yml +++ b/.github/workflows/benchmark_archive.yml @@ -42,7 +42,6 @@ jobs: - name: "infra setup cargo" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo" - # Not SFW-wrapped: archives benchmark results, not a dependency install. - name: "infra perf archive" continue-on-error: true env: diff --git a/.github/workflows/benchmark_cargo_cmp.yml b/.github/workflows/benchmark_cargo_cmp.yml index 0f2e6fdce7..c1ce1e24a6 100644 --- a/.github/workflows/benchmark_cargo_cmp.yml +++ b/.github/workflows/benchmark_cargo_cmp.yml @@ -75,7 +75,6 @@ jobs: - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" - # Not SFW-wrapped: builds/runs benchmarks and uploads to bencher, not a dependency install. - name: "Smoke Test > infra perf cargo --smoke comparison" if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}" run: "./scripts/bin/infra perf cargo --smoke comparison" diff --git a/.github/workflows/benchmark_cargo_slang.yml b/.github/workflows/benchmark_cargo_slang.yml index 71d72112a6..711a90f4a3 100644 --- a/.github/workflows/benchmark_cargo_slang.yml +++ b/.github/workflows/benchmark_cargo_slang.yml @@ -75,7 +75,6 @@ jobs: - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" - # Not SFW-wrapped: builds/runs benchmarks and uploads to bencher, not a dependency install. - name: "Smoke Test > infra perf cargo --smoke slang" if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}" run: "./scripts/bin/infra perf cargo --smoke slang" diff --git a/.github/workflows/benchmark_cargo_slang_v2.yml b/.github/workflows/benchmark_cargo_slang_v2.yml index 3db1f5db2c..7cf93422bd 100644 --- a/.github/workflows/benchmark_cargo_slang_v2.yml +++ b/.github/workflows/benchmark_cargo_slang_v2.yml @@ -74,7 +74,6 @@ jobs: - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" - # Not SFW-wrapped: builds/runs benchmarks and uploads to bencher, not a dependency install. - name: "Smoke Test > infra perf cargo --smoke slang-v2" if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}" run: "./scripts/bin/infra perf cargo --smoke slang-v2" diff --git a/.github/workflows/benchmark_npm.yml b/.github/workflows/benchmark_npm.yml index 89ff622f5e..993301a8c6 100644 --- a/.github/workflows/benchmark_npm.yml +++ b/.github/workflows/benchmark_npm.yml @@ -78,7 +78,6 @@ jobs: - name: "infra check npm" run: "${SFW_PREFIX:-} ./scripts/bin/infra check npm" - # Not SFW-wrapped: builds/runs benchmarks and uploads to bencher, not a dependency install. - name: "Smoke Test > infra perf npm --smoke" if: "${{ github.event_name == 'pull_request' && !contains(github.event.pull_request.labels.*.name, 'ci:perf') }}" run: "./scripts/bin/infra perf npm --smoke" diff --git a/.github/workflows/sourcify_single_chain.yml b/.github/workflows/sourcify_single_chain.yml index d837b95745..cf89c058af 100644 --- a/.github/workflows/sourcify_single_chain.yml +++ b/.github/workflows/sourcify_single_chain.yml @@ -176,7 +176,6 @@ jobs: with: name: "solidity_testing_sourcify_${{ inputs.chain_id }}" - # Not SFW-wrapped: hits chain RPCs / the Sourcify API, not package registries. - name: "Run Tests" env: CHECK_BINDER: "${{ inputs.check_binder }}" From ae6f9a060fd126116eb5972fa46bb1a43ab390a1 Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 11:58:19 +0000 Subject: [PATCH 13/20] Revert "TEMP: skip dependencies cache restore to force cold fetch through SFW" This reverts commit fd79cf53a1ed00deda7e1adc51b49ac57bfbc4f3. --- .github/workflows/ci.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8aa4c96616..bc3ad96ca3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,16 +46,8 @@ jobs: # Cache is updated in this workflow, and reused in subsequent workflows. # Always start with a fresh cache when running on the main branch. - # - # TEMP (SFW PoC — revert before merge): force `if: false` so the - # dependencies cache is NOT restored. That cache holds ~/.cache/hermit/, - # the pnpm store, and ~/.cargo/registry/; its restore-keys prefix - # (dependencies-${{ runner.os }}-v1-) means a key bump still hits, so the - # only way to force a cold fetch through the firewall is to skip the - # restore entirely. With it skipped, hermit/rustup/pnpm/cargo all download - # cold through SFW, which is what this PoC actually needs to validate. - name: "Restore Dependencies Cache" - if: "${{ false && github.ref_name != 'main' }}" + if: "${{ github.ref_name != 'main' }}" uses: "./.github/actions/cache/dependencies/restore" # Restore compiled Rust artifacts from a previous build on main for non-main runs, From 05f965b55bf171260335d8e136d76f765b014ceb Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 11:59:31 +0000 Subject: [PATCH 14/20] Drop the firewall-mode comment from setup-sfw --- .github/actions/setup-sfw/action.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index d83801730d..4b674e1fc2 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -7,8 +7,6 @@ description: > runs: using: "composite" steps: - # "firewall" is the only useful Free mode (no warn-only): it blocks - # confirmed-malicious packages and warns on AI-flagged-unconfirmed ones. - name: "Install SFW" uses: "socketdev/action@937f824ec476dfd164d4a4d9995751427b0be143" # v1 with: From 8eb19ebd40182a3994baddc855aac66bfe02c27d Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 12:00:31 +0000 Subject: [PATCH 15/20] Drop the Setup SFW route comment from ci.yml --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc3ad96ca3..5e33f8a2f8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,6 @@ jobs: if: "${{ github.ref_name != 'main' && github.event_name != 'merge_group' }}" uses: "./.github/actions/cache/cargo-target/restore" - # Route the dependency installs in the steps below through Socket Firewall. - name: "Setup SFW (firewall)" uses: "./.github/actions/setup-sfw" From 01fbb9fc77c079480be9ef3c706c5c06791e423b Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 12:00:55 +0000 Subject: [PATCH 16/20] Drop the SFW_PREFIX explainer comment from ci.yml --- .github/workflows/ci.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e33f8a2f8..c91c39b26d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,11 +64,6 @@ jobs: # Run all CI steps in order: _SLANG_INFRA_CI_STEPS_ORDERED_ (keep in sync) # - # `${SFW_PREFIX:-}` wraps the command with the sfw CA-merge wrapper when SFW - # is available, and expands to nothing otherwise (so a non-Linux runner or - # SFW failure just runs unwrapped). Every infra step is wrapped: setup pulls - # crates/pnpm/toolchains, and check/test/lint can still hit the network via - # build.rs or npx, so the whole pipeline runs behind the firewall. - name: "infra setup" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup" From 11b79337208c129e1dae327f1b64e3bd67614a00 Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 12:25:12 +0000 Subject: [PATCH 17/20] Strengthen setup-sfw: REQUESTS_CA_BUNDLE + document SFW_PREFIX contract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add REQUESTS_CA_BUNDLE to the merged-CA exports: pip/requests can ignore CURL_CA_BUNDLE inside a venv (pipenv), psf/requests#6660. Cheap insurance — the cold green run already exercised a wrapped pipenv install successfully. - Comment the load-bearing word-splitting contract: SFW_PREFIX is consumed unquoted so the shell splits it into 'sfw' + wrapper path; quoting breaks it. --- .github/actions/setup-sfw/action.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index 4b674e1fc2..3fcfb761fb 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -35,12 +35,15 @@ runs: 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}" + NODE_EXTRA_CA_CERTS="${merged}" REQUESTS_CA_BUNDLE="${merged}" unset SSL_CERT_DIR fi exec "$@" EOF chmod +x "${wrapper}" + # Consumed UNQUOTED by callers (`${SFW_PREFIX:-} `) so the shell splits + # it into two argv entries — `sfw` and the wrapper path. Quoting it breaks that. echo "SFW_PREFIX=sfw ${wrapper}" >> "$GITHUB_ENV" From 994ba5d71c62398a1eb03d7818d21fa510caa2a9 Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 12:25:12 +0000 Subject: [PATCH 18/20] Rename Setup SFW steps; hard-fail SFW before publish - Rename the step from 'Setup SFW (firewall)' to 'Setup SFW' at all call sites (there's only one mode now; the composite action keeps its full name). - Add a Require SFW guard to the publish job so it hard-fails if sfw didn't install, rather than silently publishing deps installed unprotected. The soft-fail default stays everywhere else (PR/benchmark flows tolerate flakes). --- .github/workflows/benchmark_archive.yml | 2 +- .github/workflows/benchmark_cargo_cmp.yml | 2 +- .github/workflows/benchmark_cargo_slang.yml | 2 +- .github/workflows/benchmark_cargo_slang_v2.yml | 2 +- .github/workflows/benchmark_npm.yml | 2 +- .github/workflows/ci.yml | 6 +++--- .github/workflows/sourcify_single_chain.yml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/benchmark_archive.yml b/.github/workflows/benchmark_archive.yml index 4d5866419c..41b18041eb 100644 --- a/.github/workflows/benchmark_archive.yml +++ b/.github/workflows/benchmark_archive.yml @@ -36,7 +36,7 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW (firewall)" + - name: "Setup SFW" uses: "./.github/actions/setup-sfw" - name: "infra setup cargo" diff --git a/.github/workflows/benchmark_cargo_cmp.yml b/.github/workflows/benchmark_cargo_cmp.yml index c1ce1e24a6..f646f3a7e5 100644 --- a/.github/workflows/benchmark_cargo_cmp.yml +++ b/.github/workflows/benchmark_cargo_cmp.yml @@ -69,7 +69,7 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW (firewall)" + - name: "Setup SFW" uses: "./.github/actions/setup-sfw" - name: "infra setup cargo pipenv" diff --git a/.github/workflows/benchmark_cargo_slang.yml b/.github/workflows/benchmark_cargo_slang.yml index 711a90f4a3..01f6605d95 100644 --- a/.github/workflows/benchmark_cargo_slang.yml +++ b/.github/workflows/benchmark_cargo_slang.yml @@ -69,7 +69,7 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW (firewall)" + - name: "Setup SFW" uses: "./.github/actions/setup-sfw" - name: "infra setup cargo pipenv" diff --git a/.github/workflows/benchmark_cargo_slang_v2.yml b/.github/workflows/benchmark_cargo_slang_v2.yml index 7cf93422bd..28659df9da 100644 --- a/.github/workflows/benchmark_cargo_slang_v2.yml +++ b/.github/workflows/benchmark_cargo_slang_v2.yml @@ -68,7 +68,7 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW (firewall)" + - name: "Setup SFW" uses: "./.github/actions/setup-sfw" - name: "infra setup cargo pipenv" diff --git a/.github/workflows/benchmark_npm.yml b/.github/workflows/benchmark_npm.yml index 993301a8c6..d29f7b32b9 100644 --- a/.github/workflows/benchmark_npm.yml +++ b/.github/workflows/benchmark_npm.yml @@ -69,7 +69,7 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW (firewall)" + - name: "Setup SFW" uses: "./.github/actions/setup-sfw" - name: "infra setup cargo npm" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c91c39b26d..294f43b5d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: if: "${{ github.ref_name != 'main' && github.event_name != 'merge_group' }}" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW (firewall)" + - name: "Setup SFW" uses: "./.github/actions/setup-sfw" # @@ -117,7 +117,7 @@ jobs: if: "${{ github.ref_name != 'main' }}" uses: "./.github/actions/cache/cargo-cooldown/restore" - - name: "Setup SFW (firewall)" + - name: "Setup SFW" uses: "./.github/actions/setup-sfw" - name: "Cargo cooldown check" @@ -139,7 +139,7 @@ jobs: with: persist-credentials: false - - name: "Setup SFW (firewall)" + - name: "Setup SFW" uses: "./.github/actions/setup-sfw" - name: "Renovate schema check" diff --git a/.github/workflows/sourcify_single_chain.yml b/.github/workflows/sourcify_single_chain.yml index cf89c058af..187b47751c 100644 --- a/.github/workflows/sourcify_single_chain.yml +++ b/.github/workflows/sourcify_single_chain.yml @@ -71,7 +71,7 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW (firewall)" + - name: "Setup SFW" uses: "./.github/actions/setup-sfw" - name: "Build Test Binary" From 1c7d6fd0f3465f9dc91833732cdf1fcc5b9870ef Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Tue, 2 Jun 2026 12:40:04 +0000 Subject: [PATCH 19/20] Soft-fail SFW; hard-fail only on the release path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SFW now soft-fails: the install step is continue-on-error and the action warns (not errors) when sfw is absent, leaving SFW_PREFIX unset so callers run unprotected. A socket.dev outage no longer reddens every PR. The release path stays strict: a Require SFW guard fails the job when SFW_PREFIX is empty, added to both the changesets and publish jobs (each sets up SFW on its own runner, so the needs-chain alone isn't enough). Callers use ${SFW_PREFIX:-} since unset is now a valid state. test/lint are unwrapped — they don't fetch dependencies. --- .github/actions/setup-sfw/action.yml | 13 ++++++++-- .github/workflows/ci.yml | 11 ++++---- .github/workflows/publish.yml | 38 ++++++++++++++++++++++++---- CONTRIBUTING.md | 30 ++++++++++++++++++++++ 4 files changed, 80 insertions(+), 12 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index 3fcfb761fb..38ce1c67d1 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -3,11 +3,19 @@ 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). + Soft-fails: if SFW can't be installed, SFW_PREFIX is left unset and callers + run unprotected (so a socket.dev outage doesn't redden every PR). Jobs that + must never install unprotected deps (publish) add an explicit Require SFW + guard that fails when SFW_PREFIX is empty. runs: using: "composite" steps: - name: "Install SFW" + # Soft-fail: a socket.dev outage must not block PR/benchmark CI. The + # wrapper step below leaves SFW_PREFIX unset when sfw is absent; publish + # jobs re-assert it via a Require SFW guard. + continue-on-error: true uses: "socketdev/action@937f824ec476dfd164d4a4d9995751427b0be143" # v1 with: mode: "firewall" @@ -21,7 +29,7 @@ runs: shell: "bash" run: | if ! command -v sfw &>/dev/null; then - echo "::warning::sfw not found after install — callers will run unwrapped" + echo "::warning::sfw not found after install — dependency installs will run UNPROTECTED. Jobs requiring SFW (publish) fail at their Require SFW guard." exit 0 fi wrapper="${RUNNER_TEMP}/sfw-wrap" @@ -45,5 +53,6 @@ runs: EOF chmod +x "${wrapper}" # Consumed UNQUOTED by callers (`${SFW_PREFIX:-} `) so the shell splits - # it into two argv entries — `sfw` and the wrapper path. Quoting it breaks that. + # it into two argv entries — `sfw` and the wrapper path. Quoting it breaks that; + # when unset (soft-fail) it expands to nothing and runs unwrapped. echo "SFW_PREFIX=sfw ${wrapper}" >> "$GITHUB_ENV" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 294f43b5d2..a77933cee2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,21 +71,22 @@ jobs: run: "${SFW_PREFIX:-} ./scripts/bin/infra check" - name: "infra test" - run: "${SFW_PREFIX:-} ./scripts/bin/infra test" + run: "./scripts/bin/infra test" - name: "infra lint" - run: "${SFW_PREFIX:-} ./scripts/bin/infra lint" + run: "./scripts/bin/infra lint" # 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' }}" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 520b5d25a8..6e740fc6c9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -99,8 +99,16 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" + - name: "Setup SFW" + uses: "./.github/actions/setup-sfw" + + # Release path: refuse to install deps unprotected if SFW is unavailable. + # Each release job needs its own guard (they set up SFW on separate runners). + - name: "Require SFW" + run: '[ -n "${SFW_PREFIX:-}" ] || { echo "::error::SFW unavailable — refusing to install deps unprotected on the release path"; exit 1; }' + - 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" @@ -184,8 +192,19 @@ jobs: if: "${{ needs.changesets.outputs.publishNeeded != 'true' }}" uses: "./.github/actions/cache/cargo-target/restore" + - name: "Setup SFW" + uses: "./.github/actions/setup-sfw" + + # On a real release this job builds the published npm tarball, so if SFW + # couldn't be installed, stop here rather than build the release without it. + # On PRs/pending pushes the build still runs without SFW (same condition the + # cache-restore steps above gate on). + - name: "Require SFW" + if: "${{ needs.changesets.outputs.publishNeeded == 'true' }}" + run: '[ -n "${SFW_PREFIX:-}" ] || { echo "::error::SFW unavailable — refusing to build release artifacts unprotected"; exit 1; }' + - name: "infra setup" - run: "./scripts/bin/infra setup" + run: "${SFW_PREFIX:-} ./scripts/bin/infra setup" - name: "Prepare publish artifacts" id: "prepare" @@ -242,8 +261,11 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" + - name: "Setup SFW" + uses: "./.github/actions/setup-sfw" + - 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 @@ -402,9 +424,15 @@ jobs: name: "${{ needs.prepare.outputs.artifact-name }}" path: "target/publish-artifacts/" - # Compile infra_cli with no registry token in scope. + - name: "Setup SFW" + uses: "./.github/actions/setup-sfw" + + # Compile infra_cli with no registry token in scope. Routed through SFW when + # available, but no Require SFW guard here: if SFW didn't install, this still + # builds without it (the published bytes were already checked behind SFW in + # prepare). 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. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9aef777340..6e12dcbad8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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. + +**Soft-fail by default.** If Socket Firewall can't be installed (e.g. a socket.dev +outage), the affected step logs a `::warning::` and runs _unprotected_ rather than +failing, so a Socket outage doesn't fail every PR. The exception is the **release +path** (`changesets` and `publish` jobs in `publish.yml`): those carry a `Require SFW` +guard and fail hard if SFW is unavailable, so we never publish dependencies that were +installed unprotected. + +### When a build fails because of Socket + +The two failure modes look different: + +- **SFW unavailable (not a block).** A wrapped step logs + `::warning::sfw not found after install …` and continues unprotected. This is a + Socket/network availability problem, not a flagged package. On the release path the + `Require SFW` step fails instead — 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. From 25e4c7a201289c49a6d2c968d92ce2a9b6338bf9 Mon Sep 17 00:00:00 2001 From: Bas van Gijzel Date: Wed, 10 Jun 2026 14:47:15 +0000 Subject: [PATCH 20/20] Require SFW by default; jobs opt out via the optional input --- .github/actions/setup-sfw/action.yml | 33 +++++++++++------ .github/workflows/benchmark_archive.yml | 4 ++- .github/workflows/benchmark_cargo_cmp.yml | 4 ++- .github/workflows/benchmark_cargo_slang.yml | 4 ++- .../workflows/benchmark_cargo_slang_v2.yml | 4 ++- .github/workflows/benchmark_npm.yml | 4 ++- .github/workflows/ci.yml | 12 +++++-- .github/workflows/publish.yml | 36 +++++++++---------- .github/workflows/sourcify_single_chain.yml | 4 ++- CONTRIBUTING.md | 20 +++++------ 10 files changed, 77 insertions(+), 48 deletions(-) diff --git a/.github/actions/setup-sfw/action.yml b/.github/actions/setup-sfw/action.yml index 38ce1c67d1..56489c4268 100644 --- a/.github/actions/setup-sfw/action.yml +++ b/.github/actions/setup-sfw/action.yml @@ -3,18 +3,25 @@ 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). - Soft-fails: if SFW can't be installed, SFW_PREFIX is left unset and callers - run unprotected (so a socket.dev outage doesn't redden every PR). Jobs that - must never install unprotected deps (publish) add an explicit Require SFW - guard that fails when SFW_PREFIX is empty. + 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" - # Soft-fail: a socket.dev outage must not block PR/benchmark CI. The - # wrapper step below leaves SFW_PREFIX unset when sfw is absent; publish - # jobs re-assert it via a Require SFW guard. + # 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: @@ -27,10 +34,16 @@ runs: # 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 - echo "::warning::sfw not found after install — dependency installs will run UNPROTECTED. Jobs requiring SFW (publish) fail at their Require SFW guard." - exit 0 + 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' @@ -54,5 +67,5 @@ runs: chmod +x "${wrapper}" # Consumed UNQUOTED by callers (`${SFW_PREFIX:-} `) so the shell splits # it into two argv entries — `sfw` and the wrapper path. Quoting it breaks that; - # when unset (soft-fail) it expands to nothing and runs unwrapped. + # when unset (optional + failed install) it expands to nothing and runs unwrapped. echo "SFW_PREFIX=sfw ${wrapper}" >> "$GITHUB_ENV" diff --git a/.github/workflows/benchmark_archive.yml b/.github/workflows/benchmark_archive.yml index 41b18041eb..71b0d5313e 100644 --- a/.github/workflows/benchmark_archive.yml +++ b/.github/workflows/benchmark_archive.yml @@ -36,8 +36,10 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW" + - name: "Setup SFW (optional)" uses: "./.github/actions/setup-sfw" + with: + optional: "true" - name: "infra setup cargo" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo" diff --git a/.github/workflows/benchmark_cargo_cmp.yml b/.github/workflows/benchmark_cargo_cmp.yml index f646f3a7e5..20a57c7c71 100644 --- a/.github/workflows/benchmark_cargo_cmp.yml +++ b/.github/workflows/benchmark_cargo_cmp.yml @@ -69,8 +69,10 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW" + - name: "Setup SFW (optional)" uses: "./.github/actions/setup-sfw" + with: + optional: "true" - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" diff --git a/.github/workflows/benchmark_cargo_slang.yml b/.github/workflows/benchmark_cargo_slang.yml index 01f6605d95..d91f3f866b 100644 --- a/.github/workflows/benchmark_cargo_slang.yml +++ b/.github/workflows/benchmark_cargo_slang.yml @@ -69,8 +69,10 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW" + - name: "Setup SFW (optional)" uses: "./.github/actions/setup-sfw" + with: + optional: "true" - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" diff --git a/.github/workflows/benchmark_cargo_slang_v2.yml b/.github/workflows/benchmark_cargo_slang_v2.yml index 28659df9da..b405b3874d 100644 --- a/.github/workflows/benchmark_cargo_slang_v2.yml +++ b/.github/workflows/benchmark_cargo_slang_v2.yml @@ -68,8 +68,10 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW" + - name: "Setup SFW (optional)" uses: "./.github/actions/setup-sfw" + with: + optional: "true" - name: "infra setup cargo pipenv" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo pipenv" diff --git a/.github/workflows/benchmark_npm.yml b/.github/workflows/benchmark_npm.yml index d29f7b32b9..83cdc063ad 100644 --- a/.github/workflows/benchmark_npm.yml +++ b/.github/workflows/benchmark_npm.yml @@ -69,8 +69,10 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW" + - name: "Setup SFW (optional)" uses: "./.github/actions/setup-sfw" + with: + optional: "true" - name: "infra setup cargo npm" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup cargo npm" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a77933cee2..bcbba78a38 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,8 +57,10 @@ jobs: if: "${{ github.ref_name != 'main' && github.event_name != 'merge_group' }}" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW" + - 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) @@ -118,8 +120,10 @@ jobs: if: "${{ github.ref_name != 'main' }}" uses: "./.github/actions/cache/cargo-cooldown/restore" - - name: "Setup SFW" + - name: "Setup SFW (optional)" uses: "./.github/actions/setup-sfw" + with: + optional: "true" - name: "Cargo cooldown check" run: "${SFW_PREFIX:-} ./scripts/bin/with-hermit cargo cooldown-check" @@ -140,8 +144,10 @@ jobs: with: persist-credentials: false - - name: "Setup SFW" + - name: "Setup SFW (optional)" uses: "./.github/actions/setup-sfw" + with: + optional: "true" - name: "Renovate schema check" run: "${SFW_PREFIX:-} ./bin/npx --yes --package renovate -- renovate-config-validator renovate.json" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 6e740fc6c9..0047bb9a88 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -99,14 +99,11 @@ 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" - # Release path: refuse to install deps unprotected if SFW is unavailable. - # Each release job needs its own guard (they set up SFW on separate runners). - - name: "Require SFW" - run: '[ -n "${SFW_PREFIX:-}" ] || { echo "::error::SFW unavailable — refusing to install deps unprotected on the release path"; exit 1; }' - - name: "infra setup" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup" @@ -192,16 +189,13 @@ 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" - - # On a real release this job builds the published npm tarball, so if SFW - # couldn't be installed, stop here rather than build the release without it. - # On PRs/pending pushes the build still runs without SFW (same condition the - # cache-restore steps above gate on). - - name: "Require SFW" - if: "${{ needs.changesets.outputs.publishNeeded == 'true' }}" - run: '[ -n "${SFW_PREFIX:-}" ] || { echo "::error::SFW unavailable — refusing to build release artifacts unprotected"; exit 1; }' + with: + optional: "${{ needs.changesets.outputs.publishNeeded != 'true' }}" - name: "infra setup" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup" @@ -261,8 +255,10 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW" + - name: "Setup SFW (optional)" uses: "./.github/actions/setup-sfw" + with: + optional: "true" - name: "infra setup" run: "${SFW_PREFIX:-} ./scripts/bin/infra setup" @@ -424,13 +420,15 @@ jobs: name: "${{ needs.prepare.outputs.artifact-name }}" path: "target/publish-artifacts/" - - name: "Setup SFW" + - 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. Routed through SFW when - # available, but no Require SFW guard here: if SFW didn't install, this still - # builds without it (the published bytes were already checked behind SFW in - # prepare). The infra publish steps below upload with auth, left unwrapped. + # 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: "${SFW_PREFIX:-} ./scripts/bin/infra --help" diff --git a/.github/workflows/sourcify_single_chain.yml b/.github/workflows/sourcify_single_chain.yml index 187b47751c..cfd4aa3284 100644 --- a/.github/workflows/sourcify_single_chain.yml +++ b/.github/workflows/sourcify_single_chain.yml @@ -71,8 +71,10 @@ jobs: - name: "Restore Cargo Target Cache" uses: "./.github/actions/cache/cargo-target/restore" - - name: "Setup SFW" + - name: "Setup SFW (optional)" uses: "./.github/actions/setup-sfw" + with: + optional: "true" - name: "Build Test Binary" run: "${SFW_PREFIX:-} ./scripts/bin/with-hermit cargo build --locked --bin solidity_testing_sourcify --release" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6e12dcbad8..f85a55ebba 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,21 +83,21 @@ it and exports `SFW_PREFIX`, which prefixes the dependency-fetching steps (`infr setup`, `infra check`, and the cooldown and renovate checks). It runs **only in CI** — local `infra` commands are unaffected. -**Soft-fail by default.** If Socket Firewall can't be installed (e.g. a socket.dev -outage), the affected step logs a `::warning::` and runs _unprotected_ rather than -failing, so a Socket outage doesn't fail every PR. The exception is the **release -path** (`changesets` and `publish` jobs in `publish.yml`): those carry a `Require SFW` -guard and fail hard if SFW is unavailable, so we never publish dependencies that were -installed unprotected. +**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).** A wrapped step logs - `::warning::sfw not found after install …` and continues unprotected. This is a - Socket/network availability problem, not a flagged package. On the release path the - `Require SFW` step fails instead — re-run once Socket is reachable. +- **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