146 lines
4.9 KiB
Go
146 lines
4.9 KiB
Go
package prodoctorov
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestConfig(t *testing.T) {
|
|
c, err := New("", "")
|
|
if err != nil || c.Configured() {
|
|
t.Fatal("empty configuration must disable the connector")
|
|
}
|
|
for _, args := range [][2]string{{"", "82347"}, {"session=test", ""}, {"session=x\r\nInjected: bad", "82347"}, {"session=test", "-1"}, {"session=test", "82347&lpu=other"}, {"Cookie", "82347"}} {
|
|
if _, err := New(args[0], args[1]); err == nil {
|
|
t.Fatal("malformed configuration accepted")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDownloadQueryAndCookie(t *testing.T) {
|
|
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/api/cabinet/lpu/appointments/direct/download/" || r.Method != "GET" ||
|
|
r.URL.Query().Get("month") != "10" || r.URL.Query().Get("year") != "2026" || r.URL.Query().Get("lpu") != "82347" ||
|
|
r.Header.Get("Cookie") != "session=synthetic" {
|
|
t.Error("wrong export request")
|
|
}
|
|
_, _ = w.Write([]byte{'P', 'K', 3, 4, 'x'})
|
|
}))
|
|
defer s.Close()
|
|
c, _ := New("session=synthetic", "82347")
|
|
c.endpoint = s.URL + "/api/cabinet/lpu/appointments/direct/download/"
|
|
data, err := c.Download(context.Background(), 2026, time.October)
|
|
if err != nil || !bytes.HasPrefix(data, []byte("PK")) {
|
|
t.Fatal("valid export failed", err)
|
|
}
|
|
}
|
|
|
|
func TestDownloadErrorsDoNotLeakResponse(t *testing.T) {
|
|
for _, status := range []int{200, 401, 403, 429, 500} {
|
|
t.Run(http.StatusText(status), func(t *testing.T) {
|
|
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(status)
|
|
_, _ = io.WriteString(w, "<html>patient-secret session=synthetic</html>")
|
|
}))
|
|
defer s.Close()
|
|
c, _ := New("session=synthetic", "82347")
|
|
c.endpoint = s.URL
|
|
_, err := c.Download(context.Background(), 2026, time.October)
|
|
if err == nil || strings.Contains(err.Error(), "patient-secret") || strings.Contains(err.Error(), "synthetic") {
|
|
t.Fatal("missing or leaking error")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDownloadProtectionIsNotExpiredSession(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
status int
|
|
body string
|
|
want string
|
|
}{
|
|
{"forbidden", http.StatusForbidden, "<html>private challenge</html>", "HTTP 403, ответ через Servicepipe"},
|
|
{"challenge page", http.StatusOK, "<html>private challenge</html>", "другая страница через Servicepipe"},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("X-SP-CRID", "private-request-id")
|
|
w.WriteHeader(tc.status)
|
|
_, _ = io.WriteString(w, tc.body)
|
|
}))
|
|
defer s.Close()
|
|
c, _ := New("session=synthetic", "82347")
|
|
c.endpoint = s.URL
|
|
_, err := c.Download(context.Background(), 2026, time.October)
|
|
if err == nil || !strings.Contains(err.Error(), tc.want) ||
|
|
strings.Contains(err.Error(), "private") || strings.Contains(err.Error(), "synthetic") {
|
|
t.Fatal("protection was misreported or secret data leaked")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDownloadProtectionHeaderDoesNotRejectXLSX(t *testing.T) {
|
|
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("X-SP-CRID", "synthetic-request-id")
|
|
_, _ = w.Write([]byte{'P', 'K', 3, 4, 'x'})
|
|
}))
|
|
defer s.Close()
|
|
c, _ := New("session=synthetic", "82347")
|
|
c.endpoint = s.URL
|
|
if _, err := c.Download(context.Background(), 2026, time.October); err != nil {
|
|
t.Fatal("valid export rejected solely because of proxy header", err)
|
|
}
|
|
}
|
|
|
|
func TestRedirectDoesNotForwardSession(t *testing.T) {
|
|
for _, sameHost := range []bool{false, true} {
|
|
calls := 0
|
|
target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { calls++ }))
|
|
var source *httptest.Server
|
|
source = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path == "/login" {
|
|
calls++
|
|
return
|
|
}
|
|
location := target.URL
|
|
if sameHost {
|
|
location = source.URL + "/login"
|
|
}
|
|
http.Redirect(w, r, location, http.StatusFound)
|
|
}))
|
|
c, _ := New("session=synthetic", "82347")
|
|
c.endpoint = source.URL
|
|
_, err := c.Download(context.Background(), 2026, time.October)
|
|
source.Close()
|
|
target.Close()
|
|
if err == nil || calls != 0 {
|
|
t.Fatal("redirect followed or accepted")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDownloadBoundAndCancellation(t *testing.T) {
|
|
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
_, _ = w.Write(append([]byte{'P', 'K', 3, 4}, bytes.Repeat([]byte{'x'}, maxDownload)...))
|
|
}))
|
|
defer s.Close()
|
|
c, _ := New("session=synthetic", "82347")
|
|
c.endpoint = s.URL
|
|
if _, err := c.Download(context.Background(), 2026, time.October); err == nil {
|
|
t.Fatal("oversized export accepted")
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
if _, err := c.Download(ctx, 2026, time.October); err != context.Canceled {
|
|
t.Fatal("cancellation lost", err)
|
|
}
|
|
}
|