Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(cli): Respect NO_PROXY on cypress download #17702

Merged
merged 4 commits into from Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 2 additions & 12 deletions cli/lib/tasks/download.js
Expand Up @@ -9,23 +9,14 @@ const request = require('@cypress/request')
const Promise = require('bluebird')
const requestProgress = require('request-progress')
const { stripIndent } = require('common-tags')
const getProxyFromURI = require('./getProxyFromURI')
westarne marked this conversation as resolved.
Show resolved Hide resolved

const { throwFormErrorText, errors } = require('../errors')
const fs = require('../fs')
const util = require('../util')

const defaultBaseUrl = 'https://download.cypress.io/'

const getProxyUrl = () => {
return process.env.HTTPS_PROXY ||
process.env.https_proxy ||
process.env.npm_config_https_proxy ||
process.env.HTTP_PROXY ||
process.env.http_proxy ||
process.env.npm_config_proxy ||
null
}

const getRealOsArch = () => {
// os.arch() returns the arch for which this node was compiled
// we want the operating system's arch instead: x64 or x86
Expand Down Expand Up @@ -205,7 +196,7 @@ const verifyDownloadedFile = (filename, expectedSize, expectedChecksum) => {
// {filename: ..., downloaded: true}
const downloadFromUrl = ({ url, downloadDestination, progress, ca }) => {
return new Promise((resolve, reject) => {
const proxy = getProxyUrl()
const proxy = getProxyFromURI(new URL(url))

debug('Downloading package', {
url,
Expand Down Expand Up @@ -357,6 +348,5 @@ const start = (opts) => {
module.exports = {
start,
getUrl,
getProxyUrl,
getCA,
}
82 changes: 82 additions & 0 deletions cli/lib/tasks/getProxyFromURI.js
@@ -0,0 +1,82 @@
'use strict'

function formatHostname (hostname) {
// canonicalize the hostname, so that 'oogle.com' won't match 'google.com'
return hostname.replace(/^\.*/, '.').toLowerCase()
}

function parseNoProxyZone (zone) {
zone = zone.trim().toLowerCase()

let zoneParts = zone.split(':', 2)
let zoneHost = formatHostname(zoneParts[0])
let zonePort = zoneParts[1]
let hasPort = zone.indexOf(':') > -1

return { hostname: zoneHost, port: zonePort, hasPort }
}

function uriInNoProxy (uri, noProxy) {
let port = uri.port || (uri.protocol === 'https:' ? '443' : '80')
let hostname = formatHostname(uri.hostname)
let noProxyList = noProxy.split(',')

// iterate through the noProxyList until it finds a match.
return noProxyList.map(parseNoProxyZone).some(function (noProxyZone) {
let isMatchedAt = hostname.indexOf(noProxyZone.hostname)
let hostnameMatched = (
isMatchedAt > -1 &&
(isMatchedAt === hostname.length - noProxyZone.hostname.length)
)

if (noProxyZone.hasPort) {
return (port === noProxyZone.port) && hostnameMatched
}

return hostnameMatched
})
}

function getProxyFromURI (uri) {
// Decide the proper request proxy to use based on the request URI object and the
// environmental variables (NO_PROXY, HTTP_PROXY, etc.)
// respect NO_PROXY environment variables (see: https://lynx.invisible-island.net/lynx2.8.7/breakout/lynx_help/keystrokes/environments.html)

let noProxy = process.env.NO_PROXY || process.env.no_proxy || ''

// if the noProxy is a wildcard then return null

if (noProxy === '*') {
return null
}

// if the noProxy is not empty and the uri is found return null

if (noProxy !== '' && uriInNoProxy(uri, noProxy)) {
return null
}

// Check for HTTP or HTTPS Proxy in environment Else default to null

if (uri.protocol === 'http:') {
return process.env.HTTP_PROXY ||
process.env.http_proxy ||
process.env.npm_config_proxy || null
}

if (uri.protocol === 'https:') {
return process.env.HTTPS_PROXY ||
process.env.https_proxy ||
process.env.npm_config_https_proxy ||
process.env.HTTP_PROXY ||
process.env.http_proxy ||
process.env.npm_config_proxy || null
}

// if none of that works, return null
// (What uri protocol are you using then?)

return null
}

module.exports = getProxyFromURI
26 changes: 0 additions & 26 deletions cli/test/lib/tasks/download_spec.js
Expand Up @@ -312,32 +312,6 @@ describe('lib/tasks/download', function () {
})
})

context('with proxy env vars', () => {
beforeEach(function () {
this.env = _.clone(process.env)
})

afterEach(function () {
process.env = this.env
})

it('prefers https_proxy over http_proxy', () => {
process.env.HTTP_PROXY = 'foo'
expect(download.getProxyUrl()).to.eq('foo')
process.env.https_proxy = 'bar'
expect(download.getProxyUrl()).to.eq('bar')
})

it('falls back to npm_config_proxy', () => {
process.env.npm_config_proxy = 'foo'
expect(download.getProxyUrl()).to.eq('foo')
process.env.npm_config_https_proxy = 'bar'
expect(download.getProxyUrl()).to.eq('bar')
process.env.https_proxy = 'baz'
expect(download.getProxyUrl()).to.eq('baz')
})
})

context('with CA and CAFILE env vars', () => {
beforeEach(function () {
this.env = _.clone(process.env)
Expand Down
62 changes: 62 additions & 0 deletions cli/test/lib/tasks/getProxyFromURI_spec.js
@@ -0,0 +1,62 @@
require('../../spec_helper')

const _ = require('lodash')
const os = require('os')
const getProxyFromURI = require(`${lib}/tasks/getProxyFromURI`)

const stdout = require('../../support/stdout')

describe('lib/tasks/getProxyFromURI', function () {
require('mocha-banner').register()

const testUri = new URL('https://anything.com')

beforeEach(function () {
this.stdout = stdout.capture()

os.platform.returns('darwin')
})

afterEach(function () {
stdout.restore()
})

context('with proxy env vars', () => {
beforeEach(function () {
this.env = _.clone(process.env)
// add a default no_proxy which does not match the testUri
process.env.NO_PROXY = 'localhost,.org'
})

afterEach(function () {
process.env = this.env
})

it('prefers https_proxy over http_proxy', () => {
process.env.HTTP_PROXY = 'foo'
expect(getProxyFromURI(testUri)).to.eq('foo')
process.env.https_proxy = 'bar'
expect(getProxyFromURI(testUri)).to.eq('bar')
})

it('falls back to npm_config_proxy', () => {
process.env.npm_config_proxy = 'foo'
expect(getProxyFromURI(testUri)).to.eq('foo')
process.env.npm_config_https_proxy = 'bar'
expect(getProxyFromURI(testUri)).to.eq('bar')
process.env.https_proxy = 'baz'
expect(getProxyFromURI(testUri)).to.eq('baz')
})

it('respects no_proxy', () => {
process.env.NO_PROXY = 'localhost,.com'

process.env.HTTP_PROXY = 'foo'
expect(getProxyFromURI(testUri)).to.eq(null)
process.env.npm_config_https_proxy = 'bar'
expect(getProxyFromURI(testUri)).to.eq(null)
process.env.https_proxy = 'baz'
expect(getProxyFromURI(testUri)).to.eq(null)
})
})
})