Argo CD Repo-Server Hardening After the October 2026 Advisories

Argo CD shipped eight security fixes on 6 October 2026, four rated CVSS 9.9 and five of them in the repo-server. Here is the version math, the AppProject bypass that breaks a tenancy boundary, and which mitigations the advisories admit do nothing.

By VVV Ops ·

On 6 October 2026 the Argo CD maintainers published eight security fixes in one release. Four are rated CVSS 9.9. Five of the eight live in one component, argocd-repo-server, and the cheapest attack path through most of them is a Git commit. If your Argo CD install predates that release, someone with push access to a repository Argo CD already trusts can probably read every credential it holds. Argo CD repo-server hardening is now an upgrade plus a set of configuration decisions, and the second half is the part teams skip. Here is what we changed in client clusters and why.

What shipped on 6 October 2026

The eight advisories were published the next day, 7 October, and the fixes are in v3.3.15, v3.4.10, v3.5.4 and v3.6.0-rc2.

| Advisory | CVE | CVSS | Component | What it gives an attacker | |---|---|---|---|---| | GHSA-fw5c-w8rc-j7fx | none assigned | 9.9 | repo-server | Command execution via a Kustomize Helm config home | | GHSA-9v9p-x54c-58gc | none assigned | 9.9 | repo-server | Command execution via a Kustomize remote ref | | GHSA-m3vr-7329-44ww | none assigned | 9.9 | repo-server | Arbitrary file read via a Jsonnet import | | GHSA-fmxq-cgp8-87wp | CVE-2026-77459 | 9.9 | application-controller | AppProject restrictions bypassed by delete hooks | | GHSA-4439-h7jw-5cjj | CVE-2025-61560 | 7.5 | API server | Login rate limit bypass through concurrent requests | | GHSA-j6cw-g6p4-7hch | CVE-2026-55797 | 8.8 | repo-server | Command injection via an SSH repository proxy URL | | GHSA-w996-f2wq-x9c6 | none assigned | 6.5 | repo-server | Repo-server memory exhaustion via an oversized OCI manifest | | GHSA-g3ff-q88g-chrj | none assigned | 4.9 | API server | Extension proxy RBAC ignores the Application namespace |

Read the pattern rather than the scores. Three of the four criticals have the same shape, repository content steering a renderer's extension point, in three different renderers, and all three reach the same process.

The repo-server holds credentials the docs say it does not

Argo CD's security documentation describes the repo-server as a service that clones permitted repositories and generates manifests, with no Kubernetes privileges and no stored service credentials, git included. That framing drives a lot of architecture decisions. Teams put the repo-server in the low-value column, skip the NetworkPolicy, and spend their hardening budget on the API server.

The advisories describe something else. The Jsonnet advisory lists what a successful file read reaches: the process environment, the projected service-account token, mounted private keys, and repository credentials. The SSH proxy advisory says injected commands can read other Git, Helm and OCI credentials stored for the repo-server. Both are maintainer-written descriptions of the same component the docs call credential-free.

Both statements are true at different times. The repo-server stores nothing at rest. It receives credentials per request, and during a render they sit in its environment and filesystem. Code execution during a render is therefore credential disclosure, and a render is exactly what every Argo CD Application asks the repo-server to do. Treat the repo-server as your highest-value pod in the argocd namespace. We were wrong about this for longer than we would like.

Rendering is code execution, and three criticals prove it

Kustomize, Helm and Jsonnet are not declarative. Each one has an extension point that executes something, and each extension point is configured by files in the repository being rendered.

The Kustomize Helm case is the clearest. With --enable-helm set, a kustomization can point Helm's config home at a directory the repository controls, Helm loads a downloader plugin from there, and the plugin runs when a helmCharts repository URL uses the plugin's protocol. No install step. The Kustomize remote ref case is a missing -- separator: Kustomize passes the ref or version query value from a remote base URL straight to git fetch, so --upload-pack=<command> is read as a Git option and runs. The Jsonnet case needs no plugin at all, because import, importstr and importbin accepted absolute paths, ../ traversal and symlinks pointing out of the repository.

