Files
knowledge-governance/internal/httpapi/qa_proxy_authority_test.go
姚章浩 2ea2111420
build / build (push) Successful in 1m1s
fix(web): 保留代理端口以修复同源质量查询误拒绝
Nginx 使用原始 Host 和端口转发,不修改后端跨站防护。补充无 Fetch Metadata 的 LAN/Tailscale 同源和异源、异端口、转发头伪造回归。

验证:配置回归先因丢失端口失败后通过;HTTP竞态、真实隔离Nginx正反例及完整make verify均通过。部署前真实入口无Metadata为403,同源Metadata对照可过;源库及人工复核无写入。
2026-10-18 15:45:09 +08:00

63 lines
2.6 KiB
Go

package httpapi
import (
"net/http/httptest"
"os"
"regexp"
"testing"
)
// HTTP LAN/tailnet origins may omit Fetch Metadata. nginx must preserve the
// external authority including its port so Origin can be checked without it.
func TestQAProxyPreservesBrowserAuthority(t *testing.T) {
config, err := os.ReadFile("../../deploy/nginx.conf")
if err != nil {
t.Fatal(err)
}
hosts := regexp.MustCompile(`(?m)^\s*proxy_set_header\s+Host\s+([^;]+);`).FindAllStringSubmatch(string(config), -1)
if len(hosts) < 2 {
t.Fatal("API and health proxy Host directives must both be explicit")
}
for _, host := range hosts {
if host[1] != "$http_host" {
t.Fatalf("proxy drops browser authority/port: got %q, want $http_host", host[1])
}
}
}
func TestQAIntentWithoutFetchMetadataChecksExactAuthority(t *testing.T) {
for _, tc := range []struct {
name, host, origin, forwarded, site, intent string
allowed bool
}{
{"LAN same authority", "192.168.50.134:8088", "http://192.168.50.134:8088", "", "", "qa-quality", true},
{"tailnet same authority", "100.92.49.78:8088", "http://100.92.49.78:8088", "", "", "qa-quality", true},
{"proxy stripped port", "192.168.50.134", "http://192.168.50.134:8088", "", "", "qa-quality", false},
{"foreign origin", "192.168.50.134:8088", "http://foreign.invalid:8088", "", "", "qa-quality", false},
{"different port", "192.168.50.134:8088", "http://192.168.50.134:8089", "", "", "qa-quality", false},
{"forwarded header cannot grant", "192.168.50.134:8088", "http://foreign.invalid:8088", "foreign.invalid:8088", "", "qa-quality", false},
{"opaque origin", "192.168.50.134:8088", "null", "", "", "qa-quality", false},
{"explicit cross site", "192.168.50.134:8088", "http://192.168.50.134:8088", "", "cross-site", "qa-quality", false},
{"missing intent", "192.168.50.134:8088", "http://192.168.50.134:8088", "", "", "", false},
} {
t.Run(tc.name, func(t *testing.T) {
req := httptest.NewRequest("POST", "http://"+tc.host+"/api/v1/qa-ops/quality/query", nil)
req.Header.Set("Origin", tc.origin)
req.Header.Set("X-Governance-Action", tc.intent)
if tc.forwarded != "" {
req.Header.Set("X-Forwarded-Host", tc.forwarded)
}
if tc.site != "" {
req.Header.Set("Sec-Fetch-Site", tc.site)
}
rec := httptest.NewRecorder()
if got := authorizeQAIntent(rec, req, "qa-quality"); got != tc.allowed {
t.Fatalf("authorized=%v want %v, HTTP=%d", got, tc.allowed, rec.Code)
}
if !tc.allowed && rec.Code != 403 {
t.Fatalf("rejected authority should be 403, got %d", rec.Code)
}
})
}
}