91 lines
2.7 KiB
Go
91 lines
2.7 KiB
Go
package httpx
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestWriteSanitizesInternalAndKeepsLegacyCode(t *testing.T) {
|
|
rec := httptest.NewRecorder()
|
|
Write(rec, http.StatusInternalServerError, CodeINTERNAL, "pq: relation foo leaked /home/weichen/secret", "req_test", nil)
|
|
var env Envelope
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &env); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if env.Code != CodeINTERNAL || env.RequestID != "req_test" {
|
|
t.Fatalf("%+v", env)
|
|
}
|
|
if strings.Contains(rec.Body.String(), "pq:") || strings.Contains(rec.Body.String(), "/home/") {
|
|
t.Fatalf("leaked: %s", rec.Body.String())
|
|
}
|
|
if env.Error != CodeINTERNAL {
|
|
t.Fatalf("legacy error=%s", env.Error)
|
|
}
|
|
}
|
|
|
|
func TestWriteKeepsStablePublicErrorCode(t *testing.T) {
|
|
rec := httptest.NewRecorder()
|
|
Write(rec, http.StatusForbidden, CodeAUTHZ, "ERR_FORBIDDEN", "req_2", nil)
|
|
var env Envelope
|
|
_ = json.Unmarshal(rec.Body.Bytes(), &env)
|
|
if env.Error != "ERR_FORBIDDEN" || env.Code != CodeAUTHZ {
|
|
t.Fatalf("%+v", env)
|
|
}
|
|
if strings.Contains(rec.Body.String(), "<script") {
|
|
t.Fatal("xss")
|
|
}
|
|
}
|
|
|
|
func TestLooksInternalXSS(t *testing.T) {
|
|
rec := httptest.NewRecorder()
|
|
Write(rec, http.StatusBadRequest, CodeVALIDATION, "<script>alert(1)</script>", "req_3", nil)
|
|
if strings.Contains(rec.Body.String(), "<script") {
|
|
t.Fatalf("%s", rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestWriteLogsCodeWithoutInternalText(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
prev := slog.Default()
|
|
slog.SetDefault(slog.New(slog.NewJSONHandler(&buf, &slog.HandlerOptions{Level: slog.LevelWarn})))
|
|
t.Cleanup(func() { slog.SetDefault(prev) })
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/login?access_token=leak", nil)
|
|
req.Header.Set(HeaderRequestID, "req_log")
|
|
rec := httptest.NewRecorder()
|
|
WriteHTTP(rec, req, http.StatusInternalServerError, CodeINTERNAL, "pq: password /home/x", "req_log", "请求无法完成", nil)
|
|
line := buf.String()
|
|
if !strings.Contains(line, `"msg":"http_error"`) && !strings.Contains(line, "http_error") {
|
|
t.Fatalf("missing log: %s", line)
|
|
}
|
|
if !strings.Contains(line, "req_log") || !strings.Contains(line, CodeINTERNAL) {
|
|
t.Fatalf("log=%s", line)
|
|
}
|
|
if !strings.Contains(line, `"/api/v1/login"`) {
|
|
t.Fatalf("path=%s", line)
|
|
}
|
|
low := strings.ToLower(line)
|
|
for _, leak := range []string{"password", "pq:", "/home/", "access_token"} {
|
|
if strings.Contains(low, leak) {
|
|
t.Fatalf("leaked %s in %s", leak, line)
|
|
}
|
|
}
|
|
if rec.Header().Get(HeaderErrorCode) != CodeINTERNAL {
|
|
t.Fatalf("header=%s", rec.Header().Get(HeaderErrorCode))
|
|
}
|
|
}
|
|
|
|
func TestCodeFromStatus(t *testing.T) {
|
|
if CodeFromStatus(http.StatusUnauthorized) != CodeAUTHZ {
|
|
t.Fatal("401")
|
|
}
|
|
if CodeFromStatus(http.StatusConflict) != CodeVERSION_CONFLICT {
|
|
t.Fatal("409")
|
|
}
|
|
}
|