@@ -10,12 +10,33 @@ governing permissions and limitations under the License.
1010*/
1111
1212const queryString = require ( 'query-string' )
13- const { createHttpsProxy, createHttpProxy } = require ( '../src/proxy' )
13+ const { createHttpsProxy, createHttpProxy, generateCert } = require ( '../src/proxy' )
1414const { createApiServer, HOSTNAME } = require ( '../src/api-server' )
1515const fetch = require ( 'node-fetch' )
16- const HttpsProxyAgent = require ( 'https-proxy-agent' )
17- const HttpProxyAgent = require ( 'http-proxy-agent' )
16+ const { HttpsProxyAgent } = require ( 'https-proxy-agent' )
17+ const { HttpProxyAgent } = require ( 'http-proxy-agent' )
1818const url = require ( 'url' )
19+ const syswidecas = require ( 'syswide-cas' )
20+
21+ jest . mock ( 'syswide-cas' )
22+
23+ /**
24+ * HttpsProxyAgent needs a patch for TLS connections.
25+ * It doesn't pass in the original options during a SSL connect.
26+ *
27+ * See https://github.com/TooTallNate/proxy-agents/issues/89
28+ * An alternative is to use https://github.com/delvedor/hpagent
29+ */
30+ class PatchedHttpsProxyAgent extends HttpsProxyAgent {
31+ constructor ( proxyUrl , opts ) {
32+ super ( proxyUrl , opts )
33+ this . savedOpts = opts
34+ }
35+
36+ async connect ( req , opts ) {
37+ return super . connect ( req , { ...this . savedOpts , ...opts } )
38+ }
39+ }
1940
2041/**
2142 * Converts a URL to a suitable object for http request options.
@@ -154,10 +175,11 @@ describe('https proxy', () => {
154175 const protocol = 'https'
155176 let proxyServer , apiServer
156177 const portNotInUse = 3009
178+ const selfSigned = true
157179
158- describe ( 'no auth' , ( ) => {
180+ describe ( 'no auth (self-signed) ' , ( ) => {
159181 beforeAll ( async ( ) => {
160- proxyServer = await createHttpsProxy ( )
182+ proxyServer = await createHttpsProxy ( { selfSigned } )
161183 apiServer = await createApiServer ( { port : 3001 , useSsl : true } )
162184 } )
163185
@@ -174,17 +196,25 @@ describe('https proxy', () => {
174196
175197 const proxyUrl = proxyServer . url
176198 const proxyOpts = urlToHttpOptions ( proxyUrl )
177- // the passing on of this property to the underlying implementation only works on https-proxy-agent@2.2.4
178- // this is only used for unit-tests and passed in the constructor
179- proxyOpts . rejectUnauthorized = false
180- proxyOpts . ALPNProtocols = [ 'http/1.1' ]
181199
182- const response = await fetch ( testUrl , {
183- agent : new HttpsProxyAgent ( proxyOpts )
184- } )
200+ // IGNORE self-signed certs
201+ {
202+ proxyOpts . rejectUnauthorized = false
203+ const response = await fetch ( testUrl , {
204+ agent : new PatchedHttpsProxyAgent ( proxyUrl , proxyOpts )
205+ } )
185206
186- const json = await response . json ( )
187- expect ( json ) . toStrictEqual ( queryObject )
207+ const json = await response . json ( )
208+ expect ( json ) . toStrictEqual ( queryObject )
209+ }
210+ // DO NOT IGNORE self-signed certs
211+ {
212+ proxyOpts . rejectUnauthorized = true
213+ const proxyFetch = fetch ( testUrl , {
214+ agent : new PatchedHttpsProxyAgent ( proxyUrl , proxyOpts )
215+ } )
216+ await expect ( proxyFetch ) . rejects . toThrow ( 'self-signed certificate in certificate chain' )
217+ }
188218 } )
189219
190220 test ( 'failure' , async ( ) => {
@@ -195,20 +225,31 @@ describe('https proxy', () => {
195225 const proxyOpts = urlToHttpOptions ( proxyUrl )
196226 // the passing on of this property to the underlying implementation only works on https-proxy-agent@2.2.4
197227 // this is only used for unit-tests and passed in the constructor
198- proxyOpts . rejectUnauthorized = false
199228 proxyOpts . ALPNProtocols = [ 'http/1.1' ]
200229
201- const response = await fetch ( testUrl , {
202- agent : new HttpsProxyAgent ( proxyOpts )
203- } )
204- expect ( response . ok ) . toEqual ( false )
205- expect ( response . status ) . toEqual ( 502 )
230+ // IGNORE self-signed certs
231+ {
232+ proxyOpts . rejectUnauthorized = false
233+ const response = await fetch ( testUrl , {
234+ agent : new PatchedHttpsProxyAgent ( proxyUrl , proxyOpts )
235+ } )
236+ expect ( response . ok ) . toEqual ( false )
237+ expect ( response . status ) . toEqual ( 502 )
238+ }
239+ // DO NOT IGNORE self-signed certs
240+ {
241+ proxyOpts . rejectUnauthorized = true
242+ const proxyFetch = fetch ( testUrl , {
243+ agent : new PatchedHttpsProxyAgent ( proxyUrl , proxyOpts )
244+ } )
245+ await expect ( proxyFetch ) . rejects . toThrow ( 'self-signed certificate in certificate chain' )
246+ }
206247 } )
207248 } )
208249
209250 describe ( 'basic auth' , ( ) => {
210251 beforeAll ( async ( ) => {
211- proxyServer = await createHttpsProxy ( { useBasicAuth : true } )
252+ proxyServer = await createHttpsProxy ( { useBasicAuth : true , selfSigned } )
212253 apiServer = await createApiServer ( { port : 3001 , useSsl : true } )
213254 } )
214255
@@ -229,19 +270,27 @@ describe('https proxy', () => {
229270 const proxyUrl = proxyServer . url
230271 const proxyOpts = urlToHttpOptions ( proxyUrl )
231272 proxyOpts . auth = `${ username } :${ password } `
232- // the passing on of this property to the underlying implementation only works on https-proxy-agent@2.2.4
233- // this is only used for unit-tests and passed in the constructor
234- proxyOpts . rejectUnauthorized = false
235- proxyOpts . ALPNProtocols = [ 'http/1.1' ]
236-
237273 const testUrl = `${ protocol } ://${ HOSTNAME } :${ apiServerPort } /mirror?${ queryString . stringify ( queryObject ) } `
238- const response = await fetch ( testUrl , {
239- agent : new HttpsProxyAgent ( proxyOpts ) ,
240- headers
241- } )
242- expect ( response . ok ) . toEqual ( true )
243- const json = await response . json ( )
244- expect ( json ) . toStrictEqual ( queryObject )
274+
275+ // IGNORE self-signed certs
276+ {
277+ proxyOpts . rejectUnauthorized = false
278+ const response = await fetch ( testUrl , {
279+ agent : new PatchedHttpsProxyAgent ( proxyUrl , proxyOpts ) ,
280+ headers
281+ } )
282+ expect ( response . ok ) . toEqual ( true )
283+ const json = await response . json ( )
284+ expect ( json ) . toStrictEqual ( queryObject )
285+ }
286+ // DO NOT IGNORE self-signed certs
287+ {
288+ proxyOpts . rejectUnauthorized = true
289+ const proxyFetch = fetch ( testUrl , {
290+ agent : new PatchedHttpsProxyAgent ( proxyUrl , proxyOpts )
291+ } )
292+ await expect ( proxyFetch ) . rejects . toThrow ( 'self-signed certificate in certificate chain' )
293+ }
245294 } )
246295
247296 test ( 'failure' , async ( ) => {
@@ -256,18 +305,51 @@ describe('https proxy', () => {
256305 const proxyUrl = proxyServer . url
257306 const proxyOpts = urlToHttpOptions ( proxyUrl )
258307 proxyOpts . auth = `${ username } :${ password } `
259- // the passing on of this property to the underlying implementation only works on https-proxy-agent@2.2.4
260- // this is only used for unit-tests and passed in the constructor
261- proxyOpts . rejectUnauthorized = false
262- proxyOpts . ALPNProtocols = [ 'http/1.1' ]
263-
264308 const testUrl = `${ protocol } ://${ HOSTNAME } :${ apiServerPort } /mirror?${ queryString . stringify ( queryObject ) } `
265- const response = await fetch ( testUrl , {
266- agent : new HttpsProxyAgent ( proxyOpts ) ,
267- headers
268- } )
269- expect ( response . ok ) . toEqual ( false )
270- expect ( response . status ) . toEqual ( 403 )
309+
310+ // IGNORE self-signed certs
311+ {
312+ proxyOpts . rejectUnauthorized = false
313+ const response = await fetch ( testUrl , {
314+ agent : new PatchedHttpsProxyAgent ( proxyUrl , proxyOpts ) ,
315+ headers
316+ } )
317+ expect ( response . ok ) . toEqual ( false )
318+ expect ( response . status ) . toEqual ( 403 )
319+ }
320+ // DO NOT IGNORE self-signed certs
321+ {
322+ proxyOpts . rejectUnauthorized = true
323+ const proxyFetch = fetch ( testUrl , {
324+ agent : new PatchedHttpsProxyAgent ( proxyUrl , proxyOpts )
325+ } )
326+ await expect ( proxyFetch ) . rejects . toThrow ( 'self-signed certificate in certificate chain' )
327+ }
271328 } )
272329 } )
330+
331+ test ( 'createHttpsProxy (default options)' , async ( ) => {
332+ syswidecas . addCAs . mockRestore ( )
333+
334+ proxyServer = await createHttpsProxy ( )
335+ await proxyServer . stop ( )
336+
337+ expect ( syswidecas . addCAs ) . toHaveBeenCalled ( )
338+ } )
339+ } )
340+
341+ describe ( 'generateCert' , ( ) => {
342+ beforeEach ( ( ) => {
343+ syswidecas . addCAs . mockRestore ( )
344+ } )
345+
346+ test ( 'default (add root CAs)' , async ( ) => {
347+ await generateCert ( )
348+ expect ( syswidecas . addCAs ) . toHaveBeenCalled ( )
349+ } )
350+
351+ test ( 'do not add root CAs' , async ( ) => {
352+ await generateCert ( { addToRootCAs : false } )
353+ expect ( syswidecas . addCAs ) . not . toHaveBeenCalled ( )
354+ } )
273355} )
0 commit comments