cors fixed
This commit is contained in:
113
main_test.go
Normal file
113
main_test.go
Normal file
@@ -0,0 +1,113 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
)
|
||||
|
||||
// Cross-origin access, checked the way a browser checks it.
|
||||
//
|
||||
// These exist because this went wrong in production and nothing caught it.
|
||||
// Adding the session token to the console made every request non-simple, so
|
||||
// browsers began asking permission first — and the answer did not name the
|
||||
// `Authorization` header, so every call was blocked.
|
||||
//
|
||||
// The reason it reached production is worth keeping in mind while reading
|
||||
// these: the preflight returns 204 and looks perfectly healthy from a terminal,
|
||||
// the server logs nothing unusual, and `curl` cannot reproduce it because curl
|
||||
// does not enforce CORS. The only thing that noticed was a browser.
|
||||
|
||||
// preflight asks the question a browser asks before a cross-origin request.
|
||||
func preflight(t *testing.T, requestHeaders string) map[string]string {
|
||||
t.Helper()
|
||||
|
||||
app := fiber.New()
|
||||
app.Use(cors.New(corsSettings()))
|
||||
app.Get("/probe", func(c *fiber.Ctx) error { return c.SendStatus(fiber.StatusOK) })
|
||||
|
||||
req := httptest.NewRequest("OPTIONS", "/probe", nil)
|
||||
req.Header.Set("Origin", "https://app.nearledaily.com")
|
||||
req.Header.Set("Access-Control-Request-Method", "GET")
|
||||
if requestHeaders != "" {
|
||||
req.Header.Set("Access-Control-Request-Headers", requestHeaders)
|
||||
}
|
||||
|
||||
resp, err := app.Test(req, -1)
|
||||
if err != nil {
|
||||
t.Fatalf("preflight: %v", err)
|
||||
}
|
||||
|
||||
out := map[string]string{}
|
||||
for _, name := range []string{
|
||||
"Access-Control-Allow-Origin",
|
||||
"Access-Control-Allow-Headers",
|
||||
"Access-Control-Allow-Methods",
|
||||
"Access-Control-Allow-Credentials",
|
||||
} {
|
||||
out[name] = resp.Header.Get(name)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func TestTheBrowserIsAllowedToSendTheSessionToken(t *testing.T) {
|
||||
// The bug itself. Without `Authorization` in this list the console cannot
|
||||
// make a single authenticated call, and the error surfaces only in a
|
||||
// browser console as a CORS failure.
|
||||
headers := preflight(t, "authorization")["Access-Control-Allow-Headers"]
|
||||
|
||||
if !strings.Contains(strings.ToLower(headers), "authorization") {
|
||||
t.Fatalf("the console may not send its session token: %q", headers)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTheHeadersTheConsoleAlreadySentStillWork(t *testing.T) {
|
||||
// Adding one header must not quietly drop the others.
|
||||
headers := strings.ToLower(preflight(t, "content-type")["Access-Control-Allow-Headers"])
|
||||
|
||||
for _, needed := range []string{"content-type", "accept", "origin"} {
|
||||
if !strings.Contains(headers, needed) {
|
||||
t.Fatalf("%q is no longer allowed: %q", needed, headers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAWildcardOriginIsNotPairedWithCredentials(t *testing.T) {
|
||||
// Not a valid combination: a browser rejects a credentialed response
|
||||
// carrying a wildcard origin. It was here already and was harmless only
|
||||
// because nothing used credentials — it would have become a second bug
|
||||
// that looked exactly like the first, the day anything did.
|
||||
got := preflight(t, "authorization")
|
||||
|
||||
if got["Access-Control-Allow-Origin"] == "*" &&
|
||||
strings.EqualFold(got["Access-Control-Allow-Credentials"], "true") {
|
||||
t.Fatal("wildcard origin with credentials allowed — browsers reject this pair")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEveryMethodTheConsoleUsesIsAllowed(t *testing.T) {
|
||||
// The console writes with POST, PUT and DELETE. A missing one fails only
|
||||
// on the screens that use it, which is the kind of gap that ships.
|
||||
methods := strings.ToUpper(preflight(t, "authorization")["Access-Control-Allow-Methods"])
|
||||
|
||||
for _, method := range []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"} {
|
||||
if !strings.Contains(methods, method) {
|
||||
t.Fatalf("%s is not allowed cross-origin: %q", method, methods)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAResponseHeaderIsNotListedAsAnAllowedRequestHeader(t *testing.T) {
|
||||
// `Access-Control-Allow-Origin` was in the allowed REQUEST headers, which is
|
||||
// a category error: it is something the server sends back, never something a
|
||||
// browser asks to send. Harmless, but it reads as though somebody added
|
||||
// names until the error went away.
|
||||
headers := strings.ToLower(preflight(t, "authorization")["Access-Control-Allow-Headers"])
|
||||
|
||||
if strings.Contains(headers, "access-control-allow-origin") {
|
||||
t.Fatalf("a response header is listed as an allowed request header: %q", headers)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user