Skip to content

Commit da8df9b

Browse files
RubenVerborghdmitrizagidulin
authored andcommitted
Use http-proxy-middleware for CORS proxy.
By delegating all proxy functionality to an existing library, we avoid the need for extra tests and checks on our side.
1 parent 775b46d commit da8df9b

File tree

2 files changed

+49
-73
lines changed

2 files changed

+49
-73
lines changed

lib/handlers/proxy.js

Lines changed: 48 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,60 @@
1-
module.exports = addProxy
1+
module.exports = addCorsProxyHandler
22

3+
const proxy = require('http-proxy-middleware')
34
const cors = require('cors')
4-
const http = require('http')
5-
const https = require('https')
65
const debug = require('../debug')
76
const url = require('url')
87
const isIp = require('is-ip')
98
const ipRange = require('ip-range-check')
109
const validUrl = require('valid-url')
1110

12-
function addProxy (app, path) {
13-
debug.settings('XSS/CORS Proxy listening at /' + path + '?uri={uri}')
14-
app.get(
15-
path,
16-
cors({
17-
methods: ['GET'],
18-
exposedHeaders: 'Authorization, User, Location, Link, Vary, Last-Modified, Content-Length',
19-
maxAge: 1728000,
20-
origin: true
21-
}),
22-
(req, res) => {
23-
if (!validUrl.isUri(req.query.uri)) {
24-
return res
25-
.status(406)
26-
.send('The uri passed is not valid')
27-
}
28-
29-
debug.settings('proxy received: ' + req.originalUrl)
30-
31-
const hostname = url.parse(req.query.uri).hostname
32-
33-
if (isIp(hostname) && (
34-
ipRange(hostname, '10.0.0.0/8') ||
35-
ipRange(hostname, '172.16.0.0/12') ||
36-
ipRange(hostname, '192.168.0.0/16')
37-
)) {
38-
return res
39-
.status(406)
40-
.send('Cannot proxy this IP')
41-
}
42-
const uri = req.query.uri
43-
if (!uri) {
44-
return res
45-
.status(400)
46-
.send('Proxy has no uri param ')
47-
}
48-
49-
debug.settings('Proxy destination URI: ' + uri)
50-
51-
const protocol = uri.split(':')[0]
52-
let request
53-
if (protocol === 'http') {
54-
request = http.get
55-
} else if (protocol === 'https') {
56-
request = https.get
57-
} else {
58-
return res.send(400)
59-
}
60-
61-
// Set the headers and uri of the proxied request
62-
const opts = url.parse(uri)
63-
opts.headers = req.headers
64-
// See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
65-
delete opts.headers.connection
66-
delete opts.headers.host
11+
const CORS_SETTINGS = {
12+
methods: 'GET',
13+
exposedHeaders: 'Authorization, User, Location, Link, Vary, Last-Modified, Content-Length',
14+
maxAge: 1728000,
15+
origin: true
16+
}
17+
const PROXY_SETTINGS = {
18+
target: 'dynamic',
19+
logLevel: 'silent',
20+
router: req => req.destination.target,
21+
pathRewrite: (path, req) => req.destination.path
22+
}
23+
const LOCAL_IP_RANGES = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16']
6724

68-
const _req = request(opts, (_res) => {
69-
res.status(_res.statusCode)
70-
// Set the response with the same header of proxied response
71-
Object.keys(_res.headers).forEach((header) => {
72-
if (!res.get(header)) {
73-
res.setHeader(header.trim(), _res.headers[header])
74-
}
75-
})
76-
_res.pipe(res)
77-
})
25+
// Adds a CORS proxy handler to the application on the given path
26+
function addCorsProxyHandler (app, path) {
27+
const corsHandler = cors(CORS_SETTINGS)
28+
const proxyHandler = proxy(PROXY_SETTINGS)
7829

79-
_req.on('error', (e) => {
80-
res.send(500, 'Cannot proxy')
81-
})
30+
debug.settings(`CORS proxy listening at ${path}?uri={uri}`)
31+
app.get(path, extractProxyConfig, corsHandler, proxyHandler)
32+
}
8233

83-
_req.end()
84-
})
34+
// Extracts proxy configuration parameters from the request
35+
function extractProxyConfig (req, res, next) {
36+
// Retrieve and validate the destination URL
37+
const uri = req.query.uri
38+
debug.settings(`Proxy request for ${uri}`)
39+
if (!uri) {
40+
return res.status(400)
41+
.send('Proxy has no uri param ')
42+
}
43+
if (!validUrl.isUri(uri)) {
44+
return res.status(406)
45+
.send('The uri passed is not valid')
46+
}
47+
48+
// Ensure the host is not a local IP
49+
// TODO: guard against hostnames such as 'localhost' as well
50+
const { protocol, host, hostname, path } = url.parse(uri)
51+
if (isIp(hostname) && LOCAL_IP_RANGES.some(r => ipRange(hostname, r))) {
52+
return res
53+
.status(406)
54+
.send('Cannot proxy this IP')
55+
}
56+
57+
// Add the proxy configuration to the request
58+
req.destination = { path, target: `${protocol}//${host}` }
59+
next()
8560
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
"fs-extra": "^2.1.0",
4747
"glob": "^7.1.1",
4848
"handlebars": "^4.0.6",
49+
"http-proxy-middleware": "^0.17.4",
4950
"inquirer": "^1.0.2",
5051
"ip-range-check": "0.0.1",
5152
"is-ip": "^1.0.0",

0 commit comments

Comments
 (0)