
Пример Seal Security — уязвимое pip-приложение (PyYAML CVE-2020-14343), исправленное до sealed-версий; интеграция GitHub Actions + Jenkins
Минимальное намеренно уязвимое Flask-приложение, которое используется для сквозной демонстрации того, как Seal Security устраняет известную CVE, заменяя уязвимую зависимость на запечатанную (с бэкпортом исправления, готовую к замене) версию — без изменения ваших объявленных зависимостей или вашего кода.
Оно задумано как сквозной смоук-тест для Seal CLI в CI/CD: запустите приложение, воспроизведите реальную эксплуатацию уязвимости, запустите Seal и наблюдайте, как та же атака блокируется.
| Экосистема | Python / pip |
| Уязвимый пакет | PyYAML==5.1 |
| CVE | CVE‑2020‑14343 — yaml.load / FullLoader десериализация → произвольное выполнение кода |
| Запечатанная (исправленная) версия | pyyaml 5.1+sp1 из PyPI-реестра Seal |
| Интеграция | Seal CLI как один шаг сборки — показано для GitHub Actions и Jenkins |
Страница приветствия принимает параметр name и разбирает его с помощью загрузчика PyYAML по умолчанию:
parsed = yaml.load(name) # PyYAML 5.1 → unsafe FullLoader (CVE-2020-14343)
В PyYAML 5.1 yaml.load() без явного SafeLoader использует FullLoader, который может создавать произвольные объекты Python из недоверенного ввода. Атакующий отправляет YAML-полезную нагрузку, которая выполняет произвольный код Python на сервере.
Обычный запрос
/?name=alice → Welcome, alice!
Эксплуатирующий запрос — передайте этот YAML как name (в логах workflow он уже URL‑закодирован):
!!python/object/apply:tuple [!!python/object/apply:map [!!python/name:eval , ["__import__('subprocess').check_output(['id']).decode()"]]]
Уязвимый PyYAML десериализует и выполняет полезную нагрузку, приложение показывает страницу “You've been pwned”, а затем сервер убивается (через несколько секунд, чтобы страница успела отобразиться). При перезагрузке приложение исчезает — через ngrok вы увидите страницу “endpoint offline”.
.
├── app.py # the vulnerable Flask app
├── requirements.txt # declares PyYAML==5.1
├── Jenkinsfile # example Jenkins (Groovy) pipeline with the Seal stage
└── .github/workflows/
├── build-and-run.yml # run + expose the app for browser testing
└── seal-security.yml # run Seal remediation, then start the app
Seal — это SaaS, размещаемый компанией Seal — в вашем окружении ничего не устанавливается, а весь трафик — только исходящий HTTPS по TCP 443. Для запуска устранения уязвимости вам понадобятся:
| Секрет / учётные данные |
|---|
Настройте их в Settings → Secrets and variables → Actions (GitHub) или Manage Jenkins → Credentials (Jenkins). Никогда не коммитьте токены в репозиторий.
Добавьте следующие хосты Seal в белый список для исходящих соединений на порт 443:
app.sealsecurity.io, authorization.sealsecurity.io, cli.sealsecurity.io и — для запечатанных pip-пакетов — pypi.sealsecurity.io. Бинарный файл CLI загружается с
github.com / objects.githubusercontent.com.
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python app.py # → http://localhost:5000
Откройте http://localhost:5000/?name=alice (работает), затем передайте указанную выше эксплуатирующую полезную нагрузку в качестве name — приложение покажет "You've been pwned", и через несколько секунд сервер будет убит.
Seal CLI выполняется как один дополнительный шаг — после pip install и до упаковки. Он сканирует разрешённые зависимости и заменяет уязвимые на их запечатанные версии, используя удалённый режим исправления (политика управляется централизованно в интерфейсе Seal).
Использует seal-community/cli-action:
- uses: seal-community/cli-action@latest
with:
mode: fix
fix_mode: remote
token: ${{ secrets.SEAL_TOKEN }}
target: requirements.txt # the manifest for this ecosystem
Запустите его через Actions → “Seal Security Remediation” → Run workflow. См.
.github/workflows/seal-security.yml.
Одна добавленная стадия — после установки и до упаковки. См. Jenkinsfile:
stage('Seal') {
steps {
sh '''
curl -fsSL https://github.com/seal-community/cli/releases/download/latest/seal-linux-amd64-latest -o seal
chmod +x seal
./seal fix --mode remote "$SEAL_MANIFEST" # SEAL_MANIFEST=requirements.txt
'''
}
}
SEAL_TOKEN берётся из учётных данных Jenkins seal-token; задайте SEAL_PROJECT равным вашему Project ID в Seal.
После seal fix уязвимая зависимость разрешается в запечатанную сборку из PyPI-реестра Seal — тот же пакет с бэкпортированным исправлением безопасности:
| Зависимость | До | После (запечатанная версия) |
|---|---|---|
| PyYAML | 5.1 | 5.1+sp1 |
Запечатанная версия — это тот же пакет с бэкпортированным исправлением безопасности: готовая к замене (drop‑in) версия, без изменений кода и без обновления до новой мажорной версии.
Повторно запустите эксплойт против исправленного приложения. Запечатанный PyYAML отказывается создавать вредоносные объекты, поэтому yaml.load выбрасывает исключение вместо выполнения полезной нагрузки, и приложение отвечает “Invalid input — payload rejected.” Обычные имена по-прежнему работают.
seal fix на конкретный манифест — requirements.txt для pip. Для репозитория с несколькими манифестами/lock-файлами запускайте по одному seal fix на каждый манифест.На этом вся интеграция — одна стадия, только исходящие соединения, без изменений в коде приложения.
| Назначение |
|---|
| Где указывается |
|---|
| Seal token | Аутентификация Seal CLI | секрет GitHub Actions SEAL_TOKEN / учётные данные Jenkins "Secret text" seal-token |
| ngrok token (необязательно) | Открытие запущенного приложения в браузере для тестирования | секрет GitHub Actions NGROK_TOKEN |