CVE-2026-5199: Verified Reproduction
CVE-2026-5199: Temporal Server: batcher worker cross-namespace authorization bypass BatchActivityWithProtobuf
CVE-2026-5199 is verified against temporal · go. Affected versions: 1.29.0 – 1.29.4 (and 1.30.0 – 1.30.2). Fixed in 1.29.5. Vulnerability class: Auth Bypass. This low reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00184.
What Is CVE-2026-5199?
CVE-2026-5199 is a medium-severity authorization bypass (CWE-285) in Temporal Server's per-namespace batcher worker, which lets an authenticated caller with writer access to one namespace mutate workflows in a different, victim namespace. Pruva reproduced it (reproduction REPRO-2026-00184).
CVE-2026-5199 Severity & CVSS Score
CVE-2026-5199 is rated low severity, with a CVSS base score of 2.3 out of 10.
Low — limited impact or hard to exploit. Address in the normal cycle.
Affected temporal Versions
temporal · go versions 1.29.0 – 1.29.4 (and 1.30.0 – 1.30.2) are affected.
How to Reproduce CVE-2026-5199
pruva-verify REPRO-2026-00184 curl -O https://www.pruva.dev/api/v1/reproductions/REPRO-2026-00184/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-5199
- Vulnerable
v1.29.4 - Fixed
v1.29.5
How the agent worked
Root Cause and Exploit Chain for CVE-2026-5199
CVE-2026-5199 is an authorization bypass in Temporal Server's per-namespace batcher worker (service/worker/batcher/activities.go). The BatchActivityWithProtobuf activity validated only batchParams.NamespaceId (a namespace UUID) via checkNamespaceID, but then forwarded batchParams.Request.Namespace (a namespace name) to the internal frontend client. Because the internal frontend connection runs with NoopClaimMapper → RoleAdmin, any namespace name supplied by an attacker was executed unconditionally. An authenticated attacker with writer access to one namespace could craft a BatchOperationInput whose NamespaceId matched their own namespace (passing the check) while Request.Namespace targeted a victim namespace, causing the batcher to signal, cancel, terminate, or reset workflows in the victim namespace without authorization.
- Package/Component:
go.temporal.io/server/service/worker/batcher— specificallyBatchActivityWithProtobufandstartTaskProcessorProtobuf - Affected versions:
1.29.0–1.29.4and1.30.0–1.30.2 - Fixed versions:
v1.29.5 - Risk level: Medium (CVSS 3.1 ~4.2, Authenticated)
- Consequences: Cross-namespace workflow mutation (signal, cancel, terminate, reset) by an authenticated principal authorized in a different namespace
Root Cause
The vulnerable code path in service/worker/batcher/activities.go (v1.29.4) is:
BatchActivityWithProtobufreceives aBatchOperationInputprotobuf.- It calls
checkNamespaceID(batchParams.NamespaceId)which only verifies the namespace UUID matches the worker-bound namespace ID. - It then uses
batchParams.Request.Namespacefor downstream operations:config.namespace = batchParams.Request.NamespacestartTaskProcessorProtobuf(..., batchParams.Request.Namespace, ...)
startTaskProcessorProtobufpasses this attacker-controlled namespace name to all internal frontend client calls (SignalWorkflowExecution,CancelWorkflowExecution,TerminateWorkflowExecution,ResetWorkflowExecution, etc.).- The internal frontend client (
NoopClaimMapper → RoleAdmin) does not perform namespace-scoped authorization checks on these internal calls, so the operation executes against the victim namespace.
The fix commit 90738c6200 ("Check namespaces in batch workflow") replaced checkNamespaceID with checkNamespaceProtobuf, which validates both:
batchParams.NamespaceId == worker-bound namespace IDbatchParams.Request.Namespace == worker-bound namespace name
It also changed BatchActivityWithProtobuf to derive the namespace from a.namespace.String() (the worker-bound name) rather than from the protobuf request, and updated startTaskProcessorProtobuf to use the already-validated namespace parameter instead of batchOperation.Request.Namespace.
Reproduction Steps
The reproduction script is repro/reproduction_steps.sh. It performs the following:
- Clones the
temporalio/temporalrepository (or uses an existing clone). - Builds the
temporal-serverbinary from source for bothv1.29.4(vulnerable) andv1.29.5(fixed). - Starts a real Temporal Server with SQLite in-memory persistence (
development-sqlite.yaml) and--allow-no-auth. - Polls port
127.0.0.1:7233until the gRPC frontend is listening. - Runs a standalone Go program (
repro_main.go) that:- Connects to the real server via gRPC
- Creates two namespaces:
attacker-nsandvictim-ns - Starts a victim workflow in
victim-ns - Gets the namespace ID for
attacker-ns - Constructs a forged
BatchOperationInputwhereNamespaceIdmatchesattacker-nsbutRequest.Namespaceisvictim-ns - Directly starts the internal
temporal-sys-batch-workflow-protobufworkflow inattacker-nswith this forged payload - Waits 15 seconds for the per-namespace batcher worker to execute
- Queries the victim workflow history for
WORKFLOW_EXECUTION_SIGNALEDevents
- On v1.29.4: the victim workflow history contains a signal event, proving the cross-namespace bypass.
- On v1.29.5: the victim workflow history contains zero signal events, proving the fix blocks the bypass.
Run the script:
./repro/reproduction_steps.sh
Evidence
Vulnerable behavior (v1.29.4)
Log location: logs/vuln_repro.log
Key excerpt:
VULNERABLE: victim workflow received 1 signal(s)
The victim workflow in victim-ns received a test-signal that was injected by the batcher worker running in attacker-ns, proving the cross-namespace authorization bypass.
Fixed behavior (v1.29.5)
Log location: logs/fix_repro.log
Key excerpt:
FIXED: victim workflow received 0 signals
exit status 1
The activity returned namespace mismatch before any frontend client call was made, and the victim workflow received zero signals.
Server logs
Server startup and shutdown logs are captured in:
logs/server_v1.29.4.loglogs/server_v1.29.5.log
Runtime manifest
repro/runtime_manifest.json records the test outcomes:
{
"cve": "CVE-2026-5199",
"vulnerable_version": "v1.29.4",
"fixed_version": "v1.29.5",
"vulnerability_confirmed": true,
"fix_verified": true
}
Recommendations / Next Steps
- Upgrade to Temporal Server
v1.29.5or later (orv1.30.3/v1.31.0depending on the release line). - Verify that the
checkNamespaceProtobufvalidation exists in any custom builds or forks. - Testing: Add regression tests that verify both
NamespaceIdandRequest.Namespacemismatches are rejected by the batcher worker. - Defense in depth: Consider adding namespace validation at the internal frontend client boundary as well, so that even if a worker misbehaves, the internal frontend rejects cross-namespace requests.
Additional Notes
- Idempotency: The reproduction script is idempotent. It cleans up server processes by PID, switches git refs, and rebuilds the binary each run. It has been verified to produce consistent results across consecutive executions.
- Edge cases: The reproduction uses the
SIGNALbatch type, but the same bypass applies toCANCEL,TERMINATE,RESET,DELETE,UPDATE_EXECUTION_OPTIONS,UNPAUSE_ACTIVITY,RESET_ACTIVITY, andUPDATE_ACTIVITY_OPTIONSbecause all paths instartTaskProcessorProtobufuse the same attacker-controllednamespaceparameter. - Real server: The reproduction stands up a real Temporal Server process with all services (frontend, history, matching, worker) using SQLite in-memory persistence. The exploit is delivered through the real gRPC frontend endpoint (
StartWorkflowExecution) and the internal per-namespace worker actually executes the batch workflow and activity. The only artifact that differs from production is the use of--allow-no-auth(which disables authorization) so that the test client can create namespaces and start system workflows without needing a full auth stack.
CVE-2026-5199 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Unknown error
Unknown error
mkdir -p external && git clone --depth=100 https://github.com/temporalio/temporal.git external/temporalCloning into 'external/temporal'...
Artifacts and Evidence for CVE-2026-5199
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-5199
Upgrade temporal · go to 1.29.5 or later.
FAQ: CVE-2026-5199
Is this the frontend gRPC signal bypass some early reports described?
Which Temporal versions are affected by CVE-2026-5199, and where is it fixed?
How severe is CVE-2026-5199?
How can I reproduce CVE-2026-5199?
References for CVE-2026-5199
Authoritative sources for CVE-2026-5199 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.