pax_global_header00006660000000000000000000000064150252714400014512gustar00rootroot0000000000000052 comment=47d9afe5a3f22e913dfd5dc0fab17d1fef58cbbc cors-1.7.6/000077500000000000000000000000001502527144000124735ustar00rootroot00000000000000cors-1.7.6/.github/000077500000000000000000000000001502527144000140335ustar00rootroot00000000000000cors-1.7.6/.github/dependabot.yml000066400000000000000000000003031502527144000166570ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: github-actions directory: / schedule: interval: weekly - package-ecosystem: gomod directory: / schedule: interval: weekly cors-1.7.6/.github/workflows/000077500000000000000000000000001502527144000160705ustar00rootroot00000000000000cors-1.7.6/.github/workflows/bearer.yml000066400000000000000000000013461502527144000200570ustar00rootroot00000000000000name: Bearer PR Check on: pull_request: types: [opened, synchronize, reopened] permissions: contents: read pull-requests: write jobs: rule_check: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 - uses: reviewdog/action-setup@v1 with: reviewdog_version: latest - name: Run Report id: report uses: bearer/bearer-action@v2 with: format: rdjson output: rd.json diff: true - name: Run reviewdog if: always() env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | cat rd.json | reviewdog -f=rdjson -reporter=github-pr-review cors-1.7.6/.github/workflows/codeql.yml000066400000000000000000000033361502527144000200670ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [master] pull_request: # The branches below must be a subset of the branches above branches: [master] schedule: - cron: "41 23 * * 6" jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: ["go"] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - name: Checkout repository uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 cors-1.7.6/.github/workflows/go.yml000066400000000000000000000032261502527144000172230ustar00rootroot00000000000000name: Run Tests on: push: branches: - master pull_request: branches: - master jobs: lint: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup go uses: actions/setup-go@v5 with: go-version-file: go.mod check-latest: true - name: Setup golangci-lint uses: golangci/golangci-lint-action@v8 with: version: v2.1 test: strategy: matrix: os: [ubuntu-latest, macos-latest] go: [1.23, 1.24] include: - os: ubuntu-latest go-build: ~/.cache/go-build - os: macos-latest go-build: ~/Library/Caches/go-build name: ${{ matrix.os }} @ Go ${{ matrix.go }} runs-on: ${{ matrix.os }} env: GO111MODULE: on GOPROXY: https://proxy.golang.org steps: - name: Set up Go ${{ matrix.go }} uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - name: Checkout Code uses: actions/checkout@v4 with: ref: ${{ github.ref }} - uses: actions/cache@v4 with: path: | ${{ matrix.go-build }} ~/go/pkg/mod key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- - name: Run Tests run: | go test -v -covermode=atomic -coverprofile=coverage.out - name: Upload coverage to Codecov uses: codecov/codecov-action@v5 with: flags: ${{ matrix.os }},go-${{ matrix.go }} cors-1.7.6/.github/workflows/goreleaser.yml000066400000000000000000000012661502527144000207500ustar00rootroot00000000000000name: Goreleaser on: push: tags: - "*" permissions: contents: write jobs: goreleaser: runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup go uses: actions/setup-go@v5 with: go-version-file: go.mod check-latest: true - name: Run GoReleaser uses: goreleaser/goreleaser-action@v6 with: # either 'goreleaser' (default) or 'goreleaser-pro' distribution: goreleaser version: latest args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} cors-1.7.6/.gitignore000066400000000000000000000002471502527144000144660ustar00rootroot00000000000000*.o *.a *.so _obj _test *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof coverage.out .idea cors-1.7.6/.golangci.yml000066400000000000000000000014171502527144000150620ustar00rootroot00000000000000version: "2" linters: default: none enable: - bodyclose - dogsled - dupl - errcheck - exhaustive - gochecknoinits - goconst - gocritic - gocyclo - goprintffuncname - gosec - govet - ineffassign - lll - misspell - nakedret - noctx - nolintlint - rowserrcheck - staticcheck - unconvert - unparam - unused - whitespace exclusions: generated: lax presets: - comments - common-false-positives - legacy - std-error-handling paths: - third_party$ - builtin$ - examples$ formatters: enable: - gofmt - gofumpt - goimports exclusions: generated: lax paths: - third_party$ - builtin$ - examples$ cors-1.7.6/.goreleaser.yaml000066400000000000000000000012341502527144000155650ustar00rootroot00000000000000builds: - # If true, skip the build. # Useful for library projects. # Default is false skip: true changelog: use: github groups: - title: Features regexp: "^.*feat[(\\w)]*:+.*$" order: 0 - title: "Bug fixes" regexp: "^.*fix[(\\w)]*:+.*$" order: 1 - title: "Enhancements" regexp: "^.*chore[(\\w)]*:+.*$" order: 2 - title: "Refactor" regexp: "^.*refactor[(\\w)]*:+.*$" order: 3 - title: "Build process updates" regexp: ^.*?(build|ci)(\(.+\))??!?:.+$ order: 4 - title: "Documentation updates" regexp: ^.*?docs?(\(.+\))??!?:.+$ order: 4 - title: Others cors-1.7.6/LICENSE000066400000000000000000000020521502527144000134770ustar00rootroot00000000000000MIT License Copyright (c) 2016 Gin-Gonic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cors-1.7.6/README.md000066400000000000000000000242101502527144000137510ustar00rootroot00000000000000# gin-contrib/cors [![Run Tests](https://github.com/gin-contrib/cors/actions/workflows/go.yml/badge.svg)](https://github.com/gin-contrib/cors/actions/workflows/go.yml) [![codecov](https://codecov.io/gh/gin-contrib/cors/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-contrib/cors) [![Go Report Card](https://goreportcard.com/badge/github.com/gin-contrib/cors)](https://goreportcard.com/report/github.com/gin-contrib/cors) [![GoDoc](https://godoc.org/github.com/gin-contrib/cors?status.svg)](https://godoc.org/github.com/gin-contrib/cors) - [gin-contrib/cors](#gin-contribcors) - [Overview](#overview) - [Installation](#installation) - [Quick Start](#quick-start) - [Advanced Usage](#advanced-usage) - [Custom Configuration](#custom-configuration) - [DefaultConfig Reference](#defaultconfig-reference) - [Default() Convenience](#default-convenience) - [Configuration Reference](#configuration-reference) - [Notes on Configuration](#notes-on-configuration) - [Examples](#examples) - [Advanced Options](#advanced-options) - [Custom Origin Validation](#custom-origin-validation) - [With Gin Context](#with-gin-context) - [Helper Methods](#helper-methods) - [Validation \& Error Handling](#validation--error-handling) - [Important Notes](#important-notes) --- ## Overview **CORS (Cross-Origin Resource Sharing)** middleware for [Gin](https://github.com/gin-gonic/gin). - Enables flexible CORS handling for your Gin-based APIs. - Highly configurable: origins, methods, headers, credentials, and more. --- ## Installation ```sh go get github.com/gin-contrib/cors ``` Import in your Go code: ```go import "github.com/gin-contrib/cors" ``` --- ## Quick Start Allow all origins (default): ```go import ( "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.Use(cors.Default()) // All origins allowed by default router.Run() } ``` > ⚠️ **Warning:** Allowing all origins disables cookies for clients. For credentialed requests, **do not** allow all origins. --- ## Advanced Usage ### Custom Configuration Configure allowed origins, methods, headers, and more: ```go import ( "time" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() router.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://foo.com"}, AllowMethods: []string{"PUT", "PATCH"}, AllowHeaders: []string{"Origin"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: true, AllowOriginFunc: func(origin string) bool { return origin == "https://github.com" }, MaxAge: 12 * time.Hour, })) router.Run() } ``` --- ### DefaultConfig Reference Start with library defaults and customize as needed: ```go import ( "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() config := cors.DefaultConfig() config.AllowOrigins = []string{"http://google.com"} // config.AllowOrigins = []string{"http://google.com", "http://facebook.com"} // config.AllowAllOrigins = true router.Use(cors.New(config)) router.Run() } ``` > **Note:** `Default()` allows all origins, but `DefaultConfig()` does **not**. To allow all origins, set `AllowAllOrigins = true`. --- ### Default() Convenience Enable all origins with a single call: ```go router.Use(cors.Default()) // Equivalent to AllowAllOrigins = true ``` --- ## Configuration Reference The middleware is controlled via the `cors.Config` struct. All fields are optional unless otherwise stated. | Field | Type | Default | Description | |-------------------------------|-----------------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------| | `AllowAllOrigins` | `bool` | `false` | If true, allows all origins. Credentials **cannot** be used. | | `AllowOrigins` | `[]string` | `[]` | List of allowed origins. Supports exact match, `*`, and wildcards. | | `AllowOriginFunc` | `func(string) bool` | `nil` | Custom function to validate origin. If set, `AllowOrigins` is ignored. | | `AllowOriginWithContextFunc` | `func(*gin.Context,string)bool` | `nil` | Like `AllowOriginFunc`, but with request context. | | `AllowMethods` | `[]string` | `[]string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"}` | Allowed HTTP methods. | | `AllowPrivateNetwork` | `bool` | `false` | Adds [Private Network Access](https://wicg.github.io/private-network-access/) CORS header. | | `AllowHeaders` | `[]string` | `[]` | List of non-simple headers permitted in requests. | | `AllowCredentials` | `bool` | `false` | Allow cookies, HTTP auth, or client certs. Only if precise origins are used. | | `ExposeHeaders` | `[]string` | `[]` | Headers exposed to the browser. | | `MaxAge` | `time.Duration` | `12 * time.Hour` | Cache time for preflight requests. | | `AllowWildcard` | `bool` | `false` | Enables wildcards in origins (e.g. `https://*.example.com`). | | `AllowBrowserExtensions` | `bool` | `false` | Allow browser extension schemes as origins (e.g. `chrome-extension://`). | | `CustomSchemas` | `[]string` | `nil` | Additional allowed URI schemes (e.g. `tauri://`). | | `AllowWebSockets` | `bool` | `false` | Allow `ws://` and `wss://` schemas. | | `AllowFiles` | `bool` | `false` | Allow `file://` origins (dangerous; use only if necessary). | | `OptionsResponseStatusCode` | `int` | `204` | Custom status code for `OPTIONS` responses. | --- ### Notes on Configuration - Only one of `AllowAllOrigins`, `AllowOrigins`, `AllowOriginFunc`, or `AllowOriginWithContextFunc` should be set. - If `AllowAllOrigins` is true, other origin settings are ignored and credentialed requests are not allowed. - If `AllowWildcard` is enabled, only one `*` is allowed per origin string. - Use `AllowBrowserExtensions`, `AllowWebSockets`, or `AllowFiles` to permit non-HTTP(s) protocols as origins. - Custom schemas allow, for example, usage in desktop apps via custom URI schemes (`tauri://`, etc.). - If both `AllowOriginFunc` and `AllowOriginWithContextFunc` are set, the context-specific function is preferred. --- ### Examples #### Advanced Options ```go config := cors.Config{ AllowOrigins: []string{"https://*.foo.com", "https://bar.com"}, AllowWildcard: true, AllowMethods: []string{"GET", "POST"}, AllowHeaders: []string{"Authorization", "Content-Type"}, AllowCredentials: true, AllowBrowserExtensions: true, AllowWebSockets: true, AllowFiles: false, CustomSchemas: []string{"tauri://"}, MaxAge: 24 * time.Hour, ExposeHeaders: []string{"X-Custom-Header"}, AllowPrivateNetwork: true, } ``` #### Custom Origin Validation ```go config := cors.Config{ AllowOriginFunc: func(origin string) bool { // Allow any github.com subdomain or a custom rule return strings.HasSuffix(origin, "github.com") }, } ``` #### With Gin Context ```go config := cors.Config{ AllowOriginWithContextFunc: func(c *gin.Context, origin string) bool { // Allow only if a certain header is present return c.Request.Header.Get("X-Allow-CORS") == "yes" }, } ``` --- ## Helper Methods Dynamically add methods or headers to the config: ```go config.AddAllowMethods("DELETE", "OPTIONS") config.AddAllowHeaders("X-My-Header") config.AddExposeHeaders("X-Other-Header") ``` --- ## Validation & Error Handling - Calling `Validate()` on a `Config` checks for misconfiguration (called internally). - If `AllowAllOrigins` is set, you cannot also set `AllowOrigins` or any `AllowOriginFunc`. - If neither `AllowAllOrigins`, `AllowOriginFunc`, nor `AllowOrigins` is set, an error is raised. - If an `AllowOrigin` contains a wildcard but `AllowWildcard` is not enabled, or more than one `*` is present, a panic is triggered. - Invalid origin schemas or unsupported wildcards are rejected. --- ## Important Notes - **Enabling all origins disables cookies:** When `AllowAllOrigins` is enabled, Gin cannot set cookies for clients. If you need credential sharing (cookies, authentication headers), **do not** allow all origins. - For detailed documentation and configuration options, see the [GoDoc](https://godoc.org/github.com/gin-contrib/cors). cors-1.7.6/_examples/000077500000000000000000000000001502527144000144505ustar00rootroot00000000000000cors-1.7.6/_examples/example.go000066400000000000000000000013501502527144000164310ustar00rootroot00000000000000package main import ( "time" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" ) func main() { router := gin.Default() // CORS for https://foo.com and https://github.com origins, allowing: // - PUT and PATCH methods // - Origin header // - Credentials share // - Preflight requests cached for 12 hours router.Use(cors.New(cors.Config{ AllowOrigins: []string{"https://foo.com"}, AllowMethods: []string{"PUT", "PATCH"}, AllowHeaders: []string{"Origin"}, ExposeHeaders: []string{"Content-Length"}, AllowCredentials: true, AllowOriginFunc: func(origin string) bool { return origin == "https://github.com" }, MaxAge: 12 * time.Hour, })) if err := router.Run(); err != nil { panic(err) } } cors-1.7.6/config.go000066400000000000000000000074211502527144000142730ustar00rootroot00000000000000package cors import ( "net/http" "regexp" "strings" "github.com/gin-gonic/gin" ) type cors struct { allowAllOrigins bool allowCredentials bool allowOriginFunc func(string) bool allowOriginWithContextFunc func(*gin.Context, string) bool allowOrigins []string normalHeaders http.Header preflightHeaders http.Header wildcardOrigins [][]string optionsResponseStatusCode int } var ( DefaultSchemas = []string{ "http://", "https://", } ExtensionSchemas = []string{ "chrome-extension://", "safari-extension://", "moz-extension://", "ms-browser-extension://", } FileSchemas = []string{ "file://", } WebSocketSchemas = []string{ "ws://", "wss://", } ) func newCors(config Config) *cors { if err := config.Validate(); err != nil { panic(err.Error()) } for _, origin := range config.AllowOrigins { if origin == "*" { config.AllowAllOrigins = true } } if config.OptionsResponseStatusCode == 0 { config.OptionsResponseStatusCode = http.StatusNoContent } return &cors{ allowOriginFunc: config.AllowOriginFunc, allowOriginWithContextFunc: config.AllowOriginWithContextFunc, allowAllOrigins: config.AllowAllOrigins, allowCredentials: config.AllowCredentials, allowOrigins: normalize(config.AllowOrigins), normalHeaders: generateNormalHeaders(config), preflightHeaders: generatePreflightHeaders(config), wildcardOrigins: config.parseWildcardRules(), optionsResponseStatusCode: config.OptionsResponseStatusCode, } } func (cors *cors) applyCors(c *gin.Context) { origin := c.Request.Header.Get("Origin") if len(origin) == 0 { // request is not a CORS request return } host := c.Request.Host if origin == "http://"+host || origin == "https://"+host { // request is not a CORS request but have origin header. // for example, use fetch api return } if !cors.isOriginValid(c, origin) { c.AbortWithStatus(http.StatusForbidden) return } if c.Request.Method == http.MethodOptions { cors.handlePreflight(c) defer c.AbortWithStatus(cors.optionsResponseStatusCode) } else { cors.handleNormal(c) } if !cors.allowAllOrigins { c.Header("Access-Control-Allow-Origin", origin) } } func (cors *cors) validateWildcardOrigin(origin string) bool { for _, w := range cors.wildcardOrigins { if w[0] == "*" && strings.HasSuffix(origin, w[1]) { return true } if w[1] == "*" && strings.HasPrefix(origin, w[0]) { return true } if strings.HasPrefix(origin, w[0]) && strings.HasSuffix(origin, w[1]) { return true } } return false } func (cors *cors) isOriginValid(c *gin.Context, origin string) bool { valid := cors.validateOrigin(origin) if !valid && cors.allowOriginWithContextFunc != nil { valid = cors.allowOriginWithContextFunc(c, origin) } return valid } var originRegex = regexp.MustCompile(`^/(.+)/[gimuy]?$`) func (cors *cors) validateOrigin(origin string) bool { if cors.allowAllOrigins { return true } for _, value := range cors.allowOrigins { if !originRegex.MatchString(value) && value == origin { return true } if originRegex.MatchString(value) && regexp.MustCompile(originRegex.FindStringSubmatch(value)[1]).MatchString(origin) { return true } } if len(cors.wildcardOrigins) > 0 && cors.validateWildcardOrigin(origin) { return true } if cors.allowOriginFunc != nil { return cors.allowOriginFunc(origin) } return false } func (cors *cors) handlePreflight(c *gin.Context) { header := c.Writer.Header() for key, value := range cors.preflightHeaders { header[key] = value } } func (cors *cors) handleNormal(c *gin.Context) { header := c.Writer.Header() for key, value := range cors.normalHeaders { header[key] = value } } cors-1.7.6/cors.go000066400000000000000000000137371502527144000140030ustar00rootroot00000000000000package cors import ( "errors" "fmt" "regexp" "strings" "time" "github.com/gin-gonic/gin" ) // Config represents all available options for the middleware. type Config struct { AllowAllOrigins bool // AllowOrigins is a list of origins a cross-domain request can be executed from. // If the special "*" value is present in the list, all origins will be allowed. // Default value is [] AllowOrigins []string // AllowOriginFunc is a custom function to validate the origin. It takes the origin // as an argument and returns true if allowed or false otherwise. If this option is // set, the content of AllowOrigins is ignored. AllowOriginFunc func(origin string) bool // Same as AllowOriginFunc except also receives the full request context. // This function should use the context as a read only source and not // have any side effects on the request, such as aborting or injecting // values on the request. AllowOriginWithContextFunc func(c *gin.Context, origin string) bool // AllowMethods is a list of methods the client is allowed to use with // cross-domain requests. Default value is simple methods (GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS) AllowMethods []string // AllowPrivateNetwork indicates whether the response should include allow private network header AllowPrivateNetwork bool // AllowHeaders is list of non simple headers the client is allowed to use with // cross-domain requests. AllowHeaders []string // AllowCredentials indicates whether the request can include user credentials like // cookies, HTTP authentication or client side SSL certificates. AllowCredentials bool // ExposeHeaders indicates which headers are safe to expose to the API of a CORS // API specification ExposeHeaders []string // MaxAge indicates how long (with second-precision) the results of a preflight request // can be cached MaxAge time.Duration // Allows to add origins like http://some-domain/*, https://api.* or http://some.*.subdomain.com AllowWildcard bool // Allows usage of popular browser extensions schemas AllowBrowserExtensions bool // Allows to add custom schema like tauri:// CustomSchemas []string // Allows usage of WebSocket protocol AllowWebSockets bool // Allows usage of file:// schema (dangerous!) use it only when you 100% sure it's needed AllowFiles bool // Allows to pass custom OPTIONS response status code for old browsers / clients OptionsResponseStatusCode int } // AddAllowMethods is allowed to add custom methods func (c *Config) AddAllowMethods(methods ...string) { c.AllowMethods = append(c.AllowMethods, methods...) } // AddAllowHeaders is allowed to add custom headers func (c *Config) AddAllowHeaders(headers ...string) { c.AllowHeaders = append(c.AllowHeaders, headers...) } // AddExposeHeaders is allowed to add custom expose headers func (c *Config) AddExposeHeaders(headers ...string) { c.ExposeHeaders = append(c.ExposeHeaders, headers...) } func (c Config) getAllowedSchemas() []string { allowedSchemas := DefaultSchemas if c.AllowBrowserExtensions { allowedSchemas = append(allowedSchemas, ExtensionSchemas...) } if c.AllowWebSockets { allowedSchemas = append(allowedSchemas, WebSocketSchemas...) } if c.AllowFiles { allowedSchemas = append(allowedSchemas, FileSchemas...) } if c.CustomSchemas != nil { allowedSchemas = append(allowedSchemas, c.CustomSchemas...) } return allowedSchemas } var regexpBasedOrigin = regexp.MustCompile(`^\/(.+)\/[gimuy]?$`) func (c Config) validateAllowedSchemas(origin string) bool { allowedSchemas := c.getAllowedSchemas() if regexpBasedOrigin.MatchString(origin) { // Normalize regexp-based origins origin = regexpBasedOrigin.FindStringSubmatch(origin)[1] origin = strings.Replace(origin, "?", "", 1) } for _, schema := range allowedSchemas { if strings.HasPrefix(origin, schema) { return true } } return false } // Validate is check configuration of user defined. func (c Config) Validate() error { hasOriginFn := c.AllowOriginFunc != nil hasOriginFn = hasOriginFn || c.AllowOriginWithContextFunc != nil if c.AllowAllOrigins && (hasOriginFn || len(c.AllowOrigins) > 0) { originFields := strings.Join([]string{ "AllowOriginFunc", "AllowOriginFuncWithContext", "AllowOrigins", }, " or ") return fmt.Errorf( "conflict settings: all origins enabled. %s is not needed", originFields, ) } if !c.AllowAllOrigins && !hasOriginFn && len(c.AllowOrigins) == 0 { return errors.New("conflict settings: all origins disabled") } for _, origin := range c.AllowOrigins { if !strings.Contains(origin, "*") && !c.validateAllowedSchemas(origin) { return errors.New("bad origin: origins must contain '*' or include " + strings.Join(c.getAllowedSchemas(), ",")) } } return nil } func (c Config) parseWildcardRules() [][]string { var wRules [][]string if !c.AllowWildcard { return wRules } for _, o := range c.AllowOrigins { if !strings.Contains(o, "*") { continue } if c := strings.Count(o, "*"); c > 1 { panic(errors.New("only one * is allowed").Error()) } i := strings.Index(o, "*") if i == 0 { wRules = append(wRules, []string{"*", o[1:]}) continue } if i == (len(o) - 1) { wRules = append(wRules, []string{o[:i], "*"}) continue } wRules = append(wRules, []string{o[:i], o[i+1:]}) } return wRules } // DefaultConfig returns a generic default configuration mapped to localhost. func DefaultConfig() Config { return Config{ AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"}, AllowHeaders: []string{"Origin", "Content-Length", "Content-Type"}, AllowCredentials: false, MaxAge: 12 * time.Hour, } } // Default returns the location middleware with default configuration. func Default() gin.HandlerFunc { config := DefaultConfig() config.AllowAllOrigins = true return New(config) } // New returns the location middleware with user-defined custom configuration. func New(config Config) gin.HandlerFunc { cors := newCors(config) return func(c *gin.Context) { cors.applyCors(c) } } cors-1.7.6/cors_test.go000066400000000000000000000521741502527144000150400ustar00rootroot00000000000000package cors import ( "context" "net/http" "net/http/httptest" "reflect" "strings" "testing" "time" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" ) func newTestRouter(config Config) *gin.Engine { router := gin.New() router.Use(New(config)) router.GET("/", func(c *gin.Context) { c.String(http.StatusOK, "get") }) router.POST("/", func(c *gin.Context) { c.String(http.StatusOK, "post") }) router.PATCH("/", func(c *gin.Context) { c.String(http.StatusOK, "patch") }) return router } func multiGroupRouter(config Config) *gin.Engine { router := gin.New() router.Use(New(config)) app1 := router.Group("/app1") app1.GET("", func(c *gin.Context) { c.String(http.StatusOK, "app1") }) app2 := router.Group("/app2") app2.GET("", func(c *gin.Context) { c.String(http.StatusOK, "app2") }) app3 := router.Group("/app3") app3.GET("", func(c *gin.Context) { c.String(http.StatusOK, "app3") }) return router } func performRequest(r http.Handler, method, origin string) *httptest.ResponseRecorder { return performRequestWithHeaders(r, method, "/", origin, http.Header{}) } func performRequestWithHeaders(r http.Handler, method, path, origin string, header http.Header) *httptest.ResponseRecorder { req, _ := http.NewRequestWithContext(context.Background(), method, path, nil) req.Host = header.Get("Host") header.Del("Host") if origin != "" { header.Set("Origin", origin) } req.Header = header w := httptest.NewRecorder() r.ServeHTTP(w, req) return w } func TestConfigAddAllow(t *testing.T) { config := Config{} config.AddAllowMethods("POST") config.AddAllowMethods("GET", "PUT") config.AddExposeHeaders() config.AddAllowHeaders("Some", " cool") config.AddAllowHeaders("header") config.AddExposeHeaders() config.AddExposeHeaders() config.AddExposeHeaders("exposed", "header") config.AddExposeHeaders("hey") assert.Equal(t, []string{"POST", "GET", "PUT"}, config.AllowMethods) assert.Equal(t, []string{"Some", " cool", "header"}, config.AllowHeaders) assert.Equal(t, []string{"exposed", "header", "hey"}, config.ExposeHeaders) } func TestBadConfig(t *testing.T) { tests := []Config{ {}, {AllowAllOrigins: true, AllowOrigins: []string{"http://google.com"}}, {AllowAllOrigins: true, AllowOriginFunc: func(origin string) bool { return false }}, {AllowOrigins: []string{"google.com"}}, {AllowOrigins: []string{"/http://google.com"}}, {AllowOrigins: []string{"http?://google.com"}}, {AllowOrigins: []string{"http?://google.com/g"}}, } for _, cfg := range tests { assert.Panics(t, func() { New(cfg) }) } } func TestNormalize(t *testing.T) { assert.Equal(t, []string{"http-access", "post", ""}, normalize([]string{ "http-Access ", "Post", "POST", " poSt ", "HTTP-Access", "", })) assert.Nil(t, normalize(nil)) assert.Equal(t, []string{}, normalize([]string{})) } func TestConvert(t *testing.T) { methods := []string{"Get", "GET", "get"} headers := []string{"X-CSRF-TOKEN", "X-CSRF-Token", "x-csrf-token"} assert.Equal(t, []string{"GET", "GET", "GET"}, convert(methods, strings.ToUpper)) assert.Equal(t, []string{"X-Csrf-Token", "X-Csrf-Token", "X-Csrf-Token"}, convert(headers, http.CanonicalHeaderKey)) } func TestGenerateNormalHeaders(t *testing.T) { tests := []struct { name string config Config expect map[string]string len int }{ { "AllowAllOrigins false", Config{AllowAllOrigins: false}, map[string]string{"Access-Control-Allow-Origin": "", "Vary": "Origin"}, 1, }, { "AllowAllOrigins true", Config{AllowAllOrigins: true}, map[string]string{"Access-Control-Allow-Origin": "*", "Vary": ""}, 1, }, { "AllowCredentials true", Config{AllowCredentials: true}, map[string]string{"Access-Control-Allow-Credentials": "true", "Vary": "Origin"}, 2, }, { "ExposeHeaders set", Config{ExposeHeaders: []string{"X-user", "xPassword"}}, map[string]string{"Access-Control-Expose-Headers": "X-User,Xpassword", "Vary": "Origin"}, 2, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { header := generateNormalHeaders(tt.config) for k, v := range tt.expect { assert.Equal(t, v, header.Get(k)) } assert.Len(t, header, tt.len) }) } } func TestGeneratePreflightHeaders(t *testing.T) { tests := []struct { name string config Config expect map[string]string len int }{ { "AllowAllOrigins false", Config{AllowAllOrigins: false}, map[string]string{"Access-Control-Allow-Origin": "", "Vary": "Origin"}, 1, }, { "AllowAllOrigins true", Config{AllowAllOrigins: true}, map[string]string{"Access-Control-Allow-Origin": "*", "Vary": ""}, 1, }, { "AllowCredentials true", Config{AllowCredentials: true}, map[string]string{"Access-Control-Allow-Credentials": "true", "Vary": "Origin"}, 2, }, { "AllowPrivateNetwork true", Config{AllowPrivateNetwork: true}, map[string]string{"Access-Control-Allow-Private-Network": "true", "Vary": "Origin"}, 2, }, { "AllowMethods set", Config{AllowMethods: []string{"GET ", "post", "PUT", " put "}}, map[string]string{"Access-Control-Allow-Methods": "GET,POST,PUT", "Vary": "Origin"}, 2, }, { "AllowHeaders set", Config{AllowHeaders: []string{"X-user", "Content-Type"}}, map[string]string{"Access-Control-Allow-Headers": "X-User,Content-Type", "Vary": "Origin"}, 2, }, { "MaxAge set", Config{MaxAge: 12 * time.Hour}, map[string]string{"Access-Control-Max-Age": "43200", "Vary": "Origin"}, 2, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { header := generatePreflightHeaders(tt.config) for k, v := range tt.expect { assert.Equal(t, v, header.Get(k)) } assert.Len(t, header, tt.len) }) } } func TestValidateOrigin(t *testing.T) { type originTest struct { config Config origins map[string]bool } tests := []originTest{ { Config{AllowAllOrigins: true}, map[string]bool{ "http://google.com": true, "https://google.com": true, "example.com": true, "chrome-extension://random-extension-id": true, }, }, { Config{ AllowOrigins: []string{"https://google.com", "https://github.com"}, AllowOriginFunc: func(origin string) bool { return origin == "http://news.ycombinator.com" }, AllowBrowserExtensions: true, }, map[string]bool{ "http://google.com": false, "https://google.com": true, "https://github.com": true, "http://news.ycombinator.com": true, "http://example.com": false, "google.com": false, "chrome-extension://random-extension-id": false, }, }, { Config{AllowOrigins: []string{"https://google.com", "https://github.com"}}, map[string]bool{ "chrome-extension://random-extension-id": false, "file://some-dangerous-file.js": false, "wss://socket-connection": false, }, }, { Config{ AllowOrigins: []string{ "chrome-extension://*", "safari-extension://my-extension-*-app", "*.some-domain.com", }, AllowBrowserExtensions: true, AllowWildcard: true, }, map[string]bool{ "chrome-extension://random-extension-id": true, "chrome-extension://another-one": true, "safari-extension://my-extension-one-app": true, "safari-extension://my-extension-two-app": true, "moz-extension://ext-id-we-not-allow": false, "http://api.some-domain.com": true, "http://api.another-domain.com": false, }, }, { Config{ AllowOrigins: []string{"file://safe-file.js", "wss://some-session-layer-connection"}, AllowFiles: true, AllowWebSockets: true, }, map[string]bool{ "file://safe-file.js": true, "file://some-dangerous-file.js": false, "wss://some-session-layer-connection": true, "ws://not-what-we-expected": false, }, }, { Config{AllowOrigins: []string{"*"}}, map[string]bool{ "http://google.com": true, "https://google.com": true, "example.com": true, "chrome-extension://random-extension-id": true, }, }, { Config{AllowOrigins: []string{"/https?://(?:.+\\.)?google\\.com/g"}}, map[string]bool{ "http://google.com": true, "https://google.com": true, "https://maps.google.com": true, "https://maps.test.google.com": true, "https://maps.google.it": false, }, }, } for i, test := range tests { cors := newCors(test.config) for origin, want := range test.origins { got := cors.validateOrigin(origin) assert.Equalf(t, want, got, "case %d: origin=%s", i, origin) } } } func TestValidateTauri(t *testing.T) { c := Config{ AllowOrigins: []string{"tauri://localhost:1234"}, AllowBrowserExtensions: true, } assert.Error(t, c.Validate()) c = Config{ AllowOrigins: []string{"tauri://localhost:1234"}, AllowBrowserExtensions: true, CustomSchemas: []string{"tauri"}, } assert.Nil(t, c.Validate()) } func TestDefaultConfig(t *testing.T) { config := DefaultConfig() config.AllowAllOrigins = true router := newTestRouter(config) w := performRequest(router, "GET", "http://google.com") assert.Equal(t, "get", w.Body.String()) assert.Equal(t, "*", w.Header().Get("Access-Control-Allow-Origin")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Empty(t, w.Header().Get("Access-Control-Expose-Headers")) } func TestCORS_AllowOrigins_NoOrigin(t *testing.T) { router := newTestRouter(Config{ AllowOrigins: []string{"http://google.com"}, AllowMethods: []string{" GeT ", "get", "post", "PUT ", "Head", "POST"}, AllowHeaders: []string{"Content-type", "timeStamp "}, ExposeHeaders: []string{"Data", "x-User"}, AllowCredentials: false, MaxAge: 12 * time.Hour, AllowOriginFunc: func(origin string) bool { return origin == "http://github.com" }, AllowOriginWithContextFunc: func(c *gin.Context, origin string) bool { return origin == "http://sample.com" }, }) w := performRequest(router, "GET", "") assert.Equal(t, "get", w.Body.String()) assert.Empty(t, w.Header().Get("Access-Control-Allow-Origin")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Empty(t, w.Header().Get("Access-Control-Expose-Headers")) } func TestCORS_AllowOrigins_OriginIsHost(t *testing.T) { router := newTestRouter(Config{ AllowOrigins: []string{"http://google.com"}, AllowMethods: []string{" GeT ", "get", "post", "PUT ", "Head", "POST"}, AllowHeaders: []string{"Content-type", "timeStamp "}, ExposeHeaders: []string{"Data", "x-User"}, AllowCredentials: false, MaxAge: 12 * time.Hour, AllowOriginFunc: func(origin string) bool { return origin == "http://github.com" }, AllowOriginWithContextFunc: func(c *gin.Context, origin string) bool { return origin == "http://sample.com" }, }) h := http.Header{} h.Set("Host", "facebook.com") w := performRequestWithHeaders(router, "GET", "/", "http://facebook.com", h) assert.Equal(t, "get", w.Body.String()) assert.Empty(t, w.Header().Get("Access-Control-Allow-Origin")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Empty(t, w.Header().Get("Access-Control-Expose-Headers")) } func TestCORS_AllowOrigins_AllowedOrigin(t *testing.T) { router := newTestRouter(Config{ AllowOrigins: []string{"http://google.com"}, AllowMethods: []string{" GeT ", "get", "post", "PUT ", "Head", "POST"}, AllowHeaders: []string{"Content-type", "timeStamp "}, ExposeHeaders: []string{"Data", "x-User"}, AllowCredentials: false, MaxAge: 12 * time.Hour, AllowOriginFunc: func(origin string) bool { return origin == "http://github.com" }, AllowOriginWithContextFunc: func(c *gin.Context, origin string) bool { return origin == "http://sample.com" }, }) tests := []struct { origin, wantExpose string }{ {"http://google.com", "Data,X-User"}, {"http://github.com", "Data,X-User"}, } for _, tt := range tests { w := performRequest(router, "GET", tt.origin) assert.Equal(t, "get", w.Body.String()) assert.Equal(t, tt.origin, w.Header().Get("Access-Control-Allow-Origin")) assert.Equal(t, "", w.Header().Get("Access-Control-Allow-Credentials")) assert.Equal(t, tt.wantExpose, w.Header().Get("Access-Control-Expose-Headers")) } } func TestCORS_AllowOrigins_DeniedOrigin(t *testing.T) { router := newTestRouter(Config{ AllowOrigins: []string{"http://google.com"}, AllowMethods: []string{" GeT ", "get", "post", "PUT ", "Head", "POST"}, AllowHeaders: []string{"Content-type", "timeStamp "}, ExposeHeaders: []string{"Data", "x-User"}, AllowCredentials: false, MaxAge: 12 * time.Hour, AllowOriginFunc: func(origin string) bool { return origin == "http://github.com" }, AllowOriginWithContextFunc: func(c *gin.Context, origin string) bool { return origin == "http://sample.com" }, }) w := performRequest(router, "GET", "https://google.com") assert.Equal(t, http.StatusForbidden, w.Code) assert.Empty(t, w.Header().Get("Access-Control-Allow-Origin")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Empty(t, w.Header().Get("Access-Control-Expose-Headers")) } func TestCORS_AllowOrigins_Preflight(t *testing.T) { router := newTestRouter(Config{ AllowOrigins: []string{"http://google.com"}, AllowMethods: []string{" GeT ", "get", "post", "PUT ", "Head", "POST"}, AllowHeaders: []string{"Content-type", "timeStamp "}, ExposeHeaders: []string{"Data", "x-User"}, AllowCredentials: false, MaxAge: 12 * time.Hour, AllowOriginFunc: func(origin string) bool { return origin == "http://github.com" }, AllowOriginWithContextFunc: func(c *gin.Context, origin string) bool { return origin == "http://sample.com" }, }) tests := []string{"http://github.com", "http://sample.com"} for _, origin := range tests { w := performRequest(router, "OPTIONS", origin) assert.Equal(t, http.StatusNoContent, w.Code) assert.Equal(t, origin, w.Header().Get("Access-Control-Allow-Origin")) assert.Equal(t, "", w.Header().Get("Access-Control-Allow-Credentials")) assert.Equal(t, "GET,POST,PUT,HEAD", w.Header().Get("Access-Control-Allow-Methods")) assert.Equal(t, "Content-Type,Timestamp", w.Header().Get("Access-Control-Allow-Headers")) assert.Equal(t, "43200", w.Header().Get("Access-Control-Max-Age")) } } func TestCORS_AllowOrigins_DeniedPreflight(t *testing.T) { router := newTestRouter(Config{ AllowOrigins: []string{"http://google.com"}, AllowMethods: []string{" GeT ", "get", "post", "PUT ", "Head", "POST"}, AllowHeaders: []string{"Content-type", "timeStamp "}, ExposeHeaders: []string{"Data", "x-User"}, AllowCredentials: false, MaxAge: 12 * time.Hour, AllowOriginFunc: func(origin string) bool { return origin == "http://github.com" }, AllowOriginWithContextFunc: func(c *gin.Context, origin string) bool { return origin == "http://sample.com" }, }) w := performRequest(router, "OPTIONS", "http://example.com") assert.Equal(t, http.StatusForbidden, w.Code) assert.Empty(t, w.Header().Get("Access-Control-Allow-Origin")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Methods")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Headers")) assert.Empty(t, w.Header().Get("Access-Control-Max-Age")) } func TestPassesAllowAllOrigins(t *testing.T) { router := newTestRouter(Config{ AllowAllOrigins: true, AllowMethods: []string{" Patch ", "get", "post", "POST"}, AllowHeaders: []string{"Content-type", " testheader "}, ExposeHeaders: []string{"Data2", "x-User2"}, AllowCredentials: false, MaxAge: 10 * time.Hour, }) w := performRequest(router, "GET", "") assert.Equal(t, "get", w.Body.String()) assert.Empty(t, w.Header().Get("Access-Control-Allow-Origin")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) assert.Empty(t, w.Header().Get("Access-Control-Expose-Headers")) w = performRequest(router, "POST", "example.com") assert.Equal(t, "post", w.Body.String()) assert.Equal(t, "*", w.Header().Get("Access-Control-Allow-Origin")) assert.Equal(t, "Data2,X-User2", w.Header().Get("Access-Control-Expose-Headers")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) w = performRequest(router, "OPTIONS", "https://facebook.com") assert.Equal(t, http.StatusNoContent, w.Code) assert.Equal(t, "*", w.Header().Get("Access-Control-Allow-Origin")) assert.Equal(t, "PATCH,GET,POST", w.Header().Get("Access-Control-Allow-Methods")) assert.Equal(t, "Content-Type,Testheader", w.Header().Get("Access-Control-Allow-Headers")) assert.Equal(t, "36000", w.Header().Get("Access-Control-Max-Age")) assert.Empty(t, w.Header().Get("Access-Control-Allow-Credentials")) } func TestWildcard(t *testing.T) { router := newTestRouter(Config{ AllowOrigins: []string{"https://*.github.com", "https://api.*", "http://*", "https://facebook.com", "*.golang.org"}, AllowMethods: []string{"GET"}, AllowWildcard: true, }) tests := []struct { origin string code int }{ {"https://gist.github.com", 200}, {"https://api.github.com/v1/users", 200}, {"https://giphy.com/", 403}, {"http://hard-to-find-http-example.com", 200}, {"https://facebook.com", 200}, {"https://something.golang.org", 200}, {"https://something.go.org", 403}, } for _, tt := range tests { w := performRequest(router, "GET", tt.origin) assert.Equal(t, tt.code, w.Code) } router = newTestRouter(Config{ AllowOrigins: []string{"https://github.com", "https://facebook.com"}, AllowMethods: []string{"GET"}, }) tests2 := []struct { origin string code int }{ {"https://gist.github.com", 403}, {"https://github.com", 200}, } for _, tt := range tests2 { w := performRequest(router, "GET", tt.origin) assert.Equal(t, tt.code, w.Code) } } func TestMultiGroupRouter(t *testing.T) { router := multiGroupRouter(Config{ AllowMethods: []string{"GET"}, AllowOriginWithContextFunc: func(c *gin.Context, origin string) bool { path := c.Request.URL.Path switch { case strings.HasPrefix(path, "/app1"): return origin == "http://app1.example.com" case strings.HasPrefix(path, "/app2"): return origin == "http://app2.example.com" default: return true } }, }) emptyHeaders := http.Header{} app1Origin := "http://app1.example.com" app2Origin := "http://app2.example.com" randomOrigin := "http://random.com" tests := []struct { method, path, origin string code int }{ {"OPTIONS", "/app1", app1Origin, http.StatusNoContent}, {"OPTIONS", "/app2", app2Origin, http.StatusNoContent}, {"OPTIONS", "/app3", randomOrigin, http.StatusNoContent}, {"OPTIONS", "/app1", randomOrigin, http.StatusForbidden}, {"OPTIONS", "/app2", randomOrigin, http.StatusForbidden}, } for _, tt := range tests { w := performRequestWithHeaders(router, tt.method, tt.path, tt.origin, emptyHeaders) assert.Equal(t, tt.code, w.Code) } } func TestParseWildcardRules_NoWildcard(t *testing.T) { config := Config{ AllowOrigins: []string{ "http://example.com", "https://google.com", "github.com", }, AllowWildcard: false, } assert.Equal(t, [][]string(nil), config.parseWildcardRules()) } func TestParseWildcardRules_InvalidWildcard(t *testing.T) { config := Config{ AllowOrigins: []string{ "http://example.com", "https://*.google.com*", "*.github.com*", }, AllowWildcard: true, } assert.Panics(t, func() { config.parseWildcardRules() }) } func TestParseWildcardRules(t *testing.T) { tests := []struct { name string config Config expectedResult [][]string expectPanic bool }{ { "Wildcard not allowed", Config{AllowWildcard: false, AllowOrigins: []string{"http://example.com", "https://*.domain.com"}}, nil, false, }, { "No wildcards", Config{AllowWildcard: true, AllowOrigins: []string{"http://example.com", "https://example.com"}}, nil, false, }, { "Single wildcard at the end", Config{AllowWildcard: true, AllowOrigins: []string{"http://*.example.com"}}, [][]string{{"http://", ".example.com"}}, false, }, { "Single wildcard at the beginning", Config{AllowWildcard: true, AllowOrigins: []string{"*.example.com"}}, [][]string{{"*", ".example.com"}}, false, }, { "Single wildcard in the middle", Config{AllowWildcard: true, AllowOrigins: []string{"http://example.*.com"}}, [][]string{{"http://example.", ".com"}}, false, }, { "Multiple wildcards should panic", Config{AllowWildcard: true, AllowOrigins: []string{"http://*.*.com"}}, nil, true, }, { "Single wildcard in the end", Config{AllowWildcard: true, AllowOrigins: []string{"http://example.com/*"}}, [][]string{{"http://example.com/", "*"}}, false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.expectPanic { defer func() { if r := recover(); r == nil { t.Errorf("The code did not panic") } }() } result := tt.config.parseWildcardRules() if !tt.expectPanic && !reflect.DeepEqual(result, tt.expectedResult) { t.Errorf("Expected %v, got %v", tt.expectedResult, result) } }) } } cors-1.7.6/go.mod000066400000000000000000000031521502527144000136020ustar00rootroot00000000000000module github.com/gin-contrib/cors go 1.23.0 require ( github.com/gin-gonic/gin v1.10.1 github.com/stretchr/testify v1.10.0 ) require ( github.com/bytedance/sonic v1.13.3 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect github.com/cloudwego/base64x v0.1.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.9 // indirect github.com/gin-contrib/sse v1.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.26.0 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.0 // indirect golang.org/x/arch v0.18.0 // indirect golang.org/x/crypto v0.39.0 // indirect golang.org/x/net v0.41.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.26.0 // indirect google.golang.org/protobuf v1.36.6 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) cors-1.7.6/go.sum000066400000000000000000000210211502527144000136220ustar00rootroot00000000000000github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0= github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc= golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= cors-1.7.6/utils.go000066400000000000000000000045431502527144000141700ustar00rootroot00000000000000package cors import ( "net/http" "strconv" "strings" "time" ) type converter func(string) string func generateNormalHeaders(c Config) http.Header { headers := make(http.Header) if c.AllowCredentials { headers.Set("Access-Control-Allow-Credentials", "true") } if len(c.ExposeHeaders) > 0 { exposeHeaders := convert(normalize(c.ExposeHeaders), http.CanonicalHeaderKey) headers.Set("Access-Control-Expose-Headers", strings.Join(exposeHeaders, ",")) } if c.AllowAllOrigins { headers.Set("Access-Control-Allow-Origin", "*") } else { headers.Set("Vary", "Origin") } return headers } func generatePreflightHeaders(c Config) http.Header { headers := make(http.Header) if c.AllowCredentials { headers.Set("Access-Control-Allow-Credentials", "true") } if len(c.AllowMethods) > 0 { allowMethods := convert(normalize(c.AllowMethods), strings.ToUpper) value := strings.Join(allowMethods, ",") headers.Set("Access-Control-Allow-Methods", value) } if len(c.AllowHeaders) > 0 { allowHeaders := convert(normalize(c.AllowHeaders), http.CanonicalHeaderKey) value := strings.Join(allowHeaders, ",") headers.Set("Access-Control-Allow-Headers", value) } if c.MaxAge > time.Duration(0) { value := strconv.FormatInt(int64(c.MaxAge/time.Second), 10) headers.Set("Access-Control-Max-Age", value) } if c.AllowPrivateNetwork { headers.Set("Access-Control-Allow-Private-Network", "true") } if c.AllowAllOrigins { headers.Set("Access-Control-Allow-Origin", "*") } else { // Always set Vary headers // see https://github.com/rs/cors/issues/10, // https://github.com/rs/cors/commit/dbdca4d95feaa7511a46e6f1efb3b3aa505bc43f#commitcomment-12352001 headers.Add("Vary", "Origin") headers.Add("Vary", "Access-Control-Request-Method") headers.Add("Vary", "Access-Control-Request-Headers") } return headers } func normalize(values []string) []string { if values == nil { return nil } distinctMap := make(map[string]bool, len(values)) normalized := make([]string, 0, len(values)) for _, value := range values { value = strings.TrimSpace(value) value = strings.ToLower(value) if _, seen := distinctMap[value]; !seen { normalized = append(normalized, value) distinctMap[value] = true } } return normalized } func convert(s []string, c converter) []string { var out []string for _, i := range s { out = append(out, c(i)) } return out }