
Esempio di Seal Security — app Maven vulnerabile (SnakeYAML CVE-2022-1471) corretta con versioni sealed; integrazione con GitHub Actions e Jenkins
Un'applicazione Spring Boot minima e volutamente vulnerabile, usata per dimostrare, end-to-end, come Seal Security ripara una CVE nota sostituendo una dipendenza vulnerabile con una versione sealed (backport, drop-in) — senza alcuna modifica alle coordinate dichiarate o al codice.
È progettata come uno smoke test completo per la CLI Seal in CI/CD: esegui l'app, innesca un exploit reale, esegui Seal e osserva lo stesso exploit venire bloccato.
| Ecosistema | Java / Maven |
| Pacchetto vulnerabile | org.yaml:snakeyaml:1.33 |
| CVE | CVE‑2022‑1471 — deserializzazione del Constructor di SnakeYAML → esecuzione di codice remoto (CVSS 9.8) |
| Versione sealed (con fix) | snakeyaml 1.33+sp1 dal registry Maven di Seal |
| Integrazione | CLI Seal come passaggio di build — mostrata sia per GitHub Actions che per Jenkins |
Il progetto dichiara anche altre dipendenze vulnerabili note (jackson-databind 2.13.1,
commons-text 1.9, spring-core/web 5.3.26, json-smart 2.4.8, commons-lang3 3.12.0,
spring-boot 2.7.18), ciascuna delle quali Seal ripara con una build sealed.
La pagina di benvenuto riceve un name e lo analizza tramite il costruttore predefinito di SnakeYAML:
Yaml yaml = new Yaml();
Object parsed = yaml.load(name); // SnakeYAML 1.33 — CVE-2022-1471
Il Constructor predefinito di SnakeYAML istanzia tipi Java arbitrari indicati nello YAML —
la primitiva di object injection alla base di CVE‑2022‑1471. Un payload che nomina javax.script.ScriptEngineManager
(il punto di ingresso della classica catena di gadget RCE) è sufficiente a dimostrare che il parser costruirà
qualsiasi classe indicata da un attaccante.
Richiesta normale
/?name=alice → Welcome, alice!
Richiesta exploit
/?name=!!javax.script.ScriptEngineManager []
Lo SnakeYAML vulnerabile istanzia la classe indicata dall'attaccante, l'app mostra una pagina
“You've been pwned” e il server viene poi terminato (pochi secondi dopo, così la pagina
viene visualizzata per prima). Ricaricando, l'app non c'è più — via ngrok vedrai una pagina
“endpoint offline”. Questa è la primitiva di object injection; un exploit completo la sfrutta
(via URLClassLoader) per caricare ed eseguire codice remoto.
.
├── pom.xml # declares the vulnerable dependencies
├── src/main/java/… # Spring Boot app (HelloController, DataService)
├── .seal-actions.yml # optional local fix-mode mapping (vulnerable → sealed)
├── Jenkinsfile # example Jenkins (Groovy) pipeline with the Seal stage
└── .github/workflows/
├── build-and-run.yml # build + expose the app for browser testing
└── seal-security.yml # run Seal remediation, then start the app
Seal è SaaS, ospitato da Seal — non viene installato nulla nel tuo ambiente e tutto il traffico è HTTPS in uscita sulla sola TCP 443. Per eseguire la remediation hai bisogno di:
| Segreto / credenziale |
|---|
Configurali in Settings → Secrets and variables → Actions (GitHub) oppure Manage Jenkins → Credentials (Jenkins). Non committare mai i token nel repository.
Aggiungi all'allowlist questi host Seal per la porta 443 in uscita:
app.sealsecurity.io, authorization.sealsecurity.io, cli.sealsecurity.io e, per
gli artefatti Maven sealed, maven.sealsecurity.io. Il binario della CLI viene scaricato da
github.com / objects.githubusercontent.com.
mvn clean package -DskipTests
java -jar target/java-example-1.0.0.jar # → http://localhost:8080
Apri http://localhost:8080/?name=alice (funziona), poi
http://localhost:8080/?name=!!javax.script.ScriptEngineManager [] — l'app mostra
"You've been pwned" e il server viene terminato pochi secondi dopo.
La CLI Seal viene eseguita come un passaggio aggiuntivo, dopo la risoluzione delle dipendenze e prima del packaging. Analizza le dipendenze risolte e riscrive quelle vulnerabili nelle rispettive versioni sealed, usando la modalità di fix remote (la policy è gestita centralmente nella UI di Seal).
Usa seal-community/cli-action:
- uses: seal-community/cli-action@latest
with:
mode: fix
fix_mode: remote
token: ${{ secrets.SEAL_TOKEN }}
target: pom.xml # the manifest for this ecosystem
Esegui la pipeline tramite Actions → “Seal Security Remediation” → Run workflow. Vedi
.github/workflows/seal-security.yml.
Uno stage aggiuntivo, dopo la risoluzione delle dipendenze e prima del packaging. Vedi
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=pom.xml
'''
}
}
SEAL_TOKEN proviene dalla credenziale Jenkins seal-token; imposta SEAL_PROJECT sul tuo
Seal Project ID.
Dopo seal fix, le dipendenze vulnerabili risolvono a build sealed dal registry Maven di Seal —
stesse coordinate, fix di sicurezza backportato:
Una versione sealed è lo stesso artefatto con il fix di sicurezza backportato — un sostitutivo drop-in, nessuna modifica al codice e nessun upgrade di major version.
Esegui di nuovo l'exploit contro l'app riparata. Lo snakeyaml sealed rifiuta di istanziare
i tipi globali dannosi, quindi il payload non carica più il JAR remoto — l'esecuzione di codice
remoto è bloccata.
seal fix al manifest specifico — pom.xml per Maven. Per una build multi-modulo,
esegui un seal fix per ciascun manifest.Questa è l'intera integrazione — uno stage, solo traffico in uscita, nessuna modifica al codice applicativo.
| Usato per |
|---|
| Dove va |
|---|
| Seal token | Autenticazione della CLI Seal | Segreto GitHub Actions SEAL_TOKEN / credenziale Jenkins "Secret text" seal-token |
| ngrok token (facoltativo) | Esporre l'app in esecuzione a un browser per i test | Segreto GitHub Actions NGROK_TOKEN |
| Dipendenza | Prima | Dopo (sealed) |
|---|
| org.yaml:snakeyaml | 1.33 | 1.33+sp1 |
| com.fasterxml.jackson.core:jackson-databind | 2.13.1 | 2.13.1+sp1 |
| org.apache.commons:commons-text | 1.9 | 1.9+sp1 |
| org.springframework:spring-core / spring-web | 5.3.26 | 5.3.26+sp1 |
| net.minidev:json-smart | 2.4.8 | 2.4.8+sp1 |
| org.apache.commons:commons-lang3 | 3.12.0 | 3.12.0+sp1 |
| org.springframework.boot:spring-boot | 2.7.18 | 2.7.18+sp1 |