diff --git a/package.json b/package.json index dff1939..f3ea4bf 100644 --- a/package.json +++ b/package.json @@ -42,15 +42,18 @@ "global-agent": "3.0.0", "leaky-bucket-queue": "0.0.2", "lodash": "4.17.21", + "proxy-from-env": "^1.1.0", "snyk-config": "^5.0.1", "source-map-support": "^0.5.16", "tslib": "^1.10.0", "uuid": "^8.0.0" }, "devDependencies": { + "@types/global-agent": "^2.1.1", "@types/jest": "^25.1.1", "@types/lodash": "4.14.186", "@types/node": "^12.12.26", + "@types/proxy-from-env": "^1.0.2", "@typescript-eslint/eslint-plugin": "^2.18.0", "@typescript-eslint/parser": "^2.18.0", "eslint": "^6.8.0", diff --git a/src/lib/request/request.ts b/src/lib/request/request.ts index 613a234..3c004f0 100644 --- a/src/lib/request/request.ts +++ b/src/lib/request/request.ts @@ -3,7 +3,8 @@ import * as Error from '../customErrors/apiError'; // Fixes issue https://github.com/axios/axios/issues/3384 // where HTTPS over HTTP Proxy Fails with 500 handshakefailed on mcafee proxy -import 'global-agent/bootstrap'; +import { bootstrap } from 'global-agent'; +import { getProxyForUrl } from 'proxy-from-env'; const DEFAULT_API = 'https://snyk.io/api/v1'; const DEFAULT_REST_API = 'https://api.snyk.io/rest/'; @@ -16,6 +17,16 @@ interface SnykRequest { useRESTApi?: boolean; } +if (process.env.HTTP_PROXY || process.env.http_proxy) { + process.env.HTTP_PROXY = process.env.HTTP_PROXY || process.env.http_proxy; +} +if (process.env.HTTPS_PROXY || process.env.https_proxy) { + process.env.HTTPS_PROXY = process.env.HTTPS_PROXY || process.env.https_proxy; +} +if (process.env.NP_PROXY || process.env.no_proxy) { + process.env.NO_PROXY = process.env.NO_PROXY || process.env.no_proxy; +} + const getTopParentModuleName = (parent: NodeModule | null): string => { if (parent == null) { return ''; @@ -35,6 +46,12 @@ const makeSnykRequest = async ( apiUrlREST = DEFAULT_REST_API, userAgentPrefix = '', ): Promise> => { + const proxyUri = getProxyForUrl(request.useRESTApi ? apiUrlREST : apiUrl); + if (proxyUri) { + bootstrap({ + environmentVariableNamespace: '', + }); + } const topParentModuleName = getTopParentModuleName(module.parent as any); const userAgentPrefixChecked = userAgentPrefix != '' && !userAgentPrefix.endsWith('/') @@ -48,15 +65,34 @@ const makeSnykRequest = async ( Authorization: 'token ' + snykToken, 'User-Agent': `${topParentModuleName}${userAgentPrefixChecked}tech-services/snyk-request-manager/1.0`, }; + let apiClient; + if (proxyUri) { + apiClient = axios.create({ + baseURL: request.useRESTApi ? apiUrlREST : apiUrl, + responseType: 'json', + headers: { ...requestHeaders, ...request.headers }, + transitional: { + clarifyTimeoutError: true, + }, + timeout: 30_000, // 5 mins same as Snyk APIs + proxy: false, // disables axios built-in proxy to let bootstrap work + }); + } else { + apiClient = axios.create({ + baseURL: request.useRESTApi ? apiUrlREST : apiUrl, + responseType: 'json', + headers: { ...requestHeaders, ...request.headers }, + transitional: { + clarifyTimeoutError: true, + }, + timeout: 30_000, // 5 mins same as Snyk APIs + }); + } - const apiClient = axios.create({ - baseURL: request.useRESTApi ? apiUrlREST : apiUrl, - responseType: 'json', - headers: { ...requestHeaders, ...request.headers }, - transitional: { - clarifyTimeoutError: true, - }, - timeout: 30_000, // 5 mins same as Snyk APIs + // sanitize error to avoid leaking sensitive data + apiClient.interceptors.response.use(undefined, async (error) => { + error.config.headers.Authorization = '****'; + return Promise.reject(error); }); // sanitize error to avoid leaking sensitive data apiClient.interceptors.response.use(undefined, async (error) => {