Skip to content

Update module github.com/go-chi/chi/v5 to v5.4.0 [SECURITY] (release-v0.7) - #1936

Merged
robnester-rh merged 1 commit into
release-v0.7from
renovate/release-v0.7-go-github.com-go-chi-chi-v5-vulnerability
Oct 27, 2026
Merged

robnester-rh merged 1 commit into
release-v0.7from
renovate/release-v0.7-go-github.com-go-chi-chi-v5-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Adoption Passing Confidence
github.com/go-chi/chi/v5 v5.2.2 → v5.4.0 age adoption passing confidence

chi: Client IP spoofing in ClientIPFromXFF via X-Forwarded-For

CVE-2026-114437 / GHSA-j6qf-3m9c-vwj3

More information

Details

Summary

ClientIPFromXFF skips any X-Forwarded-For entry in a configured trusted CIDR but never verifies that the direct TCP peer in http.Request.RemoteAddr belongs to that CIDR. A client that can reach the Go server directly can therefore append a trusted-looking proxy address and choose the client IP returned by middleware.GetClientIP.

The attack is remote and requires one unauthenticated HTTP request with no user interaction (AV:N/AC:L/PR:N/UI:N). It changes security decisions and exposes one protected handler within the same application (S:U/C:L/I:L); no availability loss was observed (A:N).

Preconditions: The application uses ClientIPFromXFF and makes the origin server reachable by an anonymous client outside the configured trusted-proxy CIDRs. No victim interaction is required.

  1. Send GET /admin directly to the origin with no X-Forwarded-For header.
  2. Observe a 403 Forbidden response and an empty derived client IP.
  3. Repeat the request with X-Forwarded-For: 10.0.0.7, 198.51.100.42.
  4. Observe 200 OK and client-ip="10.0.0.7". Expected: the untrusted direct peer prevents the forwarding chain from being accepted.
Details

The vulnerable code is ClientIPFromXFF in middleware/client_ip.go, lines 93-116 at the tested revision. It calls walkXFF with only the attacker-controlled header, skips every entry that matches a trusted prefix, and stores the first remaining address:

walkXFF(r.Header[xForwardedForHeader], func(v string) bool {
	ip, ok := parseHeaderAddr(v)
	if !ok {
		return true
	}
	if inAnyPrefix(ip, prefixes) {
		return false
	}
	found = ip
	return true
})

The function does not parse r.RemoteAddr before this walk. Consequently, the configured CIDRs identify trusted strings inside X-Forwarded-For, but do not establish that the immediate sender is one of those trusted proxies.

Proof of concept

Using Go 1.24 or later, create and run this minimal application against the tested revision:

mkdir chi-clientip-poc
cd chi-clientip-poc
go mod init clientip-poc
go get github.com/go-chi/chi/v5@167e1e3bd039d060696b99c8da4e876ae04f42c1
cat > main.go <<'EOF'
package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
)

const allowedIP = "10.0.0.7"

func main() {
	r := chi.NewRouter()
	r.Use(middleware.ClientIPFromXFF("198.51.100.0/24"))
	r.Get("/admin", func(w http.ResponseWriter, r *http.Request) {
		clientIP := middleware.GetClientIP(r.Context())
		if clientIP != allowedIP {
			http.Error(w, fmt.Sprintf("denied: tcp-peer=%q client-ip=%q", r.RemoteAddr, clientIP), http.StatusForbidden)
			return
		}
		fmt.Fprintf(w, "allowed: tcp-peer=%q client-ip=%q\n", r.RemoteAddr, clientIP)
	})

	log.Print("listening on http://127.0.0.1:3001")
	log.Fatal(http.ListenAndServe("127.0.0.1:3001", r))
}
EOF
go run .

The no-header control was rejected:

$ curl --http1.1 -i http://127.0.0.1:3001/admin
HTTP/1.1 403 Forbidden
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Tue, 06 Oct 2026 15:46:27 GMT
Content-Length: 48

denied: tcp-peer="127.0.0.1:54416" client-ip=""

Adding the forged chain authorized the same direct client:

$ curl --http1.1 -i -H 'X-Forwarded-For: 10.0.0.7, 198.51.100.42' http://127.0.0.1:3001/admin
HTTP/1.1 200 OK
Date: Tue, 06 Oct 2026 15:46:33 GMT
Content-Length: 57
Content-Type: text/plain; charset=utf-8

allowed: tcp-peer="127.0.0.1:54417" client-ip="10.0.0.7"
Impact

I made an anonymous direct connection from 127.0.0.1, supplied a header that ended in a different trusted-proxy CIDR, and caused the application to treat my client IP as the allowlisted 10.0.0.7. This changed the authorization decision from 403 Forbidden to 200 OK for the protected handler.

Severity

  • CVSS Score: 4.8 / 10 (Medium)
  • Vector String: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


Release Notes

go-chi/chi (github.com/go-chi/chi/v5)

v5.4.0

Compare Source

What's Changed

New Contributors

Full Changelog: go-chi/chi@v5.3.2...v5.4.0

v5.3.2

Compare Source