Two of the three are old. The Kustomize remote ref issue arrived with the Kustomize 4.5.7 to 5.0.1 upgrade in v2.7.0, in 2023, and the Jsonnet importer has behaved this way since v0.9.0 in 2018. These were not new mistakes. They were a rendering model that nobody had audited as an execution sandbox.

There is a second delivery path that catches people who assume Git is the only input. POST /api/v1/applications/manifestsWithFiles, which backs argocd app diff --local --server-side-generate, uploads files for rendering and needs only Application get on an existing Application. A read-only developer token was enough.

AppProject stopped being the boundary on delete

CVE-2026-77459 is the one to take to an architecture review, because it breaks a boundary rather than a process.

Argo CD checks an Application's AppProject before creating resources during a sync: destination namespaces and servers, cluster-scoped and namespaced allow and deny lists, resource names. PreDelete and PostDelete hooks skipped that check. They are created on the destination cluster with the application-controller's credentials, which in most installs is close to cluster-admin.

The exploit needs push access to a repository an Application already syncs from. Commit a PostDelete hook naming a namespace, group, kind or name the AppProject would refuse, and nothing happens at sync time. The controller adds a delete finalizer and the Application reports Synced. The hook fires when somebody deletes the Application, and a Job hook can then create objects Argo CD never cleans up. A ClusterRoleBinding is the obvious payload.

PostDelete hooks arrived in v2.10.0 and PreDelete hooks in v3.3.0, so every 2.10 and later install is affected. PreSync, Sync, PostSync and SyncFail hooks always went through the project check, which is why this sat undiscovered.

The lesson outlives the patch. AppProject enforcement happens in Argo CD's own code, not in Kubernetes, so it holds only on the code paths where someone wrote the check. We now treat AppProject as tenancy ergonomics and put the hard boundary in the controller's RBAC, which is covered below. If you took our trust boundaries for AI agents in CI/CD post and applied the same reasoning to Argo CD, this is where it lands.

If you are on 2.x there is no patch

The advisories say the same thing about older minors: 2.x and 3.0 through 3.2 are out of support and will not receive a patch, and none of the eight lists a patched 2.x version. The fix list is v3.3.15, v3.4.10, v3.5.4 and v3.6.0-rc2.

That makes the decision structural. A 2.x install is not waiting for a backport.

# Which minor are you actually on, per component
kubectl -n argocd get deploy -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.template.spec.containers[0].image}{"\n"}{end}'

# Are the two riskiest renderers switched on?
kubectl -n argocd get cm argocd-cm \
  -o jsonpath='{.data.kustomize\.buildOptions}{"\n"}{.data.jsonnet\.enable}{"\n"}'

# Any SSH repository or credential template carrying a proxy value?
kubectl -n argocd get secret -l argocd.argoproj.io/secret-type \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.data.proxy}{"\n"}{end}'

An empty first field in the second command means --enable-helm was never set, which closes the Kustomize Helm case entirely. A non-empty proxy field on an SSH repository secret is the CVE-2026-55797 precondition, and it is worth reading closely rather than counting, since a credential template's proxy applies to every matching SSH repository that has no credentials of its own.

For a 3.x install, patch within the minor and move on. For 2.x, the jump to 3.3 crosses a major, so budget a maintenance window and read the upgrade notes for each minor you skip. We have not seen a 2.x to 3.3 move that was a same-day job.

Workarounds that hold, and the ones that do not

If you cannot patch this week, the advisories are unusually honest about which mitigations are real. Several do nothing.

