security(ci): stop piping Solana CLI install to shell #186
Labels
No labels
agent:implement
agent:ready
backend
bug
cannot-reproduce
confirmed
desktop
docs
documentation
duplicate
enhancement
feature
frontend
good first issue
help wanted
high-risk
in-review
invalid
mobile
needs-triage
P0-critical
P1-high
P2-medium
P3-low
qa
QA
question
ready
report
responsive
security
security-escalate
smart-contract
solana
tablet
test-pass
ux
wallet-issue
wallet:keplr
wallet:metamask
wallet:station
wallet:walletconnect
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-bridge-monorepo#186
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
.github/workflows/test.ymljobcontracts-solanastill installs the Solana CLI by piping a remote installer into a shell. Onmainas of 2026-09-12:The URL path names a release (
v2.2.12). That is not a content digest. TLS to the publisher does not pin the installer script or the binaries it later fetches. A publisher compromise, path rewrite, or CDN substitution can change what the runner executes without a git diff. The nextanchor build/anchor testthen runs under that toolchain.Woodpecker is the live required check (
.woodpecker.yamlfrom #172 / #174). That pipeline does not run this installer. The GitHub Actions workflow is still in tree, still matchespackages/contracts-solana/**, and still runs onpush/pull_request/workflow_dispatchif GitHub Actions is enabled on a mirror or fork.This is not #185 (Compose
image:digest pins, FE-H3). This is not #172 (Woodpecker enablement). Do not reopen those for this.Internal review id: FE-H4 (high). Still in source 2026-09-12.
Bundle (same ticket, do not split):
sh -c "$(curl …)",curl … | sh,wget … | bash, and equivalents). Required for the Solana CLI step above.cargo install --git https://github.com/coral-xyz/anchor avm --forcewithout a commit SHA. Pin AVM to a git SHA or install a checksummed binary, then keepavm install 0.32.1 && avm use 0.32.1.test.ymlremains, GitHub Actions) that fails if workflow YAML reintroduces pipe-to-shell.Supply-chain / CI toolchain. No community autoland. Do not add
ready.Impact (today vs hypothetical)
Funds are not at risk today on the live bridge operator, vaults, or on-chain programs from this line alone. Woodpecker does not execute it. This is not a permissionless drain path.
Hypothetical, sticky until the installer is gone:
GITHUB_TOKEN, the checked-out tree, and the ability to write$GITHUB_PATH. That can steal the job token, poisonanchor buildartifacts, or ship a greenanchor teston a lying toolchain.packages/contracts-solana) are the affected suite. A hostilesolana/solana-test-validatorcan change BPF compilation or validator behavior so security tests pass on a lie.cargo install --git … avm --forcecan fetch a different AVM HEAD than reviewers expect, then install Anchor 0.32.1 through that installer.Today vs hypothetical: the defect is in source today (pipe-to-shell). Exploitation needs control of the install URL contents or the git default branch of the AVM repo, not a mainnet call. Treat as High for autoland and community because it is a live supply-chain finding, not because vault inventory is reachable from GitHub Actions.
Do not publish a working installer-substitution recipe or a malicious AVM commit.
Invariants
curl/wgetoutput as a shell script (sh -c "$(curl …)",curl … | sh,wget … | bash,bash <(curl …)).contracts-solanais obtained only via (a) a tarball/binary whose sha256 is committed and checked before extract/exec, or (b) a container image pinnedname:tag@sha256:<64 hex>. A versioned HTTPS path is not a pin.cargo install --gitwithout--rev <full sha>is forbidden in these workflows.anchor testcoverage in this ticket.Constraints / guardrails
scripts/ci/install-solana-cli.sh) that: downloads a documented artifact URL, checkssha256sum -cagainst a committed digest file, then installs to a path appended to$GITHUB_PATH. Fail closed on mismatch. Do notcurl | shas a fallback.contracts-solanajob (or a Woodpecker step, if this suite is later moved) must setcontainer: name:tag@sha256:…(or Woodpeckerimage:) and must not run the Anza installer. Resolve the digest at implement time; do not invent hashes in review comments.packages/contracts-solanaalready requires a documented bump. Do not silently jump majors.scripts/check_ci_no_pipe_shell.sh) that scans.github/workflows/*.ymland.woodpecker.yamlfor pipe-to-shell patterns. Fail with the offending line. Ignore#comments. Do not treatcurlused only for HTTP health checks (e.g.curl -sf http://localhost:…/status) as a hit..woodpecker.yaml) and into.github/workflows/test.yml.uses: actions/checkout@v4SHA, Foundry toolchain tags, orcargo install cargo-audit. Those are separate surfaces (#185 already carved out GHAuses:SHA pins).ready.Relevant files
.github/workflows/test.ymlcontracts-solanapipe-to-shell Solana install; unpinnedcargo install --gitAVM.github/workflows/e2e.yml:latestdocker run.woodpecker.yamlscripts/ci/install-solana-cli.sh(new, if vendoring)scripts/check_ci_no_pipe_shell.sh(new)packages/contracts-solana/Anchor.tomlRecommended direction
Install Solana CLIstep with checksum-verified download or a digest-pinned image that already has Solana 2.2.12. Commit the sha256 (or image digest) in-repo.cargo install --git https://github.com/coral-xyz/anchor --rev <full 40-hex sha> avm --locked --force(or a checksummed binary). Keepavm install 0.32.1 && avm use 0.32.1.scripts/check_ci_no_pipe_shell.sh: scan workflow YAML; fail on pipe-to-shell; ignore comments and health-checkcurl.test.yml. Do not skip it without a recorded reason in the yaml (same bar as #172’s cargo-audit skip).Acceptance criteria
.github/workflows/test.ymlhas nosh -c "$(curl …)"(orcurl … \| sh) for Solana or any other tool.contracts-solanais checksum-verified or comes from a digest-pinned image. The committed digest is the pin, not the URL pathv2.2.12.cargo install --gitwithout--revis gone from these workflows.anchor build/anchor test --skip-buildstill run the existing Solana program tests when that job is executed; no program source change.Test plan (functional paths)
sh -c "$(curlandcurl .*\| *(ba)?shsolana --versionis 2.2.12--rev40-hex or a checksum checkscripts/check_ci_no_pipe_shell.shon current tree after fixcurl -sf http://localhost:26657/statusine2e.ymlTest plan (attack, hack, and abuse)
Non-exploitative. File/CI fixtures only. Do not use as an installer-substitution recipe.
sh -c "$(curl -sSfL https://release.anza.xyz/…/install)"curl https://example.invalid/install | bash#commentshof the payloadcargo install --git … avmwithout--rev|| curl | shafter a failed checksumVerification criteria
sh -c "$(curl/curl .*\\| *(ba)?sh/wget .*\\| *(ba)?sh/bash <\\(curlin.github/workflows/or.woodpecker.yamloutside comments.@sha256:[0-9a-f]{64}) is present and referenced by the install step.contracts-solanajob still runs on GitHub Actions:solana --versionandanchor --versionmatch the pinned versions; existinganchor test --skip-buildstays green. That is toolchain integrity, not a funds-path test.Out of scope
image:digest pins (FE-H3).uses:SHAs (actions/checkout@v4,foundry-rs/foundry-toolchain@v1,dtolnay/rust-toolchain@stable).e2e.ymldocker run … localterra-cl8y:latest(belongs with #185).First-pass model recommendation
Recommendation: grok-high
Rationale: Security class (CI supply-chain, internal FE-H4 high) and founder-required deploy/keys-adjacent pinning. Composer is disallowed: High/security, not a low-risk first pass. Expected files are
.github/workflows/test.yml, a checksum helper or digest-pinned image reference, and a Woodpecker/GHA grep — file count does not establish safety. A wrong pin or a leftovercurl | shfallback can reintroduce remote code execution on CI. Verify with the fail-closed scanner and digest mismatch fixtures, not by exercising a hostile installer URL.