增加非白名单的日志
This commit is contained in:
1 parent
7f4adad7b1
commit
06bb066d82
7 files changed
+332
-28
No files matched your search
@@ -7,6 +7,7 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -123,6 +124,9 @@ func TestDashScopeChatStreamsCompatibleResultAfterStrictSuccess(t *testing.T) {
|
||||
t.Fatalf("logs contain sensitive value %q: %s", sensitive, logs.String())
|
||||
}
|
||||
}
|
||||
if strings.Contains(logs.String(), "https://allowed.example") {
|
||||
t.Fatalf("successful request log contains origin: %s", logs.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDashScopeChatMapsSessionIDToLocalConversation(t *testing.T) {
|
||||
@@ -217,6 +221,96 @@ func TestDashScopeChatCORSPreflight(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDashScopeChatForbiddenOriginLogIsActionableAndSafe(t *testing.T) {
|
||||
var logs bytes.Buffer
|
||||
handler := newTestDashScopeChatHandler(t, &fakeChatUseCase{}, []string{"https://allowed.example"}, log.New(&logs, "", 0))
|
||||
request := authenticatedDashScopeRequest(http.MethodPost, `{"input":{"prompt":"secret prompt must not be logged"}}`)
|
||||
request.Header.Set("Origin", "https://evil.example/\nforged="+strings.Repeat("a", 400))
|
||||
request.Header.Set("Authorization", "Bearer provider-secret")
|
||||
request.Header.Set("Cookie", "session=secret-cookie")
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(response, request)
|
||||
|
||||
logged := logs.String()
|
||||
if response.Code != http.StatusForbidden {
|
||||
t.Fatalf("status=%d body=%s", response.Code, response.Body.String())
|
||||
}
|
||||
if !strings.Contains(logged, `result=forbidden_origin`) ||
|
||||
!strings.Contains(logged, `origin="https://evil.example/\nforged=`) ||
|
||||
!strings.Contains(logged, `[truncated]"`) {
|
||||
t.Fatalf("forbidden origin log is not actionable and bounded: %q", logged)
|
||||
}
|
||||
if strings.Count(logged, "\n") != 1 {
|
||||
t.Fatalf("untrusted origin injected a log line: %q", logged)
|
||||
}
|
||||
for _, sensitive := range []string{testChatToken, "provider-secret", "secret-cookie", "secret prompt"} {
|
||||
if strings.Contains(logged, sensitive) {
|
||||
t.Fatalf("forbidden origin log contains sensitive value %q: %q", sensitive, logged)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDashScopeChatForbiddenPreflightLogIdentifiesCause(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
origin string
|
||||
method string
|
||||
headers string
|
||||
wantReason string
|
||||
}{
|
||||
{name: "missing origin", method: http.MethodPost, headers: "content-type", wantReason: "origin_missing"},
|
||||
{name: "forbidden origin", origin: "https://evil.example", method: http.MethodPost, headers: "content-type", wantReason: "origin_not_allowed"},
|
||||
{name: "wrong method", origin: "https://allowed.example", method: http.MethodDelete, headers: "content-type", wantReason: "method_not_allowed"},
|
||||
{name: "forbidden headers", origin: "https://allowed.example", method: http.MethodPost, headers: "authorization\nforged=" + strings.Repeat("b", 400), wantReason: "headers_not_allowed"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var logs bytes.Buffer
|
||||
handler := newTestDashScopeChatHandler(t, &fakeChatUseCase{}, []string{"https://allowed.example"}, log.New(&logs, "", 0))
|
||||
request := authenticatedDashScopeRequest(http.MethodOptions, "")
|
||||
if tt.origin != "" {
|
||||
request.Header.Set("Origin", tt.origin)
|
||||
}
|
||||
request.Header.Set("Access-Control-Request-Method", tt.method)
|
||||
request.Header.Set("Access-Control-Request-Headers", tt.headers)
|
||||
request.Header.Set("Authorization", "Bearer provider-secret")
|
||||
request.Header.Set("Cookie", "session=secret-cookie")
|
||||
response := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(response, request)
|
||||
|
||||
logged := logs.String()
|
||||
if response.Code != http.StatusForbidden {
|
||||
t.Fatalf("status=%d body=%s", response.Code, response.Body.String())
|
||||
}
|
||||
for _, want := range []string{
|
||||
`result=preflight_forbidden`,
|
||||
`origin=` + strconv.Quote(tt.origin),
|
||||
`preflight_method=` + strconv.Quote(tt.method),
|
||||
`reason="` + tt.wantReason + `"`,
|
||||
} {
|
||||
if !strings.Contains(logged, want) {
|
||||
t.Fatalf("preflight log missing %q: %q", want, logged)
|
||||
}
|
||||
}
|
||||
if tt.wantReason == "headers_not_allowed" {
|
||||
if !strings.Contains(logged, `preflight_headers="authorization\nforged=`) || !strings.Contains(logged, `[truncated]"`) {
|
||||
t.Fatalf("preflight headers are not safely bounded: %q", logged)
|
||||
}
|
||||
}
|
||||
if strings.Count(logged, "\n") != 1 {
|
||||
t.Fatalf("untrusted preflight header injected a log line: %q", logged)
|
||||
}
|
||||
for _, sensitive := range []string{testChatToken, "provider-secret", "secret-cookie"} {
|
||||
if strings.Contains(logged, sensitive) {
|
||||
t.Fatalf("preflight log contains sensitive value %q: %q", sensitive, logged)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDashScopeChatRejectsNonCompatibleJSON(t *testing.T) {
|
||||
tests := []string{
|
||||
``,
|
||||
|
||||
Reference in new issue
Block a user