Skip to content

Commit 9b9eb92

Browse files
committed
🔧 fix: strictly check origin not using sub includes
1 parent 0b13e58 commit 9b9eb92

File tree

5 files changed

+161
-141
lines changed

5 files changed

+161
-141
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
# 1.3.1 - 8 May 2025
2+
Bug fix:
3+
- strictly check origin not using sub includes
14

25
# 1.3.0-exp.0 - 23 Apr 2025
36
Change:
47
- Add support for Elysia 1.3
58

6-
79
# 1.2.0-rc.0 - 23 Dec 2024
810
Change:
911
- Add support for Elysia 1.2

example/index.ts

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
import { Elysia, t } from 'elysia'
22
import { cors } from '../src/index'
33

4-
new Elysia()
5-
.use(cors())
4+
const app = new Elysia()
5+
.use(
6+
cors({
7+
origin: 'example.com'
8+
})
9+
)
610
.post('/', ({ body }) => body)
7-
.listen(3000)
811

9-
new Elysia().get('/', () => 'hi').listen(3001)
10-
11-
// app.handle(
12-
// new Request('http://localhost/awd', {
13-
// headers: {
14-
// origin: 'https://saltyaom.com'
15-
// }
16-
// })
17-
// )
18-
// .then((x) => x.headers.toJSON())
19-
// .then(console.log)
20-
21-
// export type App = typeof app
12+
app.handle(
13+
new Request('http://localhost/awd', {
14+
headers: {
15+
origin: 'http://notexample.com'
16+
}
17+
})
18+
)
19+
.then((x) => x.headers.toJSON())
20+
.then(console.log)

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@elysiajs/cors",
3-
"version": "1.3.0",
3+
"version": "1.3.1",
44
"description": "Plugin for Elysia that for Cross Origin Requests (CORs)",
55
"author": {
66
"name": "saltyAom",
@@ -46,4 +46,4 @@
4646
"peerDependencies": {
4747
"elysia": ">= 1.3.0"
4848
}
49-
}
49+
}

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ const processOrigin = (
191191

192192
switch (typeof origin) {
193193
case 'string':
194-
if (origin.indexOf('://') === -1) return from.includes(origin)
194+
const fromProtocol = from.indexOf('://')
195+
if (fromProtocol !== -1) from = from.slice(fromProtocol + 3)
195196

196197
return origin === from
197198

test/origin.test.ts

Lines changed: 139 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -5,125 +5,143 @@ import { describe, expect, it } from 'bun:test'
55
import { req } from './utils'
66

77
describe('Origin', () => {
8-
it('Accept string', async () => {
9-
const app = new Elysia()
10-
.use(
11-
cors({
12-
origin: 'saltyaom.com'
13-
})
14-
)
15-
.get('/', () => 'A')
16-
17-
const res = await app.fetch(
18-
new Request('http://localhost/', {
19-
headers: {
20-
origin: 'https://saltyaom.com'
21-
}
22-
})
23-
)
24-
25-
expect(res.headers.get('Access-Control-Allow-Origin')).toBe(
26-
'https://saltyaom.com'
27-
)
28-
})
29-
30-
it('Accept boolean', async () => {
31-
const app = new Elysia()
32-
.use(
33-
cors({
34-
origin: true
35-
})
36-
)
37-
.get('/', () => 'HI')
38-
39-
const res = await app.handle(req('/'))
40-
expect(res.headers.get('Access-Control-Allow-Origin')).toBe(
41-
'*'
42-
)
43-
})
44-
45-
it('Accept RegExp', async () => {
46-
const app = new Elysia()
47-
.use(
48-
cors({
49-
origin: /\.com/g
50-
})
51-
)
52-
.get('/', () => 'HI')
53-
54-
const notAllowed = await app.handle(
55-
req('/', {
56-
Origin: 'https://example.org'
57-
})
58-
)
59-
const allowed = await app.handle(
60-
req('/', {
61-
Origin: 'https://example.com'
62-
})
63-
)
64-
expect(notAllowed.headers.get('Access-Control-Allow-Origin')).toBe(null)
65-
expect(allowed.headers.get('Access-Control-Allow-Origin')).toBe(
66-
'https://example.com'
67-
)
68-
})
69-
70-
it('Accept Function', async () => {
71-
const app = new Elysia()
72-
.use(
73-
cors({
74-
origin: () => true
75-
})
76-
)
77-
.get('/', () => 'HI')
78-
79-
const res = await app.handle(
80-
req('/', {
81-
Origin: 'https://example.com'
82-
})
83-
)
84-
expect(res.headers.get('Access-Control-Allow-Origin')).toBe(
85-
'https://example.com'
86-
)
87-
})
88-
89-
it('Accept string[]', async () => {
90-
const app = new Elysia()
91-
.use(
92-
cors({
93-
origin: ['gehenna.sh', 'saltyaom.com']
94-
})
95-
)
96-
.get('/', () => 'A')
97-
98-
const res = await app.fetch(
99-
new Request('http://localhost/', {
100-
headers: {
101-
origin: 'https://saltyaom.com'
102-
}
103-
})
104-
)
105-
106-
expect(res.headers.get('Access-Control-Allow-Origin')).toBe(
107-
'https://saltyaom.com'
108-
)
109-
})
110-
111-
it('Accept Function[]', async () => {
112-
const app = new Elysia()
113-
.use(
114-
cors({
115-
origin: ['https://demo.app', () => false, /.com/g]
116-
})
117-
)
118-
.get('/', () => 'HI')
119-
120-
const res = await app.handle(
121-
req('/', {
122-
Origin: 'https://example.com'
123-
})
124-
)
125-
expect(res.headers.get('Access-Control-Allow-Origin')).toBe(
126-
'https://example.com'
127-
)
128-
})
8+
it('Accept string', async () => {
9+
const app = new Elysia()
10+
.use(
11+
cors({
12+
origin: 'saltyaom.com'
13+
})
14+
)
15+
.get('/', () => 'A')
16+
17+
const res = await app.fetch(
18+
new Request('http://localhost/', {
19+
headers: {
20+
origin: 'https://saltyaom.com'
21+
}
22+
})
23+
)
24+
25+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe(
26+
'https://saltyaom.com'
27+
)
28+
})
29+
30+
it('Accept boolean', async () => {
31+
const app = new Elysia()
32+
.use(
33+
cors({
34+
origin: true
35+
})
36+
)
37+
.get('/', () => 'HI')
38+
39+
const res = await app.handle(req('/'))
40+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*')
41+
})
42+
43+
it('Accept RegExp', async () => {
44+
const app = new Elysia()
45+
.use(
46+
cors({
47+
origin: /\.com/g
48+
})
49+
)
50+
.get('/', () => 'HI')
51+
52+
const notAllowed = await app.handle(
53+
req('/', {
54+
Origin: 'https://example.org'
55+
})
56+
)
57+
const allowed = await app.handle(
58+
req('/', {
59+
Origin: 'https://example.com'
60+
})
61+
)
62+
expect(notAllowed.headers.get('Access-Control-Allow-Origin')).toBe(null)
63+
expect(allowed.headers.get('Access-Control-Allow-Origin')).toBe(
64+
'https://example.com'
65+
)
66+
})
67+
68+
it('Accept Function', async () => {
69+
const app = new Elysia()
70+
.use(
71+
cors({
72+
origin: () => true
73+
})
74+
)
75+
.get('/', () => 'HI')
76+
77+
const res = await app.handle(
78+
req('/', {
79+
Origin: 'https://example.com'
80+
})
81+
)
82+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe(
83+
'https://example.com'
84+
)
85+
})
86+
87+
it('Accept string[]', async () => {
88+
const app = new Elysia()
89+
.use(
90+
cors({
91+
origin: ['gehenna.sh', 'saltyaom.com']
92+
})
93+
)
94+
.get('/', () => 'A')
95+
96+
const res = await app.fetch(
97+
new Request('http://localhost/', {
98+
headers: {
99+
origin: 'https://saltyaom.com'
100+
}
101+
})
102+
)
103+
104+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe(
105+
'https://saltyaom.com'
106+
)
107+
})
108+
109+
it('Accept Function[]', async () => {
110+
const app = new Elysia()
111+
.use(
112+
cors({
113+
origin: ['https://demo.app', () => false, /.com/g]
114+
})
115+
)
116+
.get('/', () => 'HI')
117+
118+
const res = await app.handle(
119+
req('/', {
120+
Origin: 'https://example.com'
121+
})
122+
)
123+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe(
124+
'https://example.com'
125+
)
126+
})
127+
128+
it('strictly check origin not using sub includes', async () => {
129+
const app = new Elysia()
130+
.use(
131+
cors({
132+
origin: 'example.com'
133+
})
134+
)
135+
.post('/', ({ body }) => body)
136+
137+
const response = await app.handle(
138+
new Request('http://localhost/awd', {
139+
headers: {
140+
origin: 'http://notexample.com'
141+
}
142+
})
143+
)
144+
145+
expect(response.headers.has('access-control-allow-origin')).toBeFalse()
146+
})
129147
})

0 commit comments

Comments
 (0)