From fc3fe750d477f8f737ccf30a3b2d72ac09c72c93 Mon Sep 17 00:00:00 2001 From: Rene Nochebuena Guerrero Date: Sat, 8 Aug 2026 01:26:17 -0600 Subject: [PATCH] docs(web): document CORS wildcard rejection + env-gated convention; align to v1.1.3 --- CHANGELOG.md | 14 ++++++++++++++ README.md | 2 +- go.mod | 4 ++-- go.sum | 8 ++++---- mw/cors.go | 30 ++++++++++++++++++++++-------- 5 files changed, 43 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b08dd58..b9df785 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,20 @@ This module adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html --- +## [1.1.3] — 2026-08-08 + +Patch — CORS documentation discoverability. + +### Fixed + +- `mw.CORS` and `CORSAllowAll` doc comments now document the `"*"` rejection (panic) and the + env-gated CORS convention (`local -> CORSAllowAll`, else `mw.CORS(origins)`), so `search_symbols` + surfaces it — previously the convention lived only in code comments and the wire example. + +### Changed + +- Bumped `contracts`, `core` to v1.1.3. + ## [1.1.2] — 2026-08-08 Patch — CORS wildcard hardening plus documentation fixes. diff --git a/README.md b/README.md index 6894cad..435aeac 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # einherjar/web -[![version](https://img.shields.io/badge/version-v1.1.2-5C4EE5?style=flat-square)](https://code.nochebuena.dev/einherjar/web) +[![version](https://img.shields.io/badge/version-v1.1.3-5C4EE5?style=flat-square)](https://code.nochebuena.dev/einherjar/web) [![license](https://img.shields.io/badge/license-AGPL--3.0-22863A?style=flat-square)](LICENSE) [![go](https://img.shields.io/badge/Go-1.26+-00ADD8?style=flat-square&logo=go&logoColor=white)](https://go.dev) diff --git a/go.mod b/go.mod index d790fa1..a5107d5 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module code.nochebuena.dev/einherjar/web go 1.26 require ( - code.nochebuena.dev/einherjar/contracts v1.1.2 - code.nochebuena.dev/einherjar/core v1.1.2 + code.nochebuena.dev/einherjar/contracts v1.1.3 + code.nochebuena.dev/einherjar/core v1.1.3 github.com/go-chi/chi/v5 v5.2.1 github.com/google/uuid v1.6.0 golang.org/x/time v0.11.0 diff --git a/go.sum b/go.sum index 50bdb13..ee229f3 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -code.nochebuena.dev/einherjar/contracts v1.1.2 h1:LNAFCKQjpNjkCyMid2AgkhDPPzOx8dvpRqqZ7F3zpo0= -code.nochebuena.dev/einherjar/contracts v1.1.2/go.mod h1:ccltUtrFb5+MEJdkx2VVEUL+xC5pupVlVVsMM8AlCWI= -code.nochebuena.dev/einherjar/core v1.1.2 h1:iYU2fIWtnyOYFbTMD92NaLJMye4XgZHXWhEjB3iS7lo= -code.nochebuena.dev/einherjar/core v1.1.2/go.mod h1:Y7qZ9nri9Ydey3+40yz75KErOvOUjRSoffdykXbncsw= +code.nochebuena.dev/einherjar/contracts v1.1.3 h1:rBtQUVCeqaIKMcG1+R0ndHM/4cRm3XTFnVNzFTbf1QU= +code.nochebuena.dev/einherjar/contracts v1.1.3/go.mod h1:ccltUtrFb5+MEJdkx2VVEUL+xC5pupVlVVsMM8AlCWI= +code.nochebuena.dev/einherjar/core v1.1.3 h1:MjWUA/hJ5IOosACh0in9xzFZ7jc1TTO0YEHh1xQVuOg= +code.nochebuena.dev/einherjar/core v1.1.3/go.mod h1:xkI2uQ4S0FQHbFiawTUFgJdG5jbivcZpGFddHxQe8Q0= 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.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= diff --git a/mw/cors.go b/mw/cors.go index 64ea96c..216bbf9 100644 --- a/mw/cors.go +++ b/mw/cors.go @@ -7,13 +7,23 @@ const ( allowedHeaders = "Content-Type, Authorization, X-Request-ID" ) -// CORS sets cross-origin resource sharing headers for the provided origins. -// Returns 204 No Content for OPTIONS preflight requests. -// Pass the outermost origins first; an empty slice is a no-op. +// CORS sets cross-origin resource sharing headers for the provided origins +// (exact match; an empty slice is a no-op). Returns 204 No Content for OPTIONS +// preflight requests. +// +// It panics on "*": a wildcard matches no real Origin here, so passing it would +// silently disable CORS. For allow-all use [CORSAllowAll] (development only). The +// recommended wiring gates CORS by environment: +// +// var corsMW func(http.Handler) http.Handler +// if strings.EqualFold(cfg.AppEnv, "local") { +// corsMW = mw.CORSAllowAll() // dev: any origin +// } else { +// corsMW = mw.CORS(cfg.AllowedOrigins) // prod: explicit origins +// } func CORS(origins []string) func(http.Handler) http.Handler { - // "*" is a silent no-op here (exact-match only) — reject it loudly at - // construction so a misconfigured service fails to boot instead of quietly - // blocking every browser. For allow-all, call CORSAllowAll (development only). + // "*" would be a silent no-op (exact-match only) — reject it loudly so a + // misconfigured service fails to boot instead of quietly blocking browsers. for _, o := range origins { if o == "*" { panic(`mw.CORS: "*" is not a valid origin — list explicit origins, or use CORSAllowAll() for allow-all`) @@ -46,8 +56,12 @@ func CORS(origins []string) func(http.Handler) http.Handler { } } -// CORSAllowAll is a convenience wrapper that allows any origin. -// Use only in development — never in production. +// CORSAllowAll allows any origin by reflecting the request Origin (it does not set +// Access-Control-Allow-Credentials). Development only — never in production. +// +// Use it for the local branch of the env-gated CORS convention; use [CORS] with +// explicit origins everywhere else. Because [CORS] panics on "*", CORSAllowAll — not +// a "*" in the origins list — is the way to allow all. func CORSAllowAll() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {