giteabot backport / giteabot (push) Canceled after 0s
giteabot / giteabot (push) Canceled after 0s
release-nightly / nightly-binary (push) Canceled after 0s
release-nightly / nightly-container (push) Canceled after 0s
cache-seeder / gobuild (push) Canceled after 0s
cache-seeder / lint (bindata, lint-backend) (push) Canceled after 0s
release-nightly-snapcraft / build-and-publish (push) Canceled after 0s
Includes direct password setup links in registration emails. Assisted-by: Codex:GPT-5
70 lines
2.1 KiB
Go
70 lines
2.1 KiB
Go
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package uri
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestReadURI(t *testing.T) {
|
|
p, err := filepath.Abs("./uri.go")
|
|
assert.NoError(t, err)
|
|
f, err := Open("file://" + p)
|
|
assert.NoError(t, err)
|
|
defer f.Close()
|
|
}
|
|
|
|
// TestOpenWithClientValidatesRedirectTarget verifies OpenWithClient routes the
|
|
// whole request chain (including redirects) through the provided client, so a
|
|
// client whose transport refuses to dial an internal target blocks a redirect to
|
|
// it — whereas the default client (old Open behavior) follows it.
|
|
func TestOpenWithClientValidatesRedirectTarget(t *testing.T) {
|
|
var internalHit atomic.Bool
|
|
internal := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
internalHit.Store(true)
|
|
_, _ = w.Write([]byte("secret"))
|
|
}))
|
|
defer internal.Close()
|
|
|
|
front := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
http.Redirect(w, r, internal.URL, http.StatusFound)
|
|
}))
|
|
defer front.Close()
|
|
|
|
internalAddr := strings.TrimPrefix(internal.URL, "http://")
|
|
|
|
// a client that refuses to dial the internal target, mimicking the migration
|
|
// hostmatcher dialer that re-validates every hop
|
|
blockingClient := &http.Client{Transport: &http.Transport{
|
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
if addr == internalAddr {
|
|
return nil, errors.New("blocked internal address")
|
|
}
|
|
return (&net.Dialer{}).DialContext(ctx, network, addr)
|
|
},
|
|
}}
|
|
|
|
_, err := OpenWithClient(front.URL, blockingClient)
|
|
require.Error(t, err)
|
|
assert.False(t, internalHit.Load(), "the redirect target must not be reached through the validating client")
|
|
|
|
// the default client (the previous behavior) follows the redirect to the internal target
|
|
internalHit.Store(false)
|
|
rc, err := Open(front.URL)
|
|
require.NoError(t, err)
|
|
_ = rc.Close()
|
|
assert.True(t, internalHit.Load(), "sanity check: the default client follows the redirect")
|
|
}
|