Files
gitea/modules/egress/policies_test.go
T
2026-10-06 15:24:11 +08:00

125 lines
4.6 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package egress
import (
"net"
"net/http"
"net/url"
"strconv"
"testing"
"gitea.dev/modules/egress/policy"
"gitea.dev/modules/setting"
"gitea.dev/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func mustURL(t *testing.T, raw string) *url.URL {
t.Helper()
u, err := url.Parse(raw)
require.NoError(t, err)
return u
}
func TestNewMigrationPolicy(t *testing.T) {
defer test.MockVariableValue(&setting.Migrations)()
for _, tc := range []struct {
allow, block, mode, target string
want bool
}{
{target: "https://1.2.3.4", want: true},
{allow: "github.com", target: "https://10.0.0.1"}, // a hostname allow doesn't cover a private IP
{allow: "github.com", target: "https://8.8.8.8", want: true}, // lax exempts public targets
{allow: "github.com", mode: "strict", target: "https://8.8.8.8"},
{block: "8.8.0.0/16", target: "https://8.8.8.8"},
} {
setting.Migrations.AllowedHostList, setting.Migrations.BlockedHostList, setting.Migrations.EgressMode = tc.allow, tc.block, tc.mode
u, err := url.Parse(tc.target)
require.NoError(t, err)
err = NewMigrationPolicy().CheckHostIPs(u)
assert.Equal(t, tc.want, err == nil, "%+v: %v", tc, err)
}
}
func TestWebhookPolicyProxy(t *testing.T) {
proxyURL := &url.URL{Scheme: "http", Host: "localhost:8080"}
defer test.MockVariableValue(&setting.Security.EgressMode, "lax")()
defer test.MockVariableValue(&setting.Webhook.AllowedHostList, "discordapp.com,s.discordapp.com")()
defer test.MockVariableValue(&setting.Webhook.ProxyURL, proxyURL.String())()
defer test.MockVariableValue(&setting.Webhook.ProxyURLFixed, proxyURL)()
defer test.MockVariableValue(&setting.Webhook.ProxyHosts, []string{"*.discordapp.com", "discordapp.com"})()
selectProxy := NewWebhookPolicy().NewHTTPTransport().Proxy
for target, want := range map[string]string{
"https://discordapp.com/api/webhooks/xxxxxxxxx/xxxxxxxxxxxxxxxxxxx": proxyURL.String(),
"http://s.discordapp.com/assets/xxxxxx": proxyURL.String(),
"http://github.com/a/b": "",
"http://www.discordapp.com/assets/xxxxxx": proxyURL.String(),
} {
req, err := http.NewRequest(http.MethodPost, target, nil)
require.NoError(t, err)
req.Host = ""
u, err := selectProxy(req)
require.NoError(t, err, target)
if want == "" {
assert.Nil(t, u, target)
} else {
assert.Equal(t, want, u.String(), target)
}
}
}
func TestWebhookPolicyNeedsIPAllow(t *testing.T) {
defer test.MockVariableValue(&setting.Webhook.AllowedHostList, "localhost")()
defer test.MockVariableValue(&setting.Security.EgressMode, "lax")()
ln, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err)
t.Cleanup(func() { _ = ln.Close() })
dial := func() error {
tcpAddr, ok := ln.Addr().(*net.TCPAddr)
require.True(t, ok)
target := net.JoinHostPort("localhost", strconv.Itoa(tcpAddr.Port))
conn, err := NewWebhookPolicy().NewDialContext()(t.Context(), "tcp", target)
if err == nil {
_ = conn.Close()
}
return err
}
assert.ErrorIs(t, dial(), policy.ErrDenied) // a host name entry doesn't cover the loopback address
setting.Webhook.AllowedHostList = "loopback"
assert.NoError(t, dial()) // an IP entry does
}
func TestSecurityPolicy(t *testing.T) {
defer test.MockVariableValue(&setting.Security.AllowedHostList, "avatars.example.com")()
defer test.MockVariableValue(&setting.Security.EgressMode, "lax")()
lax := NewSecurityPolicy("test")
assert.NoError(t, lax.CheckHost(mustURL(t, "https://avatars.example.com")))
assert.NoError(t, lax.CheckHost(mustURL(t, "https://8.8.8.8"))) // lax exempts public targets
assert.Error(t, lax.CheckHost(mustURL(t, "https://10.0.0.1"))) // restricted targets still need an allow entry
setting.Security.EgressMode = "strict"
strict := NewSecurityPolicy("test")
assert.NoError(t, strict.CheckHost(mustURL(t, "https://avatars.example.com")))
assert.Error(t, strict.CheckHost(mustURL(t, "https://8.8.8.8")))
}
func TestNewGitPolicy(t *testing.T) {
gitConfig := map[string]string{"http.proxy": "proxy.corp"}
defer test.MockVariableValue(&setting.GitConfig.Options, gitConfig)()
gitPolicy, err := NewGitPolicy()
require.NoError(t, err)
selected, err := gitPolicy.Proxy(&http.Request{URL: &url.URL{Scheme: "https", Host: "git.example.com"}})
require.NoError(t, err)
assert.Equal(t, "http://proxy.corp:1080", selected.String())
gitConfig["http.proxy"] = "http://user:secret@[::1"
_, err = NewGitPolicy()
require.Error(t, err)
assert.NotContains(t, err.Error(), "secret")
}