Authorization Bypass Through User-Controlled Key in emicklei/go-restful
Description
Authorization Bypass Through User-Controlled Key in GitHub repository emicklei/go-restful prior to v3.8.0.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
CVE-2022-1996 is an authorization bypass in go-restful's CORS filter that matches allowed domains as regex patterns, enabling attackers to bypass origin restrictions.
Vulnerability
Description CVE-2022-1996 is an authorization bypass vulnerability in the CORS filter of the emicklei/go-restful library for Go, affecting versions prior to 3.8.0. The root cause is that the AllowedDomains configuration option, intended to specify a list of allowed origin domains for CORS policy, is incorrectly compiled and matched as regular expressions rather than as exact strings [3]. This happens in cors_filter.go where AllowedDomains entries are compiled into regex patterns used to match the Origin header [1][3]. Consequently, a domain entry like example.com becomes a regex pattern that matches any origin containing example.com as a substring (e.g., evil-example.com or example.com.attacker.com).
Exploitation
Method An attacker can exploit this flaw by sending HTTP requests to a target application that uses go-restful's CORS filter with a malicious Origin header crafted to match the regex derived from a trusted domain. No authentication is required, and the attack can be carried out from a browser or any HTTP client [3]. The vulnerability is particularly dangerous in scenarios where CORS is used to restrict cross-origin requests to trusted origins, as the attacker's origin may be incorrectly allowed, bypassing the intended security controls.
Impact
Successful exploitation allows an attacker to bypass CORS origin validation, enabling cross-origin read access to sensitive resources and potentially leading to data exfiltration or session hijacking [3]. The vulnerability is classified as an authorization bypass (CWE-639) with a CVSS score reported by the NVD as modified after enrichment [1].
Mitigation
The issue was fixed in version 3.8.0 of go-restful. The patch (commit fd3c327) changed the matching logic to use exact string comparison for AllowedDomains and introduced a new AllowedDomainFunc callback for custom matching [4]. Users should upgrade to v3.8.0 or later. As of the publication date, no workaround is documented beyond upgrading [1][4].
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/emicklei/go-restful/v3Go | >= 3.0.0, < 3.8.0 | 3.8.0 |
github.com/emicklei/go-restfulGo | < 2.16.0 | 2.16.0 |
github.com/emicklei/go-restful/v2Go | <= 2.7.1 | — |
Affected products
133- osv-coords132 versionspkg:apk/chainguard/kubeflowpkg:apk/chainguard/kubeflow-access-managementpkg:apk/chainguard/kubeflow-access-management-compatpkg:apk/chainguard/kubeflow-access-management-fipspkg:apk/chainguard/kubeflow-access-management-fips-compatpkg:apk/chainguard/kubeflow-admission-webhookpkg:apk/chainguard/kubeflow-admission-webhook-compatpkg:apk/chainguard/kubeflow-admission-webhook-fipspkg:apk/chainguard/kubeflow-admission-webhook-fips-compatpkg:apk/chainguard/kubeflow-fipspkg:apk/chainguard/kubeflow-notebook-controllerpkg:apk/chainguard/kubeflow-notebook-controller-compatpkg:apk/chainguard/kubeflow-notebook-controller-fipspkg:apk/chainguard/kubeflow-notebook-controller-fips-compatpkg:apk/chainguard/kubeflow-profile-controllerpkg:apk/chainguard/kubeflow-profile-controller-compatpkg:apk/chainguard/kubeflow-profile-controller-fipspkg:apk/chainguard/kubeflow-profile-controller-fips-compatpkg:apk/chainguard/kubeflow-pvcviewer-controllerpkg:apk/chainguard/kubeflow-pvcviewer-controller-compatpkg:apk/chainguard/kubeflow-pvcviewer-controller-fipspkg:apk/chainguard/kubeflow-pvcviewer-controller-fips-compatpkg:apk/chainguard/kubeflow-tensorboard-controllerpkg:apk/chainguard/kubeflow-tensorboard-controller-compatpkg:apk/chainguard/kubeflow-tensorboard-controller-fipspkg:apk/chainguard/kubeflow-tensorboard-controller-fips-compatpkg:apk/chainguard/kube-state-metrics-2.6pkg:apk/chainguard/py3-seldon-core-1.16pkg:apk/chainguard/seldon-core-operator-1.16pkg:apk/chainguard/seldon-core-operator-compatpkg:apk/chainguard/seldon-core-operator-compat-helmpkg:apk/chainguard/seldon-core-operator-fipspkg:apk/wolfi/kubeflowpkg:apk/wolfi/kubeflow-access-managementpkg:apk/wolfi/kubeflow-access-management-compatpkg:apk/wolfi/kubeflow-admission-webhookpkg:apk/wolfi/kubeflow-admission-webhook-compatpkg:apk/wolfi/kubeflow-notebook-controllerpkg:apk/wolfi/kubeflow-notebook-controller-compatpkg:apk/wolfi/kubeflow-profile-controllerpkg:apk/wolfi/kubeflow-profile-controller-compatpkg:apk/wolfi/kubeflow-pvcviewer-controllerpkg:apk/wolfi/kubeflow-pvcviewer-controller-compatpkg:apk/wolfi/kubeflow-tensorboard-controllerpkg:apk/wolfi/kubeflow-tensorboard-controller-compatpkg:golang/github.com/emicklei/go-restfulpkg:golang/github.com/emicklei/go-restful/v2pkg:golang/github.com/emicklei/go-restful/v3pkg:rpm/opensuse/aws-iam-authenticator&distro=openSUSE%20Leap%2015.5pkg:rpm/opensuse/aws-iam-authenticator&distro=openSUSE%20Leap%2015.6pkg:rpm/opensuse/containerd&distro=openSUSE%20Leap%2015.4pkg:rpm/opensuse/containerd&distro=openSUSE%20Leap%2015.5pkg:rpm/opensuse/containerd&distro=openSUSE%20Leap%2015.6pkg:rpm/opensuse/containerd&distro=openSUSE%20Leap%20Micro%205.3pkg:rpm/opensuse/containerd&distro=openSUSE%20Leap%20Micro%205.4pkg:rpm/opensuse/containerd&distro=openSUSE%20Leap%20Micro%205.5pkg:rpm/opensuse/containerized-data-importer&distro=openSUSE%20Leap%2015.3pkg:rpm/opensuse/containerized-data-importer&distro=openSUSE%20Leap%2015.4pkg:rpm/opensuse/helm&distro=openSUSE%20Leap%2015.3pkg:rpm/opensuse/helm&distro=openSUSE%20Leap%2015.4pkg:rpm/opensuse/kubevirt&distro=openSUSE%20Leap%2015.3pkg:rpm/opensuse/kubevirt&distro=openSUSE%20Leap%2015.4pkg:rpm/opensuse/runc&distro=openSUSE%20Leap%2015.4pkg:rpm/opensuse/runc&distro=openSUSE%20Leap%2015.5pkg:rpm/opensuse/runc&distro=openSUSE%20Leap%20Micro%205.3pkg:rpm/opensuse/runc&distro=openSUSE%20Leap%20Micro%205.4pkg:rpm/opensuse/trivy&distro=openSUSE%20Leap%2015.3pkg:rpm/opensuse/trivy&distro=openSUSE%20Leap%2015.4pkg:rpm/suse/aws-iam-authenticator&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Public%20Cloud%2015%20SP2pkg:rpm/suse/aws-iam-authenticator&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Public%20Cloud%2015%20SP3pkg:rpm/suse/aws-iam-authenticator&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Public%20Cloud%2015%20SP4pkg:rpm/suse/aws-iam-authenticator&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Public%20Cloud%2015%20SP5pkg:rpm/suse/aws-iam-authenticator&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Public%20Cloud%2015%20SP6pkg:rpm/suse/catatonit&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-LTSSpkg:rpm/suse/catatonit&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP2-LTSSpkg:rpm/suse/catatonit&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-LTSSpkg:rpm/suse/catatonit&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP2-LTSSpkg:rpm/suse/catatonit&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP1pkg:rpm/suse/catatonit&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP2pkg:rpm/suse/containerd&distro=SUSE%20Enterprise%20Storage%207.1pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP2-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP3-ESPOSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP3-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP4-ESPOSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP4-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Micro%205.1pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Micro%205.2pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Micro%205.3pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Micro%205.4pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Micro%205.5pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2012pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP4pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP5pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP6pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP2-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP3-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP4-LTSSpkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP1pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP2pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP3pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP4pkg:rpm/suse/containerd&distro=SUSE%20Linux%20Micro%206.0pkg:rpm/suse/containerized-data-importer&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP3pkg:rpm/suse/containerized-data-importer&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP4pkg:rpm/suse/helm&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP3pkg:rpm/suse/helm&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP4pkg:rpm/suse/helm&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Package%20Hub%2015%20SP3pkg:rpm/suse/helm&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Package%20Hub%2015%20SP4pkg:rpm/suse/kubevirt&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP3pkg:rpm/suse/kubevirt&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP4pkg:rpm/suse/runc&distro=SUSE%20Enterprise%20Storage%207.1pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP1-LTSSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP2-LTSSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP3-ESPOSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20High%20Performance%20Computing%2015%20SP3-LTSSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Micro%205.1pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Micro%205.2pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Micro%205.3pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Micro%205.4pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Micro%205.5pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP4pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Module%20for%20Containers%2015%20SP5pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP1-LTSSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP2-LTSSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Server%2015%20SP3-LTSSpkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP1pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP2pkg:rpm/suse/runc&distro=SUSE%20Linux%20Enterprise%20Server%20for%20SAP%20Applications%2015%20SP3pkg:rpm/suse/trivy&distro=SUSE%20Package%20Hub%2015%20SP3pkg:rpm/suse/trivy&distro=SUSE%20Package%20Hub%2015%20SP4
< 1.10.0-r3+ 131 more
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 1.10.0-r2
- (no CPE)range: < 2.6.0-r1
- (no CPE)range: < 1.16.0-r6
- (no CPE)range: < 1.16.0-r1
- (no CPE)range: < 1.16.0-r6
- (no CPE)range: < 1.16.0-r1
- (no CPE)range: < 1.16.0-r6
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 1.10.0-r3
- (no CPE)range: < 2.16.0
- (no CPE)range: <= 2.7.1
- (no CPE)range: >= 3.0.0, < 3.8.0
- (no CPE)range: < 0.5.3-150000.1.12.1
- (no CPE)range: < 0.5.3-150000.1.12.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.21-150000.117.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.21-150000.117.1
- (no CPE)range: < 1.43.2-150300.8.9.3
- (no CPE)range: < 1.51.0-150400.4.3.1
- (no CPE)range: < 3.9.4-150000.1.10.3
- (no CPE)range: < 3.9.4-150000.1.10.3
- (no CPE)range: < 0.49.0-150300.8.13.1
- (no CPE)range: < 0.54.0-150400.3.3.2
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 0.30.4-bp153.8.1
- (no CPE)range: < 0.30.4-bp154.2.6.1
- (no CPE)range: < 0.5.3-150000.1.12.1
- (no CPE)range: < 0.5.3-150000.1.12.1
- (no CPE)range: < 0.5.3-150000.1.12.1
- (no CPE)range: < 0.5.3-150000.1.12.1
- (no CPE)range: < 0.5.3-150000.1.12.1
- (no CPE)range: < 0.2.0-150000.3.6.1
- (no CPE)range: < 0.2.0-150000.3.6.1
- (no CPE)range: < 0.2.0-150000.3.6.1
- (no CPE)range: < 0.2.0-150000.3.6.1
- (no CPE)range: < 0.2.0-150000.3.6.1
- (no CPE)range: < 0.2.0-150000.3.6.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.21-150000.117.1
- (no CPE)range: < 1.7.21-150000.117.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-16.91.7
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.21-150000.117.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.21-150000.117.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.8-150000.103.1
- (no CPE)range: < 1.7.21-150000.117.1
- (no CPE)range: < 1.7.21-1.1
- (no CPE)range: < 1.43.2-150300.8.9.3
- (no CPE)range: < 1.51.0-150400.4.3.1
- (no CPE)range: < 3.9.4-150000.1.10.3
- (no CPE)range: < 3.9.4-150000.1.10.3
- (no CPE)range: < 3.9.4-150000.1.10.3
- (no CPE)range: < 3.9.4-150000.1.10.3
- (no CPE)range: < 0.49.0-150300.8.13.1
- (no CPE)range: < 0.54.0-150400.3.3.2
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 1.1.10-150000.55.1
- (no CPE)range: < 0.30.4-bp153.8.1
- (no CPE)range: < 0.30.4-bp154.2.6.1
- emicklei/emicklei/go-restfulv5Range: unspecified
Patches
3926662532debuse exact matching of allowed domain entries, issue #489 (#493) (#503)
2 files changed · +64 −40
cors_filter.go+26 −38 modified@@ -18,9 +18,22 @@ import ( // http://enable-cors.org/server.html // http://www.html5rocks.com/en/tutorials/cors/#toc-handling-a-not-so-simple-request type CrossOriginResourceSharing struct { - ExposeHeaders []string // list of Header names - AllowedHeaders []string // list of Header names - AllowedDomains []string // list of allowed values for Http Origin. An allowed value can be a regular expression to support subdomain matching. If empty all are allowed. + ExposeHeaders []string // list of Header names + + // AllowedHeaders is alist of Header names. Checking is case-insensitive. + // The list may contain the special wildcard string ".*" ; all is allowed + AllowedHeaders []string + + // AllowedDomains is a list of allowed values for Http Origin. + // The list may contain the special wildcard string ".*" ; all is allowed + // If empty all are allowed. + AllowedDomains []string + + // AllowedDomainFunc is optional and is a function that will do the check + // when the origin is not part of the AllowedDomains and it does not contain the wildcard ".*". + AllowedDomainFunc func(origin string) bool + + // AllowedMethods is either empty or has a list of http methods names. Checking is case-insensitive. AllowedMethods []string MaxAge int // number of seconds before requiring new Options request CookiesAllowed bool @@ -119,36 +132,24 @@ func (c CrossOriginResourceSharing) isOriginAllowed(origin string) bool { if len(origin) == 0 { return false } + lowerOrigin := strings.ToLower(origin) if len(c.AllowedDomains) == 0 { + if c.AllowedDomainFunc != nil { + return c.AllowedDomainFunc(lowerOrigin) + } return true } - allowed := false + // exact match on each allowed domain for _, domain := range c.AllowedDomains { - if domain == origin { - allowed = true - break + if domain == ".*" || strings.ToLower(domain) == lowerOrigin { + return true } } - - if !allowed { - if len(c.allowedOriginPatterns) == 0 { - // compile allowed domains to allowed origin patterns - allowedOriginRegexps, err := compileRegexps(c.AllowedDomains) - if err != nil { - return false - } - c.allowedOriginPatterns = allowedOriginRegexps - } - - for _, pattern := range c.allowedOriginPatterns { - if allowed = pattern.MatchString(origin); allowed { - break - } - } + if c.AllowedDomainFunc != nil { + return c.AllowedDomainFunc(origin) } - - return allowed + return false } func (c CrossOriginResourceSharing) setAllowOriginHeader(req *Request, resp *Response) { @@ -190,16 +191,3 @@ func (c CrossOriginResourceSharing) isValidAccessControlRequestHeader(header str } return false } - -// Take a list of strings and compile them into a list of regular expressions. -func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) { - regexps := []*regexp.Regexp{} - for _, regexpStr := range regexpStrings { - r, err := regexp.Compile(regexpStr) - if err != nil { - return regexps, err - } - regexps = append(regexps, r) - } - return regexps, nil -}
cors_filter_test.go+38 −2 modified@@ -120,10 +120,46 @@ func TestCORSFilter_AllowedDomains(t *testing.T) { DefaultContainer.Dispatch(httpWriter, httpRequest) actual := httpWriter.Header().Get(HEADER_AccessControlAllowOrigin) if actual != each.origin && each.allowed { - t.Fatal("expected to be accepted") + t.Error("expected to be accepted", each) } if actual == each.origin && !each.allowed { - t.Fatal("did not expect to be accepted") + t.Error("did not expect to be accepted") } } } + +func TestCORSFilter_AllowedDomainFunc(t *testing.T) { + cors := CrossOriginResourceSharing{ + AllowedDomains: []string{"here", "there"}, + AllowedDomainFunc: func(origin string) bool { + return "where" == origin + }, + } + if got, want := cors.isOriginAllowed("here"), true; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + if got, want := cors.isOriginAllowed("HERE"), true; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + if got, want := cors.isOriginAllowed("there"), true; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + if got, want := cors.isOriginAllowed("where"), true; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + if got, want := cors.isOriginAllowed("nowhere"), false; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + // just func + cors.AllowedDomains = []string{} + if got, want := cors.isOriginAllowed("here"), false; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + if got, want := cors.isOriginAllowed("where"), true; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + // empty domain + if got, want := cors.isOriginAllowed(""), false; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } +}
fd3c327a379cuse exact matching of allowed domain entries, issue #489 (#493)
2 files changed · +64 −40
cors_filter.go+26 −38 modified@@ -18,9 +18,22 @@ import ( // http://enable-cors.org/server.html // http://www.html5rocks.com/en/tutorials/cors/#toc-handling-a-not-so-simple-request type CrossOriginResourceSharing struct { - ExposeHeaders []string // list of Header names - AllowedHeaders []string // list of Header names - AllowedDomains []string // list of allowed values for Http Origin. An allowed value can be a regular expression to support subdomain matching. If empty all are allowed. + ExposeHeaders []string // list of Header names + + // AllowedHeaders is alist of Header names. Checking is case-insensitive. + // The list may contain the special wildcard string ".*" ; all is allowed + AllowedHeaders []string + + // AllowedDomains is a list of allowed values for Http Origin. + // The list may contain the special wildcard string ".*" ; all is allowed + // If empty all are allowed. + AllowedDomains []string + + // AllowedDomainFunc is optional and is a function that will do the check + // when the origin is not part of the AllowedDomains and it does not contain the wildcard ".*". + AllowedDomainFunc func(origin string) bool + + // AllowedMethods is either empty or has a list of http methods names. Checking is case-insensitive. AllowedMethods []string MaxAge int // number of seconds before requiring new Options request CookiesAllowed bool @@ -119,36 +132,24 @@ func (c CrossOriginResourceSharing) isOriginAllowed(origin string) bool { if len(origin) == 0 { return false } + lowerOrigin := strings.ToLower(origin) if len(c.AllowedDomains) == 0 { + if c.AllowedDomainFunc != nil { + return c.AllowedDomainFunc(lowerOrigin) + } return true } - allowed := false + // exact match on each allowed domain for _, domain := range c.AllowedDomains { - if domain == origin { - allowed = true - break + if domain == ".*" || strings.ToLower(domain) == lowerOrigin { + return true } } - - if !allowed { - if len(c.allowedOriginPatterns) == 0 { - // compile allowed domains to allowed origin patterns - allowedOriginRegexps, err := compileRegexps(c.AllowedDomains) - if err != nil { - return false - } - c.allowedOriginPatterns = allowedOriginRegexps - } - - for _, pattern := range c.allowedOriginPatterns { - if allowed = pattern.MatchString(origin); allowed { - break - } - } + if c.AllowedDomainFunc != nil { + return c.AllowedDomainFunc(origin) } - - return allowed + return false } func (c CrossOriginResourceSharing) setAllowOriginHeader(req *Request, resp *Response) { @@ -190,16 +191,3 @@ func (c CrossOriginResourceSharing) isValidAccessControlRequestHeader(header str } return false } - -// Take a list of strings and compile them into a list of regular expressions. -func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) { - regexps := []*regexp.Regexp{} - for _, regexpStr := range regexpStrings { - r, err := regexp.Compile(regexpStr) - if err != nil { - return regexps, err - } - regexps = append(regexps, r) - } - return regexps, nil -}
cors_filter_test.go+38 −2 modified@@ -120,10 +120,46 @@ func TestCORSFilter_AllowedDomains(t *testing.T) { DefaultContainer.Dispatch(httpWriter, httpRequest) actual := httpWriter.Header().Get(HEADER_AccessControlAllowOrigin) if actual != each.origin && each.allowed { - t.Fatal("expected to be accepted") + t.Error("expected to be accepted", each) } if actual == each.origin && !each.allowed { - t.Fatal("did not expect to be accepted") + t.Error("did not expect to be accepted") } } } + +func TestCORSFilter_AllowedDomainFunc(t *testing.T) { + cors := CrossOriginResourceSharing{ + AllowedDomains: []string{"here", "there"}, + AllowedDomainFunc: func(origin string) bool { + return "where" == origin + }, + } + if got, want := cors.isOriginAllowed("here"), true; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + if got, want := cors.isOriginAllowed("HERE"), true; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + if got, want := cors.isOriginAllowed("there"), true; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + if got, want := cors.isOriginAllowed("where"), true; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + if got, want := cors.isOriginAllowed("nowhere"), false; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + // just func + cors.AllowedDomains = []string{} + if got, want := cors.isOriginAllowed("here"), false; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + if got, want := cors.isOriginAllowed("where"), true; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } + // empty domain + if got, want := cors.isOriginAllowed(""), false; got != want { + t.Errorf("got [%v:%T] want [%v:%T]", got, got, want, want) + } +}
f292efff46aeuse exact matching of allowed domain entries, issue #489
1 file changed · +9 −4
cors_filter.go+9 −4 modified@@ -5,6 +5,7 @@ package restful // that can be found in the LICENSE file. import ( + "fmt" "regexp" "strconv" "strings" @@ -191,11 +192,15 @@ func (c CrossOriginResourceSharing) isValidAccessControlRequestHeader(header str return false } -// Take a list of strings and compile them into a list of regular expressions. -func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) { +// Take a list of allowed domains as strings and compile them into a list of regular expressions. +func compileRegexps(allowedDomains []string) ([]*regexp.Regexp, error) { regexps := []*regexp.Regexp{} - for _, regexpStr := range regexpStrings { - r, err := regexp.Compile(regexpStr) + for _, each := range allowedDomains { + // make sure the expression represents an exact match + if !strings.HasPrefix(each, "^") { + each = fmt.Sprintf("^%s$", each) + } + r, err := regexp.Compile(each) if err != nil { return regexps, err }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
26- github.com/advisories/GHSA-r48q-9g5r-8q2hghsaADVISORY
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/575BLJ3Y2EQBRNTFR2OSQQ6L2W6UCST3/mitrevendor-advisory
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/OBDD3Q23RCGAGHIXUCWBU6N3S4RNAKXB/mitrevendor-advisory
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/RQXU752ALW53OJAF5MG3WMR5CCZVLWW6/mitrevendor-advisory
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/SO5QC2JFW2PXBWAE27OYYYL5SPFUBHTY/mitrevendor-advisory
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/W56PP46JVZEKCANBKXFKRVSBBRRMCY6V/mitrevendor-advisory
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/Z55VUVGO7E5PJFXIOVAY373NZRHBNCI5/mitrevendor-advisory
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/ZGQKWD6SE75PFBPFVSZYAKAVXKBZXKWS/mitrevendor-advisory
- lists.fedoraproject.org/archives/list/package-announce%40lists.fedoraproject.org/message/ZY2SLWOQR4ZURQ7UBRZ7JIX6H6F5JHJR/mitrevendor-advisory
- nvd.nist.gov/vuln/detail/CVE-2022-1996ghsaADVISORY
- github.com/emicklei/go-restful/commit/926662532deb450272956c7bc573978464aae74eghsaWEB
- github.com/emicklei/go-restful/commit/f292efff46ae17e9d104f865a60a39a2ae9402f1ghsaWEB
- github.com/emicklei/go-restful/commit/fd3c327a379ce08c68ef18765bdc925f5d9bad10ghsaWEB
- github.com/emicklei/go-restful/issues/489ghsaWEB
- huntr.dev/bounties/be837427-415c-4d8c-808b-62ce20aa84f1ghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/575BLJ3Y2EQBRNTFR2OSQQ6L2W6UCST3ghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/OBDD3Q23RCGAGHIXUCWBU6N3S4RNAKXBghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/RQXU752ALW53OJAF5MG3WMR5CCZVLWW6ghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/SO5QC2JFW2PXBWAE27OYYYL5SPFUBHTYghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/W56PP46JVZEKCANBKXFKRVSBBRRMCY6VghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/Z55VUVGO7E5PJFXIOVAY373NZRHBNCI5ghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/ZGQKWD6SE75PFBPFVSZYAKAVXKBZXKWSghsaWEB
- lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/ZY2SLWOQR4ZURQ7UBRZ7JIX6H6F5JHJRghsaWEB
- pkg.go.dev/vuln/GO-2022-0619ghsaWEB
- security.netapp.com/advisory/ntap-20220923-0005ghsaWEB
- security.netapp.com/advisory/ntap-20220923-0005/mitre
News mentions
0No linked articles in our index yet.