| Mitigation | Covers | Verdict | |---|---|---| | Remove --enable-helm from kustomize.buildOptions in argocd-cm | Kustomize Helm config home | Complete, unless Applications render Helm through Kustomize | | Set jsonnet.enable: "false" in argocd-cm | Jsonnet file read | Complete, and Jsonnet Applications stop rendering | | Pin Kustomize below 5.0 via kustomize.path.<version> | Kustomize remote ref | Partial, and leaving 5.x registered reopens it | | Clear the proxy field on SSH repositories and credential templates | CVE-2026-55797 | Complete for that path, HTTPS proxies are unaffected | | Restrict applications, get or block POST /api/v1/applications/manifestsWithFiles | Upload path only | Partial, Git-committed payloads still render on next sync | | Block repo-server egress | Kustomize remote ref | None, the advisory says so explicitly | | Leave proxy extensions off in argocd-cmd-params-cm | Extension proxy RBAC | Complete, they are off by default |

The egress row is the one that surprises people. Network controls feel like the right answer to a rendering exploit and they do not touch this one, because the command runs locally over a file:// transport. The extension proxy row is the cheap win: server.enable.proxy.extension is off by default, so most installs were never exposed, and extension.config in argocd-cm is the other half to check.

Narrowing the blast radius after you patch

Patching closes eight holes in a component that will have more. The configuration work is what changes the next advisory from an incident into a maintenance ticket. Four changes, in the order we apply them.

Narrow the application-controller's cluster access. CVE-2026-77459 was bounded entirely by what the controller could reach, and the advisory names this as a mitigation. The default cluster role is broad, and Argo CD's own security page tells operators to narrow it. If the controller cannot create ClusterRoleBindings, a delete-hook bypass is a much smaller event.

Restrict who can create or update repositories. The SSH proxy injection needs only repository create or update, and that includes project-scoped repository permissions. Argo CD's RBAC model lives in the argocd-rbac-cm ConfigMap under policy.csv:

# Illustrative. Names like acme are placeholders.
p, role:acme-developer, applications, get, acme/*, allow
p, role:acme-developer, applications, sync, acme/*, allow
p, role:acme-developer, repositories, get, *, allow
# repositories create and update stay with platform admins only
p, role:acme-platform, repositories, create, *, allow
p, role:acme-platform, repositories, update, *, allow
g, acme:developers, role:acme-developer

Cap manifest memory. The security page documents reposerver.max.combined.directory.manifests.size in argocd-cmd-params-cm, with '3M' as its example value, and notes that in-memory manifests can reach roughly 300 times their on-disk size for a maliciously crafted file. The limit applies per Application. The OCI manifest fix in v3.5.4 is a hard 4 MiB cap, so this key covers the directory case only.

Stop using sourceRepos: ['*']. The default AppProject ships with it, which permits any source, and the OCI advisory names restricting sourceRepos as the main workaround. Anyone who can push to an allowed registry can still publish an oversized manifest, so this narrows the set of people who can try.

Treat Git write access as production access while you are here. Three of the four criticals start with a commit. Branch protection and required review on the repositories Argo CD syncs are security controls, not hygiene, and they belong in the same conversation as the pipeline gates in our DevSecOps implementation guide.

One thing we would not do: add a scanner that greps repositories for malicious kustomizations. The payloads are legal configuration, the signatures move, and you will be maintaining detection for a bug class the patch already closed.

If MCP servers are also in your stack, the failure modes rhyme with the credential and sandbox problems in MCP server security in production, and the same question applies to both: what does this process hold while it is running, not at rest?

When to Get Help

Most teams can do the 3.x patch themselves in an afternoon. The work that stalls is the 2.x to 3.3 crossing, narrowing an application-controller role that half the platform depends on without breaking syncs, and deciding what AppProject is still allowed to mean once you know it is not a Kubernetes boundary.

We do that work with clients: version audit, a controller RBAC narrowing with a tested rollback, repository permission cleanup, and a written record of which mitigations you applied and why, which is the artifact your SOC 2 auditor asks for. If you are running Argo CD across more than a handful of clusters and have not touched the October advisories yet, get in touch and we will start with the audit.

Tags: argo cd repo-server hardening, secure kubernetes deployments, devsecops implementation, gitops security, security automation, cloud security consulting