diff --git a/help/commands-docs/_ENVIRONMENT.md b/help/commands-docs/_ENVIRONMENT.md index e56fcfed29a..b173949e0fb 100644 --- a/help/commands-docs/_ENVIRONMENT.md +++ b/help/commands-docs/_ENVIRONMENT.md @@ -8,19 +8,28 @@ You can set these environment variables to change CLI run settings. [How to get your account token](https://snyk.co/ucT6J)
[How to use Service Accounts](https://snyk.co/ucT6L)
-- `SNYK_API`: - Sets API host to use for Snyk requests. Useful for on-premise instances and configuring proxies. - - `SNYK_CFG_`: Allows you to override any key that's also available as `snyk config` option. E.g. `SNYK_CFG_ORG`=myorg will override default org option in `config` with "myorg". - `SNYK_REGISTRY_USERNAME`: - Specify a username to use when connecting to a container registry. Note that using the `--username` flag will - override this value. This will be ignored in favour of local Docker binary credentials when Docker is present. - + Specify a username to use when connecting to a container registry. Note that using the `--username` flag will + override this value. This will be ignored in favour of local Docker binary credentials when Docker is present. + - `SNYK_REGISTRY_PASSWORD`: - Specify a password to use when connecting to a container registry. Note that using the `--password` flag will - override this value. This will be ignored in favour of local Docker binary credentials when Docker is present. - + Specify a password to use when connecting to a container registry. Note that using the `--password` flag will + override this value. This will be ignored in favour of local Docker binary credentials when Docker is present. + +## Connecting to Snyk API + +By default Snyk CLI will connect to `https://snyk.io/api/v1`. + +- `SNYK_API`: + Sets API host to use for Snyk requests. Useful for on-premise instances and configuring proxies. If set with `http` protocol CLI will upgrade the requests to `https`. Unless `SNYK_HTTP_PROTOCOL_UPGRADE` is set to `0`. + +- `SNYK_HTTP_PROTOCOL_UPGRADE`=0: + If set to the value of `0` the API requests aimed at `http` URLs will not be upgraded to `https`. Useful e.g., for reverse proxies. + +- `HTTPS_PROXY` and `HTTP_PROXY`: + Allows you to specify a proxy to use for `https` and `http` calls. The `https` in the `HTTPS_PROXY` means that _requests using `https` protocol_ will use this proxy. The proxy itself doesn't need to use `https`. diff --git a/src/lib/request/request.ts b/src/lib/request/request.ts index 0b51c14e1a9..1a0772df10a 100644 --- a/src/lib/request/request.ts +++ b/src/lib/request/request.ts @@ -71,7 +71,8 @@ export = function makeRequest( if ( parsedUrl.protocol === 'http:' && - parsedUrl.hostname !== 'localhost' + parsedUrl.hostname !== 'localhost' && + process.env.SNYK_HTTP_PROTOCOL_UPGRADE !== '0' ) { debug('forcing api request to https'); parsedUrl.protocol = 'https:'; @@ -95,7 +96,10 @@ export = function makeRequest( let url = payload.url; if (payload.qs) { - url = url + '?' + querystring.stringify(payload.qs); + // Parse the URL and append the search part - this will take care of adding the '/?' part if it's missing + const urlObject = new URL(url); + urlObject.search = querystring.stringify(payload.qs); + url = urlObject.toString(); delete payload.qs; } diff --git a/test/request.test.ts b/test/request.test.ts index d8b8fb92c86..dc455e5bd1b 100644 --- a/test/request.test.ts +++ b/test/request.test.ts @@ -139,7 +139,7 @@ test('request with timeout calls needle as expected', (t) => { test('request with query string calls needle as expected', (t) => { needleStub.yields(null, { statusCode: 200 }, 'text'); const payload = { - url: 'http://test.stub', + url: 'https://test.stub', qs: { key: 'value', test: ['multi', 'value'], @@ -155,7 +155,7 @@ test('request with query string calls needle as expected', (t) => { t.ok( needleStub.calledWith( 'get', // default - 'https://test.stub/?key=value&test=multi&test=value', // turns http to https and appends querystring + 'https://test.stub/?key=value&test=multi&test=value', // appends querystring sinon.match.falsy, // no data sinon.match({ headers: sinon.match({ @@ -442,3 +442,42 @@ test('request rejects if needle fails', (t) => { t.equals(e, 'Unexpected Error', 'rejects error'); }); }); + +test('request calls needle as expected and will not update HTTP to HTTPS if envvar is set', (t) => { + process.env.SNYK_HTTP_PROTOCOL_UPGRADE = '0'; + needleStub.yields(null, { statusCode: 200 }, 'text'); + const payload = { + url: 'http://test.stub', + }; + return request(payload) + .then((response) => { + process.env.SNYK_HTTP_PROTOCOL_UPGRADE = '1'; + t.deepEquals( + response, + { res: { statusCode: 200 }, body: 'text' }, + 'response ok', + ); + t.ok( + needleStub.calledWith( + 'get', // default + 'http://test.stub', // won't upgrade http to https + sinon.match.falsy, // no data + sinon.match({ + headers: sinon.match({ + 'x-snyk-cli-version': sinon.match.string, // dynamic version + 'content-encoding': undefined, // should not set when no data + 'content-length': undefined, // should not be set when no data + }), + follow_max: 5, // default + timeout: 300000, // default + json: undefined, // default + agent: sinon.match.instanceOf(http.Agent), + rejectUnauthorized: undefined, // should not be set when not use insecure mode + }), + sinon.match.func, // callback function + ), + 'needle called as expected', + ); + }) + .catch((e) => t.fail('should not throw error', e)); +});