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

feat: introduce envvar to control HTTP-HTTPS upgrade behavior #1778

Merged
merged 2 commits into from Mar 30, 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
27 changes: 18 additions & 9 deletions help/commands-docs/_ENVIRONMENT.md
Expand Up @@ -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)<br />
[How to use Service Accounts](https://snyk.co/ucT6L)<br />

- `SNYK_API`:
Sets API host to use for Snyk requests. Useful for on-premise instances and configuring proxies.

- `SNYK_CFG_`<KEY>:
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.
JackuB marked this conversation as resolved.
Show resolved Hide resolved

- `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`.
8 changes: 6 additions & 2 deletions src/lib/request/request.ts
Expand Up @@ -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:';
Expand All @@ -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;
}

Expand Down
43 changes: 41 additions & 2 deletions test/request.test.ts
Expand Up @@ -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'],
Expand All @@ -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({
Expand Down Expand Up @@ -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));
});