Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions v3/UNRELEASED_CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ After processing, the content will be moved to the main changelog and this file

## Fixed
<!-- Bug fixes -->
- Give a webview request a loopback `RemoteAddr` (`127.0.0.1:0`) instead of the `192.0.2.1:1234` (RFC 5737 TEST-NET) placeholder. A webview request originates on the local machine, so a user `Handler` that gates on request origin now sees it as loopback rather than as a remote peer.

## Deprecated
<!-- Soon-to-be removed features -->
Expand Down
10 changes: 8 additions & 2 deletions v3/internal/assetserver/assetserver_webview.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,14 @@ func (a *AssetServer) processWebViewRequestInternal(r webview.Request) {
req.Header = header

if req.RemoteAddr == "" {
// 192.0.2.0/24 is "TEST-NET" in RFC 5737
req.RemoteAddr = "192.0.2.1:1234"
// A webview request originates from the embedded webview on this
// machine, not from a routable peer, so give it a loopback RemoteAddr:
// a handler that gates on request origin (net.ParseIP(host).IsLoopback())
// then sees it as the local request it is. The previous placeholder
// (192.0.2.1, RFC 5737 TEST-NET) is non-loopback — the same address the
// host checks elsewhere in this tree treat as the canonical remote peer
// — so it made the most-local surface look remote.
req.RemoteAddr = "127.0.0.1:0"
}

if req.RequestURI == "" && req.URL != nil {
Expand Down
34 changes: 34 additions & 0 deletions v3/internal/assetserver/assetserver_webview_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"context"
"io"
"log/slog"
"net"
"net/http"
"sync/atomic"
"testing"
Expand All @@ -13,6 +14,39 @@ import (
"github.com/wailsapp/wails/v3/internal/assetserver/webview"
)

// A webview request originates from the embedded webview on this machine, so a
// handler that gates on request origin must see a loopback peer. The synthesized
// RemoteAddr must therefore parse as a loopback IP.
func TestWebViewRequestPeerIsLoopback(t *testing.T) {
var peer atomic.Value // string

srv, err := NewAssetServer(&Options{
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
peer.Store(r.RemoteAddr)
w.WriteHeader(http.StatusOK)
}),
Logger: slog.Default(),
})
if err != nil {
t.Fatalf("NewAssetServer failed: %v", err)
}

req := &contextWebViewRequest{ctx: context.Background(), rw: &contextWebViewResponse{}}
srv.processWebViewRequest(req)

got, _ := peer.Load().(string)
if got == "" {
t.Fatal("handler was not reached")
}
host, _, err := net.SplitHostPort(got)
if err != nil {
t.Fatalf("RemoteAddr %q is not host:port: %v", got, err)
}
if ip := net.ParseIP(host); ip == nil || !ip.IsLoopback() {
t.Fatalf("webview request RemoteAddr = %q, want a loopback peer", got)
}
}

func TestWebViewRequestCancellationReachesHandler(t *testing.T) {
started := make(chan struct{})
contextErr := make(chan error, 1)
Expand Down
Loading