CVE-2025-50864
Description
An Origin Validation Error in the elysia-cors library thru 1.3.0 allows attackers to bypass Cross-Origin Resource Sharing (CORS) restrictions. The library incorrectly validates the supplied origin by checking if it is a substring of any domain in the site's CORS policy, rather than performing an exact match. For example, a malicious origin like "notexample.com", "example.common.net" is whitelisted when the site's CORS policy specifies "example.com." This vulnerability enables unauthorized access to user data on sites using the elysia-cors library for CORS validation.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
@elysiajs/corsnpm | < 1.3.1 | 1.3.1 |
Affected products
1- Range: 1.1.1, 1.2.0, 1.3.0
Patches
19b9eb92e32a7:wrench: fix: strictly check origin not using sub includes
5 files changed · +161 −141
CHANGELOG.md+3 −1 modified@@ -1,9 +1,11 @@ +# 1.3.1 - 8 May 2025 +Bug fix: +- strictly check origin not using sub includes # 1.3.0-exp.0 - 23 Apr 2025 Change: - Add support for Elysia 1.3 - # 1.2.0-rc.0 - 23 Dec 2024 Change: - Add support for Elysia 1.2
example/index.ts+15 −16 modified@@ -1,21 +1,20 @@ import { Elysia, t } from 'elysia' import { cors } from '../src/index' -new Elysia() - .use(cors()) +const app = new Elysia() + .use( + cors({ + origin: 'example.com' + }) + ) .post('/', ({ body }) => body) - .listen(3000) -new Elysia().get('/', () => 'hi').listen(3001) - -// app.handle( -// new Request('http://localhost/awd', { -// headers: { -// origin: 'https://saltyaom.com' -// } -// }) -// ) -// .then((x) => x.headers.toJSON()) -// .then(console.log) - -// export type App = typeof app +app.handle( + new Request('http://localhost/awd', { + headers: { + origin: 'http://notexample.com' + } + }) +) + .then((x) => x.headers.toJSON()) + .then(console.log)
package.json+2 −2 modified@@ -1,6 +1,6 @@ { "name": "@elysiajs/cors", - "version": "1.3.0", + "version": "1.3.1", "description": "Plugin for Elysia that for Cross Origin Requests (CORs)", "author": { "name": "saltyAom", @@ -46,4 +46,4 @@ "peerDependencies": { "elysia": ">= 1.3.0" } -} \ No newline at end of file +}
src/index.ts+2 −1 modified@@ -191,7 +191,8 @@ const processOrigin = ( switch (typeof origin) { case 'string': - if (origin.indexOf('://') === -1) return from.includes(origin) + const fromProtocol = from.indexOf('://') + if (fromProtocol !== -1) from = from.slice(fromProtocol + 3) return origin === from
test/origin.test.ts+139 −121 modified@@ -5,125 +5,143 @@ import { describe, expect, it } from 'bun:test' import { req } from './utils' describe('Origin', () => { - it('Accept string', async () => { - const app = new Elysia() - .use( - cors({ - origin: 'saltyaom.com' - }) - ) - .get('/', () => 'A') - - const res = await app.fetch( - new Request('http://localhost/', { - headers: { - origin: 'https://saltyaom.com' - } - }) - ) - - expect(res.headers.get('Access-Control-Allow-Origin')).toBe( - 'https://saltyaom.com' - ) - }) - - it('Accept boolean', async () => { - const app = new Elysia() - .use( - cors({ - origin: true - }) - ) - .get('/', () => 'HI') - - const res = await app.handle(req('/')) - expect(res.headers.get('Access-Control-Allow-Origin')).toBe( - '*' - ) - }) - - it('Accept RegExp', async () => { - const app = new Elysia() - .use( - cors({ - origin: /\.com/g - }) - ) - .get('/', () => 'HI') - - const notAllowed = await app.handle( - req('/', { - Origin: 'https://example.org' - }) - ) - const allowed = await app.handle( - req('/', { - Origin: 'https://example.com' - }) - ) - expect(notAllowed.headers.get('Access-Control-Allow-Origin')).toBe(null) - expect(allowed.headers.get('Access-Control-Allow-Origin')).toBe( - 'https://example.com' - ) - }) - - it('Accept Function', async () => { - const app = new Elysia() - .use( - cors({ - origin: () => true - }) - ) - .get('/', () => 'HI') - - const res = await app.handle( - req('/', { - Origin: 'https://example.com' - }) - ) - expect(res.headers.get('Access-Control-Allow-Origin')).toBe( - 'https://example.com' - ) - }) - - it('Accept string[]', async () => { - const app = new Elysia() - .use( - cors({ - origin: ['gehenna.sh', 'saltyaom.com'] - }) - ) - .get('/', () => 'A') - - const res = await app.fetch( - new Request('http://localhost/', { - headers: { - origin: 'https://saltyaom.com' - } - }) - ) - - expect(res.headers.get('Access-Control-Allow-Origin')).toBe( - 'https://saltyaom.com' - ) - }) - - it('Accept Function[]', async () => { - const app = new Elysia() - .use( - cors({ - origin: ['https://demo.app', () => false, /.com/g] - }) - ) - .get('/', () => 'HI') - - const res = await app.handle( - req('/', { - Origin: 'https://example.com' - }) - ) - expect(res.headers.get('Access-Control-Allow-Origin')).toBe( - 'https://example.com' - ) - }) + it('Accept string', async () => { + const app = new Elysia() + .use( + cors({ + origin: 'saltyaom.com' + }) + ) + .get('/', () => 'A') + + const res = await app.fetch( + new Request('http://localhost/', { + headers: { + origin: 'https://saltyaom.com' + } + }) + ) + + expect(res.headers.get('Access-Control-Allow-Origin')).toBe( + 'https://saltyaom.com' + ) + }) + + it('Accept boolean', async () => { + const app = new Elysia() + .use( + cors({ + origin: true + }) + ) + .get('/', () => 'HI') + + const res = await app.handle(req('/')) + expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*') + }) + + it('Accept RegExp', async () => { + const app = new Elysia() + .use( + cors({ + origin: /\.com/g + }) + ) + .get('/', () => 'HI') + + const notAllowed = await app.handle( + req('/', { + Origin: 'https://example.org' + }) + ) + const allowed = await app.handle( + req('/', { + Origin: 'https://example.com' + }) + ) + expect(notAllowed.headers.get('Access-Control-Allow-Origin')).toBe(null) + expect(allowed.headers.get('Access-Control-Allow-Origin')).toBe( + 'https://example.com' + ) + }) + + it('Accept Function', async () => { + const app = new Elysia() + .use( + cors({ + origin: () => true + }) + ) + .get('/', () => 'HI') + + const res = await app.handle( + req('/', { + Origin: 'https://example.com' + }) + ) + expect(res.headers.get('Access-Control-Allow-Origin')).toBe( + 'https://example.com' + ) + }) + + it('Accept string[]', async () => { + const app = new Elysia() + .use( + cors({ + origin: ['gehenna.sh', 'saltyaom.com'] + }) + ) + .get('/', () => 'A') + + const res = await app.fetch( + new Request('http://localhost/', { + headers: { + origin: 'https://saltyaom.com' + } + }) + ) + + expect(res.headers.get('Access-Control-Allow-Origin')).toBe( + 'https://saltyaom.com' + ) + }) + + it('Accept Function[]', async () => { + const app = new Elysia() + .use( + cors({ + origin: ['https://demo.app', () => false, /.com/g] + }) + ) + .get('/', () => 'HI') + + const res = await app.handle( + req('/', { + Origin: 'https://example.com' + }) + ) + expect(res.headers.get('Access-Control-Allow-Origin')).toBe( + 'https://example.com' + ) + }) + + it('strictly check origin not using sub includes', async () => { + const app = new Elysia() + .use( + cors({ + origin: 'example.com' + }) + ) + .post('/', ({ body }) => body) + + const response = await app.handle( + new Request('http://localhost/awd', { + headers: { + origin: 'http://notexample.com' + } + }) + ) + + expect(response.headers.has('access-control-allow-origin')).toBeFalse() + }) })
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
7- github.com/advisories/GHSA-f9qj-4c5x-cpcwghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-50864ghsaADVISORY
- elysiajs.comnvdWEB
- github.com/elysiajs/elysia-cors/blob/main/src/index.tsnvdWEB
- github.com/elysiajs/elysia-cors/commit/9b9eb92e32a7a4b43b6d5108668941701c33e221nvdWEB
- github.com/elysiajs/elysia-cors/tree/mainnvdWEB
- medium.com/@raghavagrawal_23036/cors-bypass-in-popular-opensource-library-ad27fb41e16anvdWEB
News mentions
0No linked articles in our index yet.