CVE-2026-82447 w Skyvern to świetny przykład podatności, której nie widać, jeśli review zatrzyma się na pojedynczym komponencie. Pierwsza część pipeline wyglądała bezpiecznie — prompt w TextPromptBlock był renderowany przez sandboxed Jinja environment. Można było powiedzieć: untrusted template data → sandboxed Jinja → safe rendering. Problem polegał na tym, że wynik tego renderowania trafiał później do drugiego renderowania — tym razem przez zwykły jinja2.Environment, bez sandboxa.
CVE ma CVSS v4 8.7 High i obejmuje Skyvern >= 0.2.1, < 1.0.45; fix w 1.0.45 usuwa drugie, niesandboxowane renderowanie. Ale najważniejsza lekcja brzmi: security control może działać poprawnie na jednym etapie i zostać całkowicie unieważniony przez następny etap pipeline.
Co robił TextPromptBlock?
Skyvern buduje workflow'y, w których wartości pochodzą z parametrów workflow, wyników poprzednich bloków albo danych pobieranych podczas automatyzacji, a następnie są wstawiane do promptów. Pierwsze renderowanie wykorzystywało mechanizm ograniczający niebezpieczne możliwości Jinja. To ważne, bo Jinja nie jest zwykłym string formatterem — niewłaściwie użyty template engine staje się interpreterem kodu. Dlatego sandbox jest naturalnym security control.
Ale dane potraktowano jak template drugi raz
VulnCheck opisuje problem precyzyjnie: first render: sandboxed, second render: unsandboxed. Drugi etap korzystał z PromptEngine, który używał zwykłego jinja2.Environment. Wartość, która w pierwszym kroku była tylko danymi, mogła po pierwszym renderowaniu utworzyć nową składnię Jinja: parameter value → contains characters that become template syntax → first pass inserts them literally → second pass interprets them. To klasyczny second-order injection.
Pierwszy sanitizer może działać idealnie i nadal przegrać
Wyobraźmy proces Input → sanitizer → safe value → serialization → parser → execution. Security review sprawdza sanitizer i potwierdza „works correctly”. Ale jeśli downstream ponownie interpretuje wartość w innym kontekście, bezpieczeństwo pierwszej warstwy może nie mieć znaczenia. W Skyvern pierwsze renderowanie nie musiało być zepsute — problemem było, że rendered output became template source again. Wartość zmienia role w pipeline: Stage 1: DATA → Stage 2: OUTPUT → Stage 3: TEMPLATE SOURCE → Stage 4: CODE. Jeśli model zagrożeń śledzi tylko pierwszy etap, uzna ją za bezpieczną. Podobne klasy błędów są w SQL budowanym etapami, Markdown → HTML, YAML → templating, generated code, shell wrappers, LLM prompt pipelines i workflow engines.
Dlaczego to szczególnie ciekawe w systemach AI/agentowych?
W systemach jak Skyvern dane często pochodzą z nieoczywistych miejsc — nie tylko z formularza użytkownika, ale z poprzedniego bloku workflow, strony internetowej, odpowiedzi modelu, wyniku automatyzacji albo zewnętrznego API. Atakujący nie zawsze musi mieć bezpośredni dostęp do pola „prompt” — może kontrolować wcześniejsze źródło danych, które później przepłynie przez workflow:
Dlatego agentic systems wymagają data-flow-aware security.
CVSS mówi PR:L — ale kto naprawdę kontroluje dane?
CVSS v4 dla CVE-2026-82447 ma PR:L, co odpowiada scenariuszowi wymagającemu wpływu na workflow parameters lub upstream output. Ale środowiskowy risk zależy od źródła danych. Deployment A: workflow values only from trusted admins. Deployment B: workflow ingests external web/API content — tam realna ścieżka jest znacznie bardziej osiągalna. Znów: same CVE, same version, same CVSS != same environmental risk.
Fix usuwa etap, a nie dodaje kolejny filtr
Commit naprawczy ma wymowny tytuł: remove non-sandboxed second render in TextPromptBlock. Kod usuwa ponowne renderowanie w send_prompt(), a komentarz jasno mówi, że prompt jest już wcześniej w pełni wyrenderowany i wartości parametrów powinny pozostać literalne. To dobra zasada secure design: po wyznaczeniu granicy data/code nie interpretuj ponownie danych jako kodu — lepsze niż dokładanie kolejnej denylisty. Patch dodaje też testy sprawdzające, że dane zawierające {{ }} po pierwszym renderze trafiają dalej literalnie. Dobry regression test nie sprawdza tylko „PoC X już nie działa”, lecz właściwość: user-controlled values are rendered exactly once — invariant bezpieczeństwa.
„Mamy sandbox” to nie dowód
Security questionnaire często pyta „czy template rendering jest sandboxed?”. Dla podatnej wersji Skyvern odpowiedź brzmiałaby „Tak” — i jednocześnie byłaby niewystarczająca. Prawidłowe pytanie: „czy wszystkie interpretacje template data odbywają się w sandboxie i czy po bezpiecznym renderze wynik nie jest ponownie interpretowany?”. To pokazuje ograniczenie checkbox security. Zamiast myśleć „we use sandbox”, lepiej zdefiniować właściwość: untrusted workflow data can never reach an unsandboxed template evaluator — i sprawdzać ją przez cały data flow.
Jakie compensating controls mają sens?
Permanent remediation: upgrade to 1.0.45+. Jeżeli upgrade musi poczekać, można ograniczać: kto może tworzyć/edytować TextPromptBlock, jakie źródła dostarczają workflow parameters, czy nieufny upstream output trafia do tego bloku, uprawnienia procesu Skyvern, network egress i dostęp do sekretów. Ale uwaga: sandbox pierwszego renderowania nie jest skutecznym compensating control dla tego CVE, skoro problemem jest drugi render. To bardzo ważne dla risk acceptance.
Wniosek
Skyvern pokazuje coś ważniejszego niż samo SSTI. Security control może być obecny, poprawnie skonfigurowany i skuteczny na swoim etapie — a mimo to cały system pozostaje podatny. Dlaczego? Bo bezpieczeństwo pipeline jest właściwością całego przepływu danych, nie pojedynczej funkcji. Najważniejsze pytanie nie brzmi „czy używamy sandboxa?”, tylko: czy dane po opuszczeniu sandboxa mogą zostać ponownie potraktowane jako kod?
Powiązane na blogu
- Content editor, który kończy z RCE — dane, które stają się kodem na serwerze
- 12 CVE dla MCP w mniej niż 9 sekund — bezpieczeństwo narzędzi agentów AI
Źródła
- VulnCheck — Skyvern before 1.0.45 Sandbox Escape via TextPromptBlock / CVE-2026-82447 (29.08.2026) — vulncheck.com
- Skyvern fix commit — remove non-sandboxed second render in TextPromptBlock — github.com
- Skyvern vulnerable block.py (v1.0.44) — github.com
Nota redakcyjna: stan informacji 30 sierpnia 2026, 10:21 CEST. CVE-2026-82447 obejmuje Skyvern od 0.2.1 do wersji wcześniejszych niż 1.0.45. Fix usuwa niesandboxowane drugie renderowanie promptu. Artykuł używa określenia „second-order injection” jako analitycznego opisu data flow; formalne CWE w advisory to CWE-1336.