Adding upstream version 2.52.6.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
a960158181
commit
6d002e9543
441 changed files with 95392 additions and 0 deletions
289
middleware/cors/cors.go
Normal file
289
middleware/cors/cors.go
Normal file
|
@ -0,0 +1,289 @@
|
|||
package cors
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/log"
|
||||
)
|
||||
|
||||
// Config defines the config for middleware.
|
||||
type Config struct {
|
||||
// Next defines a function to skip this middleware when returned true.
|
||||
//
|
||||
// Optional. Default: nil
|
||||
Next func(c *fiber.Ctx) bool
|
||||
|
||||
// AllowOriginsFunc defines a function that will set the 'Access-Control-Allow-Origin'
|
||||
// response header to the 'origin' request header when returned true. This allows for
|
||||
// dynamic evaluation of allowed origins. Note if AllowCredentials is true, wildcard origins
|
||||
// will be not have the 'Access-Control-Allow-Credentials' header set to 'true'.
|
||||
//
|
||||
// Optional. Default: nil
|
||||
AllowOriginsFunc func(origin string) bool
|
||||
|
||||
// AllowOrigin defines a comma separated list of origins that may access the resource.
|
||||
//
|
||||
// Optional. Default value "*"
|
||||
AllowOrigins string
|
||||
|
||||
// AllowMethods defines a list methods allowed when accessing the resource.
|
||||
// This is used in response to a preflight request.
|
||||
//
|
||||
// Optional. Default value "GET,POST,HEAD,PUT,DELETE,PATCH"
|
||||
AllowMethods string
|
||||
|
||||
// AllowHeaders defines a list of request headers that can be used when
|
||||
// making the actual request. This is in response to a preflight request.
|
||||
//
|
||||
// Optional. Default value "".
|
||||
AllowHeaders string
|
||||
|
||||
// AllowCredentials indicates whether or not the response to the request
|
||||
// can be exposed when the credentials flag is true. When used as part of
|
||||
// a response to a preflight request, this indicates whether or not the
|
||||
// actual request can be made using credentials. Note: If true, AllowOrigins
|
||||
// cannot be set to a wildcard ("*") to prevent security vulnerabilities.
|
||||
//
|
||||
// Optional. Default value false.
|
||||
AllowCredentials bool
|
||||
|
||||
// ExposeHeaders defines a whitelist headers that clients are allowed to
|
||||
// access.
|
||||
//
|
||||
// Optional. Default value "".
|
||||
ExposeHeaders string
|
||||
|
||||
// MaxAge indicates how long (in seconds) the results of a preflight request
|
||||
// can be cached.
|
||||
// If you pass MaxAge 0, Access-Control-Max-Age header will not be added and
|
||||
// browser will use 5 seconds by default.
|
||||
// To disable caching completely, pass MaxAge value negative. It will set the Access-Control-Max-Age header 0.
|
||||
//
|
||||
// Optional. Default value 0.
|
||||
MaxAge int
|
||||
}
|
||||
|
||||
// ConfigDefault is the default config
|
||||
var ConfigDefault = Config{
|
||||
Next: nil,
|
||||
AllowOriginsFunc: nil,
|
||||
AllowOrigins: "*",
|
||||
AllowMethods: strings.Join([]string{
|
||||
fiber.MethodGet,
|
||||
fiber.MethodPost,
|
||||
fiber.MethodHead,
|
||||
fiber.MethodPut,
|
||||
fiber.MethodDelete,
|
||||
fiber.MethodPatch,
|
||||
}, ","),
|
||||
AllowHeaders: "",
|
||||
AllowCredentials: false,
|
||||
ExposeHeaders: "",
|
||||
MaxAge: 0,
|
||||
}
|
||||
|
||||
// New creates a new middleware handler
|
||||
func New(config ...Config) fiber.Handler {
|
||||
// Set default config
|
||||
cfg := ConfigDefault
|
||||
|
||||
// Override config if provided
|
||||
if len(config) > 0 {
|
||||
cfg = config[0]
|
||||
|
||||
// Set default values
|
||||
if cfg.AllowMethods == "" {
|
||||
cfg.AllowMethods = ConfigDefault.AllowMethods
|
||||
}
|
||||
// When none of the AllowOrigins or AllowOriginsFunc config was defined, set the default AllowOrigins value with "*"
|
||||
if cfg.AllowOrigins == "" && cfg.AllowOriginsFunc == nil {
|
||||
cfg.AllowOrigins = ConfigDefault.AllowOrigins
|
||||
}
|
||||
}
|
||||
|
||||
// Warning logs if both AllowOrigins and AllowOriginsFunc are set
|
||||
if cfg.AllowOrigins != "" && cfg.AllowOriginsFunc != nil {
|
||||
log.Warn("[CORS] Both 'AllowOrigins' and 'AllowOriginsFunc' have been defined.")
|
||||
}
|
||||
|
||||
// Validate CORS credentials configuration
|
||||
if cfg.AllowCredentials && cfg.AllowOrigins == "*" {
|
||||
panic("[CORS] Insecure setup, 'AllowCredentials' is set to true, and 'AllowOrigins' is set to a wildcard.")
|
||||
}
|
||||
|
||||
// allowOrigins is a slice of strings that contains the allowed origins
|
||||
// defined in the 'AllowOrigins' configuration.
|
||||
allowOrigins := []string{}
|
||||
allowSOrigins := []subdomain{}
|
||||
allowAllOrigins := false
|
||||
|
||||
// Validate and normalize static AllowOrigins
|
||||
if cfg.AllowOrigins != "" && cfg.AllowOrigins != "*" {
|
||||
origins := strings.Split(cfg.AllowOrigins, ",")
|
||||
for _, origin := range origins {
|
||||
if i := strings.Index(origin, "://*."); i != -1 {
|
||||
trimmedOrigin := strings.TrimSpace(origin[:i+3] + origin[i+4:])
|
||||
isValid, normalizedOrigin := normalizeOrigin(trimmedOrigin)
|
||||
if !isValid {
|
||||
panic("[CORS] Invalid origin format in configuration: " + trimmedOrigin)
|
||||
}
|
||||
sd := subdomain{prefix: normalizedOrigin[:i+3], suffix: normalizedOrigin[i+3:]}
|
||||
allowSOrigins = append(allowSOrigins, sd)
|
||||
} else {
|
||||
trimmedOrigin := strings.TrimSpace(origin)
|
||||
isValid, normalizedOrigin := normalizeOrigin(trimmedOrigin)
|
||||
if !isValid {
|
||||
panic("[CORS] Invalid origin format in configuration: " + trimmedOrigin)
|
||||
}
|
||||
allowOrigins = append(allowOrigins, normalizedOrigin)
|
||||
}
|
||||
}
|
||||
} else if cfg.AllowOrigins == "*" {
|
||||
allowAllOrigins = true
|
||||
}
|
||||
|
||||
// Strip white spaces
|
||||
allowMethods := strings.ReplaceAll(cfg.AllowMethods, " ", "")
|
||||
allowHeaders := strings.ReplaceAll(cfg.AllowHeaders, " ", "")
|
||||
exposeHeaders := strings.ReplaceAll(cfg.ExposeHeaders, " ", "")
|
||||
|
||||
// Convert int to string
|
||||
maxAge := strconv.Itoa(cfg.MaxAge)
|
||||
|
||||
// Return new handler
|
||||
return func(c *fiber.Ctx) error {
|
||||
// Don't execute middleware if Next returns true
|
||||
if cfg.Next != nil && cfg.Next(c) {
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
// Get originHeader header
|
||||
originHeader := strings.ToLower(c.Get(fiber.HeaderOrigin))
|
||||
|
||||
// If the request does not have Origin header, the request is outside the scope of CORS
|
||||
if originHeader == "" {
|
||||
// See https://fetch.spec.whatwg.org/#cors-protocol-and-http-caches
|
||||
// Unless all origins are allowed, we include the Vary header to cache the response correctly
|
||||
if !allowAllOrigins {
|
||||
c.Vary(fiber.HeaderOrigin)
|
||||
}
|
||||
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
// If it's a preflight request and doesn't have Access-Control-Request-Method header, it's outside the scope of CORS
|
||||
if c.Method() == fiber.MethodOptions && c.Get(fiber.HeaderAccessControlRequestMethod) == "" {
|
||||
// Response to OPTIONS request should not be cached but,
|
||||
// some caching can be configured to cache such responses.
|
||||
// To Avoid poisoning the cache, we include the Vary header
|
||||
// for non-CORS OPTIONS requests:
|
||||
c.Vary(fiber.HeaderOrigin)
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
// Set default allowOrigin to empty string
|
||||
allowOrigin := ""
|
||||
|
||||
// Check allowed origins
|
||||
if allowAllOrigins {
|
||||
allowOrigin = "*"
|
||||
} else {
|
||||
// Check if the origin is in the list of allowed origins
|
||||
for _, origin := range allowOrigins {
|
||||
if origin == originHeader {
|
||||
allowOrigin = originHeader
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the origin is in the list of allowed subdomains
|
||||
if allowOrigin == "" {
|
||||
for _, sOrigin := range allowSOrigins {
|
||||
if sOrigin.match(originHeader) {
|
||||
allowOrigin = originHeader
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run AllowOriginsFunc if the logic for
|
||||
// handling the value in 'AllowOrigins' does
|
||||
// not result in allowOrigin being set.
|
||||
if allowOrigin == "" && cfg.AllowOriginsFunc != nil && cfg.AllowOriginsFunc(originHeader) {
|
||||
allowOrigin = originHeader
|
||||
}
|
||||
|
||||
// Simple request
|
||||
// Ommit allowMethods and allowHeaders, only used for pre-flight requests
|
||||
if c.Method() != fiber.MethodOptions {
|
||||
if !allowAllOrigins {
|
||||
// See https://fetch.spec.whatwg.org/#cors-protocol-and-http-caches
|
||||
c.Vary(fiber.HeaderOrigin)
|
||||
}
|
||||
setCORSHeaders(c, allowOrigin, "", "", exposeHeaders, maxAge, cfg)
|
||||
return c.Next()
|
||||
}
|
||||
|
||||
// Pre-flight request
|
||||
|
||||
// Response to OPTIONS request should not be cached but,
|
||||
// some caching can be configured to cache such responses.
|
||||
// To Avoid poisoning the cache, we include the Vary header
|
||||
// of preflight responses:
|
||||
c.Vary(fiber.HeaderAccessControlRequestMethod)
|
||||
c.Vary(fiber.HeaderAccessControlRequestHeaders)
|
||||
c.Vary(fiber.HeaderOrigin)
|
||||
|
||||
setCORSHeaders(c, allowOrigin, allowMethods, allowHeaders, exposeHeaders, maxAge, cfg)
|
||||
|
||||
// Send 204 No Content
|
||||
return c.SendStatus(fiber.StatusNoContent)
|
||||
}
|
||||
}
|
||||
|
||||
// Function to set CORS headers
|
||||
func setCORSHeaders(c *fiber.Ctx, allowOrigin, allowMethods, allowHeaders, exposeHeaders, maxAge string, cfg Config) {
|
||||
if cfg.AllowCredentials {
|
||||
// When AllowCredentials is true, set the Access-Control-Allow-Origin to the specific origin instead of '*'
|
||||
if allowOrigin == "*" {
|
||||
c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin)
|
||||
log.Warn("[CORS] 'AllowCredentials' is true, but 'AllowOrigins' cannot be set to '*'.")
|
||||
} else if allowOrigin != "" {
|
||||
c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin)
|
||||
c.Set(fiber.HeaderAccessControlAllowCredentials, "true")
|
||||
}
|
||||
} else if allowOrigin != "" {
|
||||
// For non-credential requests, it's safe to set to '*' or specific origins
|
||||
c.Set(fiber.HeaderAccessControlAllowOrigin, allowOrigin)
|
||||
}
|
||||
|
||||
// Set Allow-Methods if not empty
|
||||
if allowMethods != "" {
|
||||
c.Set(fiber.HeaderAccessControlAllowMethods, allowMethods)
|
||||
}
|
||||
|
||||
// Set Allow-Headers if not empty
|
||||
if allowHeaders != "" {
|
||||
c.Set(fiber.HeaderAccessControlAllowHeaders, allowHeaders)
|
||||
} else {
|
||||
h := c.Get(fiber.HeaderAccessControlRequestHeaders)
|
||||
if h != "" {
|
||||
c.Set(fiber.HeaderAccessControlAllowHeaders, h)
|
||||
}
|
||||
}
|
||||
|
||||
// Set MaxAge if set
|
||||
if cfg.MaxAge > 0 {
|
||||
c.Set(fiber.HeaderAccessControlMaxAge, maxAge)
|
||||
} else if cfg.MaxAge < 0 {
|
||||
c.Set(fiber.HeaderAccessControlMaxAge, "0")
|
||||
}
|
||||
|
||||
// Set Expose-Headers if not empty
|
||||
if exposeHeaders != "" {
|
||||
c.Set(fiber.HeaderAccessControlExposeHeaders, exposeHeaders)
|
||||
}
|
||||
}
|
1335
middleware/cors/cors_test.go
Normal file
1335
middleware/cors/cors_test.go
Normal file
File diff suppressed because it is too large
Load diff
66
middleware/cors/utils.go
Normal file
66
middleware/cors/utils.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
package cors
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// matchScheme compares the scheme of the domain and pattern
|
||||
func matchScheme(domain, pattern string) bool {
|
||||
didx := strings.Index(domain, ":")
|
||||
pidx := strings.Index(pattern, ":")
|
||||
return didx != -1 && pidx != -1 && domain[:didx] == pattern[:pidx]
|
||||
}
|
||||
|
||||
// normalizeDomain removes the scheme and port from the input domain
|
||||
func normalizeDomain(input string) string {
|
||||
// Remove scheme
|
||||
input = strings.TrimPrefix(strings.TrimPrefix(input, "http://"), "https://")
|
||||
|
||||
// Find and remove port, if present
|
||||
if len(input) > 0 && input[0] != '[' {
|
||||
if portIndex := strings.Index(input, ":"); portIndex != -1 {
|
||||
input = input[:portIndex]
|
||||
}
|
||||
}
|
||||
|
||||
return input
|
||||
}
|
||||
|
||||
// normalizeOrigin checks if the provided origin is in a correct format
|
||||
// and normalizes it by removing any path or trailing slash.
|
||||
// It returns a boolean indicating whether the origin is valid
|
||||
// and the normalized origin.
|
||||
func normalizeOrigin(origin string) (bool, string) {
|
||||
parsedOrigin, err := url.Parse(origin)
|
||||
if err != nil {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// Don't allow a wildcard with a protocol
|
||||
// wildcards cannot be used within any other value. For example, the following header is not valid:
|
||||
// Access-Control-Allow-Origin: https://*
|
||||
if strings.Contains(parsedOrigin.Host, "*") {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// Validate there is a host present. The presence of a path, query, or fragment components
|
||||
// is checked, but a trailing "/" (indicative of the root) is allowed for the path and will be normalized
|
||||
if parsedOrigin.Host == "" || (parsedOrigin.Path != "" && parsedOrigin.Path != "/") || parsedOrigin.RawQuery != "" || parsedOrigin.Fragment != "" {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// Normalize the origin by constructing it from the scheme and host.
|
||||
// The path or trailing slash is not included in the normalized origin.
|
||||
return true, strings.ToLower(parsedOrigin.Scheme) + "://" + strings.ToLower(parsedOrigin.Host)
|
||||
}
|
||||
|
||||
type subdomain struct {
|
||||
// The wildcard pattern
|
||||
prefix string
|
||||
suffix string
|
||||
}
|
||||
|
||||
func (s subdomain) match(o string) bool {
|
||||
return len(o) >= len(s.prefix)+len(s.suffix) && strings.HasPrefix(o, s.prefix) && strings.HasSuffix(o, s.suffix)
|
||||
}
|
196
middleware/cors/utils_test.go
Normal file
196
middleware/cors/utils_test.go
Normal file
|
@ -0,0 +1,196 @@
|
|||
package cors
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gofiber/fiber/v2/utils"
|
||||
)
|
||||
|
||||
// go test -run -v Test_normalizeOrigin
|
||||
func Test_normalizeOrigin(t *testing.T) {
|
||||
testCases := []struct {
|
||||
origin string
|
||||
expectedValid bool
|
||||
expectedOrigin string
|
||||
}{
|
||||
{origin: "http://example.com", expectedValid: true, expectedOrigin: "http://example.com"}, // Simple case should work.
|
||||
{origin: "http://example.com/", expectedValid: true, expectedOrigin: "http://example.com"}, // Trailing slash should be removed.
|
||||
{origin: "http://example.com:3000", expectedValid: true, expectedOrigin: "http://example.com:3000"}, // Port should be preserved.
|
||||
{origin: "http://example.com:3000/", expectedValid: true, expectedOrigin: "http://example.com:3000"}, // Trailing slash should be removed.
|
||||
{origin: "app://example.com/", expectedValid: true, expectedOrigin: "app://example.com"}, // App scheme should be accepted.
|
||||
{origin: "http://", expectedValid: false, expectedOrigin: ""}, // Invalid origin should not be accepted.
|
||||
{origin: "file:///etc/passwd", expectedValid: false, expectedOrigin: ""}, // File scheme should not be accepted.
|
||||
{origin: "https://*example.com", expectedValid: false, expectedOrigin: ""}, // Wildcard domain should not be accepted.
|
||||
{origin: "http://*.example.com", expectedValid: false, expectedOrigin: ""}, // Wildcard subdomain should not be accepted.
|
||||
{origin: "http://example.com/path", expectedValid: false, expectedOrigin: ""}, // Path should not be accepted.
|
||||
{origin: "http://example.com?query=123", expectedValid: false, expectedOrigin: ""}, // Query should not be accepted.
|
||||
{origin: "http://example.com#fragment", expectedValid: false, expectedOrigin: ""}, // Fragment should not be accepted.
|
||||
{origin: "http://localhost", expectedValid: true, expectedOrigin: "http://localhost"}, // Localhost should be accepted.
|
||||
{origin: "http://127.0.0.1", expectedValid: true, expectedOrigin: "http://127.0.0.1"}, // IPv4 address should be accepted.
|
||||
{origin: "http://[::1]", expectedValid: true, expectedOrigin: "http://[::1]"}, // IPv6 address should be accepted.
|
||||
{origin: "http://[::1]:8080", expectedValid: true, expectedOrigin: "http://[::1]:8080"}, // IPv6 address with port should be accepted.
|
||||
{origin: "http://[::1]:8080/", expectedValid: true, expectedOrigin: "http://[::1]:8080"}, // IPv6 address with port and trailing slash should be accepted.
|
||||
{origin: "http://[::1]:8080/path", expectedValid: false, expectedOrigin: ""}, // IPv6 address with port and path should not be accepted.
|
||||
{origin: "http://[::1]:8080?query=123", expectedValid: false, expectedOrigin: ""}, // IPv6 address with port and query should not be accepted.
|
||||
{origin: "http://[::1]:8080#fragment", expectedValid: false, expectedOrigin: ""}, // IPv6 address with port and fragment should not be accepted.
|
||||
{origin: "http://[::1]:8080/path?query=123#fragment", expectedValid: false, expectedOrigin: ""}, // IPv6 address with port, path, query, and fragment should not be accepted.
|
||||
{origin: "http://[::1]:8080/path?query=123#fragment/", expectedValid: false, expectedOrigin: ""}, // IPv6 address with port, path, query, fragment, and trailing slash should not be accepted.
|
||||
{origin: "http://[::1]:8080/path?query=123#fragment/invalid", expectedValid: false, expectedOrigin: ""}, // IPv6 address with port, path, query, fragment, trailing slash, and invalid segment should not be accepted.
|
||||
{origin: "http://[::1]:8080/path?query=123#fragment/invalid/", expectedValid: false, expectedOrigin: ""}, // IPv6 address with port, path, query, fragment, trailing slash, and invalid segment with trailing slash should not be accepted.
|
||||
{origin: "http://[::1]:8080/path?query=123#fragment/invalid/segment", expectedValid: false, expectedOrigin: ""}, // IPv6 address with port, path, query, fragment, trailing slash, and invalid segment with additional segment should not be accepted.
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
valid, normalizedOrigin := normalizeOrigin(tc.origin)
|
||||
|
||||
if valid != tc.expectedValid {
|
||||
t.Errorf("Expected origin '%s' to be valid: %v, but got: %v", tc.origin, tc.expectedValid, valid)
|
||||
}
|
||||
|
||||
if normalizedOrigin != tc.expectedOrigin {
|
||||
t.Errorf("Expected normalized origin '%s' for origin '%s', but got: '%s'", tc.expectedOrigin, tc.origin, normalizedOrigin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// go test -run -v Test_matchScheme
|
||||
func Test_matchScheme(t *testing.T) {
|
||||
testCases := []struct {
|
||||
domain string
|
||||
pattern string
|
||||
expected bool
|
||||
}{
|
||||
{"http://example.com", "http://example.com", true}, // Exact match should work.
|
||||
{"https://example.com", "http://example.com", false}, // Scheme mismatch should matter.
|
||||
{"http://example.com", "https://example.com", false}, // Scheme mismatch should matter.
|
||||
{"http://example.com", "http://example.org", true}, // Different domains should not matter.
|
||||
{"http://example.com", "http://example.com:8080", true}, // Port should not matter.
|
||||
{"http://example.com:8080", "http://example.com", true}, // Port should not matter.
|
||||
{"http://example.com:8080", "http://example.com:8081", true}, // Different ports should not matter.
|
||||
{"http://localhost", "http://localhost", true}, // Localhost should match.
|
||||
{"http://127.0.0.1", "http://127.0.0.1", true}, // IPv4 address should match.
|
||||
{"http://[::1]", "http://[::1]", true}, // IPv6 address should match.
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
result := matchScheme(tc.domain, tc.pattern)
|
||||
|
||||
if result != tc.expected {
|
||||
t.Errorf("Expected matchScheme('%s', '%s') to be %v, but got %v", tc.domain, tc.pattern, tc.expected, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// go test -run -v Test_normalizeDomain
|
||||
func Test_normalizeDomain(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
expectedOutput string
|
||||
}{
|
||||
{"http://example.com", "example.com"}, // Simple case with http scheme.
|
||||
{"https://example.com", "example.com"}, // Simple case with https scheme.
|
||||
{"http://example.com:3000", "example.com"}, // Case with port.
|
||||
{"https://example.com:3000", "example.com"}, // Case with port and https scheme.
|
||||
{"http://example.com/path", "example.com/path"}, // Case with path.
|
||||
{"http://example.com?query=123", "example.com?query=123"}, // Case with query.
|
||||
{"http://example.com#fragment", "example.com#fragment"}, // Case with fragment.
|
||||
{"example.com", "example.com"}, // Case without scheme.
|
||||
{"example.com:8080", "example.com"}, // Case without scheme but with port.
|
||||
{"sub.example.com", "sub.example.com"}, // Case with subdomain.
|
||||
{"sub.sub.example.com", "sub.sub.example.com"}, // Case with nested subdomain.
|
||||
{"http://localhost", "localhost"}, // Case with localhost.
|
||||
{"http://127.0.0.1", "127.0.0.1"}, // Case with IPv4 address.
|
||||
{"http://[::1]", "[::1]"}, // Case with IPv6 address.
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
output := normalizeDomain(tc.input)
|
||||
|
||||
if output != tc.expectedOutput {
|
||||
t.Errorf("Expected normalized domain '%s' for input '%s', but got: '%s'", tc.expectedOutput, tc.input, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// go test -v -run=^$ -bench=Benchmark_CORS_SubdomainMatch -benchmem -count=4
|
||||
func Benchmark_CORS_SubdomainMatch(b *testing.B) {
|
||||
s := subdomain{
|
||||
prefix: "www",
|
||||
suffix: ".example.com",
|
||||
}
|
||||
|
||||
o := "www.example.com"
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
s.match(o)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_CORS_SubdomainMatch(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
sub subdomain
|
||||
origin string
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "match with different scheme",
|
||||
sub: subdomain{prefix: "http://api.", suffix: ".example.com"},
|
||||
origin: "https://api.service.example.com",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "match with different scheme",
|
||||
sub: subdomain{prefix: "https://", suffix: ".example.com"},
|
||||
origin: "http://api.service.example.com",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "match with valid subdomain",
|
||||
sub: subdomain{prefix: "https://", suffix: ".example.com"},
|
||||
origin: "https://api.service.example.com",
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "match with valid nested subdomain",
|
||||
sub: subdomain{prefix: "https://", suffix: ".example.com"},
|
||||
origin: "https://1.2.api.service.example.com",
|
||||
expected: true,
|
||||
},
|
||||
|
||||
{
|
||||
name: "no match with invalid prefix",
|
||||
sub: subdomain{prefix: "https://abc.", suffix: ".example.com"},
|
||||
origin: "https://service.example.com",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "no match with invalid suffix",
|
||||
sub: subdomain{prefix: "https://", suffix: ".example.com"},
|
||||
origin: "https://api.example.org",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "no match with empty origin",
|
||||
sub: subdomain{prefix: "https://", suffix: ".example.com"},
|
||||
origin: "",
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "partial match not considered a match",
|
||||
sub: subdomain{prefix: "https://service.", suffix: ".example.com"},
|
||||
origin: "https://api.example.com",
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tt.sub.match(tt.origin)
|
||||
utils.AssertEqual(t, tt.expected, got, "subdomain.match()")
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue