Skip to content

Commit 124f7eb

Browse files
authored
Merge pull request from GHSA-8qr4-xgw6-wmr3
1 parent aef314c commit 124f7eb

File tree

3 files changed

+171
-4
lines changed

3 files changed

+171
-4
lines changed

Diff for: index.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,12 @@ function makeDispatcher (fn) {
5353
throw new InvalidArgumentError('invalid opts.path')
5454
}
5555

56-
url = new URL(opts.path, util.parseOrigin(url))
56+
let path = opts.path
57+
if (!opts.path.startsWith('/')) {
58+
path = `/${path}`
59+
}
60+
61+
url = new URL(util.parseOrigin(url).origin + path)
5762
} else {
5863
if (!opts) {
5964
opts = typeof url === 'object' ? url : {}

Diff for: lib/core/util.js

+14-3
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,25 @@ function parseURL (url) {
108108
const port = url.port != null
109109
? url.port
110110
: (url.protocol === 'https:' ? 443 : 80)
111-
const origin = url.origin != null
111+
let origin = url.origin != null
112112
? url.origin
113113
: `${url.protocol}//${url.hostname}:${port}`
114-
const path = url.path != null
114+
let path = url.path != null
115115
? url.path
116116
: `${url.pathname || ''}${url.search || ''}`
117117

118-
url = new URL(path, origin)
118+
if (origin.endsWith('/')) {
119+
origin = origin.substring(0, origin.length - 1)
120+
}
121+
122+
if (path && !path.startsWith('/')) {
123+
path = `/${path}`
124+
}
125+
// new URL(path, origin) is unsafe when `path` contains an absolute URL
126+
// From https://developer.mozilla.org/en-US/docs/Web/API/URL/URL:
127+
// If first parameter is a relative URL, second param is required, and will be used as the base URL.
128+
// If first parameter is an absolute URL, a given second param will be ignored.
129+
url = new URL(origin + path)
119130
}
120131

121132
return url

Diff for: test/request.js

+151
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
'use strict'
2+
3+
const { createServer } = require('http')
4+
const { test } = require('tap')
5+
const { request } = require('..')
6+
7+
test('no-slash/one-slash pathname should be included in req.path', async (t) => {
8+
const pathServer = createServer((req, res) => {
9+
t.fail('it shouldn\'t be called')
10+
res.statusCode = 200
11+
res.end('hello')
12+
})
13+
14+
const requestedServer = createServer((req, res) => {
15+
t.equal(`/localhost:${pathServer.address().port}`, req.url)
16+
t.equal('GET', req.method)
17+
t.equal(`localhost:${requestedServer.address().port}`, req.headers.host)
18+
res.statusCode = 200
19+
res.end('hello')
20+
})
21+
22+
t.teardown(requestedServer.close.bind(requestedServer))
23+
t.teardown(pathServer.close.bind(pathServer))
24+
25+
await Promise.all([
26+
requestedServer.listen(0),
27+
pathServer.listen(0)
28+
])
29+
30+
const noSlashPathname = await request({
31+
method: 'GET',
32+
origin: `http://localhost:${requestedServer.address().port}`,
33+
pathname: `localhost:${pathServer.address().port}`
34+
})
35+
t.equal(noSlashPathname.statusCode, 200)
36+
const noSlashPath = await request({
37+
method: 'GET',
38+
origin: `http://localhost:${requestedServer.address().port}`,
39+
path: `localhost:${pathServer.address().port}`
40+
})
41+
t.equal(noSlashPath.statusCode, 200)
42+
const noSlashPath2Arg = await request(
43+
`http://localhost:${requestedServer.address().port}`,
44+
{ path: `localhost:${pathServer.address().port}` }
45+
)
46+
t.equal(noSlashPath2Arg.statusCode, 200)
47+
const oneSlashPathname = await request({
48+
method: 'GET',
49+
origin: `http://localhost:${requestedServer.address().port}`,
50+
pathname: `/localhost:${pathServer.address().port}`
51+
})
52+
t.equal(oneSlashPathname.statusCode, 200)
53+
const oneSlashPath = await request({
54+
method: 'GET',
55+
origin: `http://localhost:${requestedServer.address().port}`,
56+
path: `/localhost:${pathServer.address().port}`
57+
})
58+
t.equal(oneSlashPath.statusCode, 200)
59+
const oneSlashPath2Arg = await request(
60+
`http://localhost:${requestedServer.address().port}`,
61+
{ path: `/localhost:${pathServer.address().port}` }
62+
)
63+
t.equal(oneSlashPath2Arg.statusCode, 200)
64+
t.end()
65+
})
66+
67+
test('protocol-relative URL as pathname should be included in req.path', async (t) => {
68+
const pathServer = createServer((req, res) => {
69+
t.fail('it shouldn\'t be called')
70+
res.statusCode = 200
71+
res.end('hello')
72+
})
73+
74+
const requestedServer = createServer((req, res) => {
75+
t.equal(`//localhost:${pathServer.address().port}`, req.url)
76+
t.equal('GET', req.method)
77+
t.equal(`localhost:${requestedServer.address().port}`, req.headers.host)
78+
res.statusCode = 200
79+
res.end('hello')
80+
})
81+
82+
t.teardown(requestedServer.close.bind(requestedServer))
83+
t.teardown(pathServer.close.bind(pathServer))
84+
85+
await Promise.all([
86+
requestedServer.listen(0),
87+
pathServer.listen(0)
88+
])
89+
90+
const noSlashPathname = await request({
91+
method: 'GET',
92+
origin: `http://localhost:${requestedServer.address().port}`,
93+
pathname: `//localhost:${pathServer.address().port}`
94+
})
95+
t.equal(noSlashPathname.statusCode, 200)
96+
const noSlashPath = await request({
97+
method: 'GET',
98+
origin: `http://localhost:${requestedServer.address().port}`,
99+
path: `//localhost:${pathServer.address().port}`
100+
})
101+
t.equal(noSlashPath.statusCode, 200)
102+
const noSlashPath2Arg = await request(
103+
`http://localhost:${requestedServer.address().port}`,
104+
{ path: `//localhost:${pathServer.address().port}` }
105+
)
106+
t.equal(noSlashPath2Arg.statusCode, 200)
107+
t.end()
108+
})
109+
110+
test('Absolute URL as pathname should be included in req.path', async (t) => {
111+
const pathServer = createServer((req, res) => {
112+
t.fail('it shouldn\'t be called')
113+
res.statusCode = 200
114+
res.end('hello')
115+
})
116+
117+
const requestedServer = createServer((req, res) => {
118+
t.equal(`/http://localhost:${pathServer.address().port}`, req.url)
119+
t.equal('GET', req.method)
120+
t.equal(`localhost:${requestedServer.address().port}`, req.headers.host)
121+
res.statusCode = 200
122+
res.end('hello')
123+
})
124+
125+
t.teardown(requestedServer.close.bind(requestedServer))
126+
t.teardown(pathServer.close.bind(pathServer))
127+
128+
await Promise.all([
129+
requestedServer.listen(0),
130+
pathServer.listen(0)
131+
])
132+
133+
const noSlashPathname = await request({
134+
method: 'GET',
135+
origin: `http://localhost:${requestedServer.address().port}`,
136+
pathname: `http://localhost:${pathServer.address().port}`
137+
})
138+
t.equal(noSlashPathname.statusCode, 200)
139+
const noSlashPath = await request({
140+
method: 'GET',
141+
origin: `http://localhost:${requestedServer.address().port}`,
142+
path: `http://localhost:${pathServer.address().port}`
143+
})
144+
t.equal(noSlashPath.statusCode, 200)
145+
const noSlashPath2Arg = await request(
146+
`http://localhost:${requestedServer.address().port}`,
147+
{ path: `http://localhost:${pathServer.address().port}` }
148+
)
149+
t.equal(noSlashPath2Arg.statusCode, 200)
150+
t.end()
151+
})

0 commit comments

Comments
 (0)