CVE Patch Review

GHSA-387M-935M-C4VW: Micronaut HTTP Client Redirect Loop DoS Gets a Root-Cause Fix

GHSA-387M-935M-C4VW · Updated 2026-07-09 Root-cause

Summary

The patch introduces a configurable redirect ceiling in Micronaut's HTTP client, defaulting to 5, and enforces it in the Netty client redirect path. This directly addresses the denial-of-service condition caused by unbounded redirect following. The accompanying tests validate both configuration binding and loop termination behavior, making the change a root-cause remediation rather than a superficial guard.

Analysis

Vulnerability

GHSA-387M-935M-C4VW describes a denial-of-service condition in Micronaut's Netty HTTP client caused by unbounded redirect following. A malicious or misconfigured upstream can emit a redirect loop and keep the client recursively issuing follow-up requests without a terminating condition, leading to thread occupation and resource starvation. The advisory and patch references show that the vulnerable behavior was the absence of a maximum redirect depth in client configuration and redirect execution logic.

Source references: GitHub Security Advisory, commit f1dffff, commit 6e88a97, CVE Reports summary.

public static final int DEFAULT_MAX_REDIRECTS = 5;

private int maxRedirects = DEFAULT_MAX_REDIRECTS;

public int getMaxRedirects() {
    return maxRedirects;
}

public void setMaxRedirects(int maxRedirects) {
    this.maxRedirects = maxRedirects;
}

The added configuration API is significant because it establishes an explicit policy boundary that was previously missing. Without such a boundary, redirect handling can be attacker-controlled for as long as the remote endpoint keeps returning redirect responses.

Patch

The patch adds a new client configuration property, maxRedirects, with a default of 5 in HttpClientConfiguration. It then enforces this limit in the Netty redirect path by tracking a per-request redirect count via a request attribute and aborting once the count exceeds the configured maximum. The implementation returns an HttpClientException with the observed redirect count when the ceiling is crossed.

private static final String REDIRECT_COUNT = "micronaut.http.client.redirect-count";
int redirectCount = request.getAttribute(REDIRECT_COUNT, Integer.class).orElse(0) + 1;
if (redirectCount > configuration.getMaxRedirects()) {
    return ExecutionFlow.error(decorate(new HttpClientException("Maximum number of redirects exceeded at redirect count: " + redirectCount)));
}
redirectRequest.setAttribute(REDIRECT_COUNT, redirectCount);

The test additions are aligned with the fix:

  • Configuration binding coverage verifies micronaut.http.client.max-redirects maps to maxRedirects.
  • RedirectLoopSpec validates that a loop is terminated once the configured depth is exceeded.
  • The TCK redirect test confirms that when redirect following is disabled, the client returns the redirect response rather than entering follow-up behavior.

Relevant patch references: commit f1dffff and commit 6e88a97.

Review

Pros

  • The fix addresses the actual failure mode: redirect processing now has a hard upper bound, eliminating infinite redirect traversal as an unbounded control-flow condition.
  • The default of 5 is conservative and operationally reasonable for most HTTP client workloads.
  • The redirect count is tracked internally as a request attribute rather than trusting server-provided headers, which avoids attacker control over the enforcement state.
  • The error path is explicit and deterministic, improving diagnosability during incident response and application debugging.
  • Tests cover both behavior and configuration plumbing, reducing regression risk.

Cons

  • The setter shown in the patch does not validate negative or zero values. Depending on broader framework semantics, a negative value could produce surprising behavior by failing on the first redirect rather than being rejected at configuration time.
  • The review material shows enforcement in the Netty client path only. If other HTTP client implementations or redirect-capable code paths exist outside this path, they would need equivalent enforcement to guarantee uniform protection.
  • The exception message exposes the redirect count but does not include target URI context, which may limit troubleshooting value in complex redirect chains.

Verdict

Root-cause.

This patch is a root-cause remediation because it introduces the missing invariant that redirect following must be bounded. The vulnerable behavior was not merely a bad edge case in one test scenario; it was the absence of a redirect depth limit in configuration and execution. The patch closes that gap by adding a default maximum, enforcing it at redirect time, and validating the behavior with dedicated tests. Based on the supplied sources, this is not a bandaid and not a partial mitigation.

Primary evidence: GitHub Security Advisory, commit f1dffff, commit 6e88a97.

Sources