This commit is contained in:
Renich Bon Ciric 2026-08-03 15:15:46 -06:00 committed by GitHub
commit 09ae55a02e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 88 additions and 0 deletions

View file

@ -365,6 +365,18 @@ func (t Transport) buildEnv(r *http.Request) (envVars, error) {
"SCRIPT_NAME": scriptName,
}
if localAddr, ok := r.Context().Value(http.LocalAddrContextKey).(net.Addr); ok {
var ipStr string
if host, _, err := net.SplitHostPort(localAddr.String()); err == nil {
ipStr = host
} else {
ipStr = localAddr.String()
}
if ip := net.ParseIP(ipStr); ip != nil {
env["SERVER_ADDR"] = ipStr
}
}
// compliance with the CGI specification requires that
// PATH_TRANSLATED should only exist if PATH_INFO is defined.
// Info: https://www.ietf.org/rfc/rfc3875 Page 14

View file

@ -1,6 +1,9 @@
package fastcgi
import (
"context"
"net"
"net/http"
"strings"
"testing"
@ -8,6 +11,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
func TestProvisionSplitPath(t *testing.T) {
@ -355,3 +359,75 @@ func TestSplitPosSecurityRegressionUnicodeBypass(t *testing.T) {
assert.Equalf(t, -1, tr.splitPos(p), "payload %q must not be detected as .php", p)
}
}
func TestBuildEnvServerAddr(t *testing.T) {
testCases := []struct {
name string
localAddr net.Addr
envVars map[string]string
expectedAddr string
expectExists bool
}{
{
name: "IPv4 TCP listener",
localAddr: &net.TCPAddr{IP: net.ParseIP("10.0.0.12"), Port: 80},
expectedAddr: "10.0.0.12",
expectExists: true,
},
{
name: "IPv6 TCP listener",
localAddr: &net.TCPAddr{IP: net.ParseIP("2001:db8::1"), Port: 443},
expectedAddr: "2001:db8::1",
expectExists: true,
},
{
name: "Missing local address context",
localAddr: nil,
expectExists: false,
},
{
name: "Unix listener",
localAddr: &net.UnixAddr{Name: "/var/run/caddy.sock", Net: "unix"},
expectExists: false,
},
{
name: "Explicitly configured SERVER_ADDR override",
localAddr: &net.TCPAddr{IP: net.ParseIP("10.0.0.12"), Port: 80},
envVars: map[string]string{"SERVER_ADDR": "192.168.1.100"},
expectedAddr: "192.168.1.100",
expectExists: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
tr := Transport{
Root: "/var/www",
EnvVars: tc.envVars,
}
err := tr.Provision(caddy.Context{})
require.NoError(t, err)
req, err := http.NewRequest(http.MethodGet, "http://localhost/index.php", nil)
require.NoError(t, err)
req = req.WithContext(context.WithValue(req.Context(), caddyhttp.OriginalRequestCtxKey, *req))
repl := caddy.NewReplacer()
req = req.WithContext(context.WithValue(req.Context(), caddy.ReplacerCtxKey, repl))
if tc.localAddr != nil {
req = req.WithContext(context.WithValue(req.Context(), http.LocalAddrContextKey, tc.localAddr))
}
env, err := tr.buildEnv(req)
require.NoError(t, err)
val, exists := env["SERVER_ADDR"]
assert.Equal(t, tc.expectExists, exists)
if tc.expectExists {
assert.Equal(t, tc.expectedAddr, val)
}
})
}
}