What's Changed

  • feat(middleware): add text/markdown, text/csv, text/vtt to default compressible types by @​VojtechVitek in #​1151
  • docs: deployment recipe for middleware.ClientIPFromXFFTrustedProxies() by @​VojtechVitek in #​1111
  • fix: don't drop handlers that collide with a Mount()/Route() pattern by @​VojtechVitek in #​1148
  • Don't duplicate methods in Allow: header for 405 responses by @​flimzy in #​1029
  • fix(middleware): reject catch-all compress wildcards by @​VojtechVitek in #​1156
    • middleware.NewCompressor(level, "/*") never worked and silently compressed nothing. Instead of turning it into a compress-everything catch-all (as proposed in #​868 and #​1121), we decided to reject both "/" and "/*" at construction and panic. Compressing every response wastes CPU on already-compressed types (zip, jpeg, png), which is why the middleware keeps a curated default list. Users should pass explicit content types.

Full Changelog: go-chi/chi@v5.3.1...v5.3.2

v5.3.1

Compare Source

What's Changed

New Contributors

Full Changelog: go-chi/chi@v5.3.0...v5.3.1

v5.3.0

Compare Source

What's Changed

New Contributors

SECURITY: middleware.ClientIP, a replacement for middleware.RealIP

@​VojtechVitek submitted PR #​967, which introduces middleware.ClientIP — a replacement for middleware.RealIP that closes the three open spoofing advisories:

It also addresses issues outlined at:

middleware.RealIP is deprecated in this PR with pointers to the new API.

The deprecation only adds a // Deprecated: doc comment; the function keeps working for backward compatibility.

Why a new middleware (not "fix RealIP in place")

RealIP has two unfixable design choices: it mutates r.RemoteAddr, and it tries to be a one-size-fits-all default by walking a hard-coded list of headers any client can supply. Per adam-p's "The perils of the 'real' client IP" (which calls chi out by name on this), there is no safe default — the user must pick their trust source explicitly.

The new API

Four middlewares, two accessors. Pick exactly one middleware based on your
infrastructure, read the result with one of the two accessors:

// One of the four. There is no safe default — pick exactly one.
func ClientIPFromHeader(trustedHeader string) func(http.Handler) http.Handler
func ClientIPFromXFF(trustedIPPrefixes ...string) func(http.Handler) http.Handler
func ClientIPFromXFFTrustedProxies(numTrustedProxies int) func(http.Handler) http.Handler
func ClientIPFromRemoteAddr(h http.Handler) http.Handler

// Read the result.
func GetClientIP(ctx context.Context) string         // for logs, rate-limit keys
func GetClientIPAddr(ctx context.Context) netip.Addr // for typed work

Example usage:

// Pick a single ClientIP middleware based on your deployment
  
// Cloudflare.
r.Use(middleware.ClientIPFromHeader("CF-Connecting-IP"))

// Nginx with ngx_http_realip_module.
r.Use(middleware.ClientIPFromHeader("X-Real-IP"))

// Apache with mod_remoteip.
r.Use(middleware.ClientIPFromHeader("X-Client-IP"))

// AWS CloudFront, or any proxy fleet with known CIDRs.
r.Use(middleware.ClientIPFromXFF(
    "13.32.0.0/15",   // CloudFront IPv4
    "52.46.0.0/18",   // CloudFront IPv4
    "2600:9000::/28", // CloudFront IPv6
))

// Behind exactly 2 trusted proxies with dynamic IPs (autoscaling pools,
// ephemeral containers, dynamic CDN edges).
r.Use(middleware.ClientIPFromXFFTrustedProxies(2))

// Server directly on the public internet, no proxy in front.
r.Use(middleware.ClientIPFromRemoteAddr)

And in your handler or downstream middleware:

clientIP := middleware.GetClientIP(r.Context())
// log it, use it as a rate-limit key, etc.

Thanks to @​adam-p, @​c2h5oh, @​rezmoss, @​Saku0512, @​convto, @​Dirbaio, @​jawnsy, @​lrstanley, @​mfridman, @​n33pm, @​pkieltyka for the prior discussions, detailed reviews, advisory reports, and test contributions that shaped this PR.

Full Changelog: go-chi/chi@v5.2.5...v5.3.0

v5.2.5

Compare Source

What's Changed

New Contributors

Full Changelog: go-chi/chi@v5.2.3...v5.2.5

v5.2.4

Compare Source

What's Changed

New Contributors

Full Changelog: go-chi/chi@v5.2.3...v5.2.4

v5.2.3

Compare Source

What's Changed

New Contributors

Full Changelog: go-chi/chi@v5.2.2...v5.2.3


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Enterprise
  • Run ID: e1346116-f4dc-4a00-aa6e-1052821d0ac9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@fullsend-ai-review

fullsend-ai-review Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:02 AM UTC · Completed 9:11 AM UTC

Commit: 94e6035 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $2.30

@fullsend-ai-review fullsend-ai-review Bot added the risk/moderate PR risk: moderate label Oct 25, 2026
@fullsend-ai-review

fullsend-ai-review Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Routine bot-driven security dependency update touching only go.mod and go.sum (6 lines); the elevated dependency-file signal remains the sole driver above minimum, while small size, bot authorship, and no protected or security-sensitive paths keep the composite at moderate — unchanged from prior assessment.

Previous run

<!-- fullsend:risk-assessment -->
Risk Assessment: moderate (2/5)

Details

Routine bot-driven security dependency update touching only go.mod and go.sum (6 lines); the elevated dependency-file signal is the sole driver above minimum, but small size, bot authorship, and no protected or security-sensitive paths keep the composite well within moderate territory.

@fullsend-ai-review

fullsend-ai-review Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Looks good to me

Previous run

<!-- fullsend:review-findings-v2:eyJ2ZXJzaW9uIjoyLCJhY3Rpb24iOiJhcHByb3ZlIiwiZmluZGluZ3MiOltdfQ== -->

Looks good to me


Labels: Dependency update PR modifying Go module files

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge dependencies Pull requests that update a dependency file go Pull requests that update Go code labels Oct 25, 2026
@renovate
renovate Bot force-pushed the renovate/release-v0.7-go-github.com-go-chi-chi-v5-vulnerability branch from 94e6035 to 6b04672 Compare October 26, 2026 22:21
@fullsend-ai-review

fullsend-ai-review Bot commented Oct 26, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 10:23 PM UTC · Completed 10:33 PM UTC

Commit: 6b04672 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $2.77

@fullsend-ai-review fullsend-ai-review Bot removed the ready-for-merge All reviewers approved — ready to merge label Oct 26, 2026
@fullsend-ai-review
fullsend-ai-review Bot dismissed their stale review October 26, 2026 22:33

Superseded by updated review

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Oct 26, 2026
@robnester-rh
robnester-rh enabled auto-merge (squash) October 27, 2026 15:32

@robnester-rh robnester-rh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@robnester-rh
robnester-rh merged commit 25e546d into release-v0.7 Oct 27, 2026
34 checks passed
@fullsend-ai-retro

fullsend-ai-retro Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 3:39 PM UTC · Completed 3:50 PM UTC

Commit: 6b04672 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $4.78

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #1936 — Renovate go-chi/chi v5.4.0 security bump (release-v0.7)

This PR was a Renovate bot security dependency update bumping go-chi/chi/v5 from v5.2.2 to v5.4.0 (CVE-2026-114437), touching 6 lines in go.mod and go.sum. The workflow executed correctly — the review agent approved with no findings both times, the risk assessment (moderate, 2/5) was appropriate, and the PR merged after two human approvals.

Cost concern: $5.07 in review for a 6-line dependency bump

The review agent ran twice on this PR:

  1. Oct 25, 09:02–09:11 UTC (run 40178848664) — triggered by PR open. Opus at high effort, 4 sub-agents (correctness, style-conventions, intent-coherence, risk-assessment), ~12 min, $2.30. All sub-agents early-exited with zero findings.
  2. Oct 26, 22:23–22:33 UTC (run 40411805044) — triggered by Renovate force-push/rebase (synchronize event). Opus at high effort, 4 sub-agents (correctness, style-conventions, security, risk-assessment), ~12 min, $2.77. All sub-agents early-exited with zero findings again.

The second review was unnecessary — the diff-to-base was identical after the rebase, and the prior review had approved with zero findings. The agent recognized the re-review scenario but still launched the full orchestration pipeline.

Additionally, the same dependency bump was independently reviewed on sibling PRs targeting other branches: #1935 (main) and #1937 (release-v0.8).

No new proposals — existing issues cover all findings

Re-review on rebase: This retro adds supporting evidence for fullsend-ai/fullsend#8830 and fullsend-ai/fullsend#4401 — both propose skipping re-review dispatch when a force-push/rebase produces an identical diff. This PR confirms the pattern persists, with a $2.77 wasted re-review on an unchanged diff.

Lighter review for bot dependency PRs: This retro adds evidence for fullsend-ai/fullsend#8383 (route trivial bot dependency-bump PRs to lighter review config) and fullsend-ai/agents#257 (pre-review early exit for bot dependency-update PRs). Using opus at high effort for a 6-line go.mod/go.sum change is disproportionate — every sub-agent immediately early-exited.

Cross-branch duplication: fullsend-ai/fullsend#4568 (cross-reference sibling PRs) is the nearest tracked issue. Three identical dependency bumps were independently reviewed across main, release-v0.7, and release-v0.8.

Retro on bot PRs: This retro itself is a data point for fullsend-ai/agents#276 (skip retro on zero-involvement bot PRs). The workflow went well; the retro confirms known waste patterns without surfacing new issues.

What went well

  • The review agent correctly identified this as a routine dependency update and approved without false findings.
  • Risk assessment (moderate, 2/5) was appropriate for dependency file changes.
  • The agent properly dismissed its stale review after the force-push and posted a fresh approval.
  • Agents repo: fullsend-ai/agents@main.

@renovate
renovate Bot deleted the renovate/release-v0.7-go-github.com-go-chi-chi-v5-vulnerability branch October 27, 2026 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file go Pull requests that update Go code ready-for-merge All reviewers approved — ready to merge release-v0.7 renovate risk/moderate PR risk: moderate size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants