Strona główna / Blog / Argos CI: branch jako komenda

Blog · case study

Argos CI CVE-2026-59960: nazwa brancha staje się komendą /bin/sh — kiedy metadata z pull requestu trafia do uprzywilejowanego runnera

CVE-2026-59960 nie jest kolejnym „command injection w bibliotece”. Ciekawsze jest, gdzie leży granica zaufania: dane, które zespoły traktują jak metadane — nazwa brancha lub ref — mogą być kontrolowane przez autora pull requestu. W podatnej ścieżce @argos-ci/core trafiają do ciągu przekazanego do execSync, a więc ostatecznie do /bin/sh -c. W CI to szczególnie groźne, bo runner często ma więcej uprawnień niż osoba otwierająca PR: token repozytorium, cache, dostęp do artefaktów, czasem sekrety chmurowe albo możliwość publikacji paczek.

CVE-2026-59960 (High, fix 6.2.1): przy hasRemoteContentAccess=false wartość ref/branch (attacker-controlled) interpolowana do polecenia powłoki → /bin/sh -c → arbitrary command execution na runnerze.
Preconditions: podatna wersja + użycie podatnej ścieżki + attacker-controlled ref/branch + wykonanie workflow w kontekście uruchamiającym tę ścieżkę.

Attack path i pull_request_target

attacker creates PR / branch with crafted ref -> CI event imports attacker-controlled metadata -> Argos handles branch/ref -> interpolacja do execSync -> /bin/sh -c -> arbitrary command execution on runner -> secret theft / artifact tampering / lateral movement / package publishing

Najważniejszy punkt jest między metadanymi SCM a procesem powłoki — trust-boundary crossing, którego zwykłe SCA nie pokaże. W GitHub Actions pull_request_target może działać w kontekście repozytorium bazowego, potencjalnie z dostępem do sekretów niedostępnych zwykłemu PR z forka; sam fakt użycia nie dowodzi exploita, ale jest krytycznym enrichmentem. Repo A (prywatny runner, brak PR od niezaufanych, minimalny token) i Repo B (publiczne PR, pull_request_target, token write, publikacja artefaktów) z identyczną wersją Argos mają zupełnie inny priorytet — w drugim realny blast radius bliższy supply-chain incident niż „lokalnemu RCE”.

Remediation, verification, compromise

Primary remediation: upgrade @argos-ci/core do 6.2.1+. W CI patch biblioteki to tylko jedna warstwa — równolegle: ograniczyć permissions GITHUB_TOKEN, nie udostępniać sekretów niezaufanym PR, unikać checkoutu kodu z forka w uprzywilejowanym pull_request_target, izolować self-hosted runnery, ephemeral runners dla publicznych workflow, traktować ref/branch/tag/commit message jako untrusted input. Closure odpowiada na: czy runtime używa 6.2.1+ (nie manifest — lockfile, cache, faktyczna wersja w jobie), czy podatny path jest osiągalny (hasRemoteContentAccess i workflow), czy untrusted PR może wywołać job z wysokimi uprawnieniami (trigger, permissions, secrets, environment protection, typ runnera), czy test negatywny potwierdza brak interpretacji shella. Jeśli przed poprawką podatny workflow był publicznie osiągalny z wartościowymi sekretami — compromise assessment: review logów runnera/workflow, rotacja sekretów dostępnych dla jobu, sprawdzenie artefaktów/paczek z okresu, analiza zmian wykonanych tokenem CI. Active exploitation: źródła nie potwierdzają — nie dopisuj bez danych IR.

Powiązane na blogu

Źródła

Nota redakcyjna: stan informacji 12 września 2026. CVE-2026-59960 (@argos-ci/core <6.2.1): nazwa brancha/ref interpolowana do execSync na runnerze CI. Brak potwierdzenia active exploitation. Artykuł podkreśla kontekst workflow (event, permissions, runner, secrets) i metadata SCM jako untrusted input.

Zapamiętaj jedno

Sama nazwa brancha wygląda banalnie, ale w automatyzacji jest inputem przekraczającym granicę z SCM do powłoki. Jeśli za tą granicą stoi uprzywilejowany runner, „High” bywa operacyjnie ważniejsze niż wiele „Critical” na nieosiągalnych assetach — a VE w CI/CD musi zbierać kontekst workflow, nie tylko SBOM.