Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ unreleased:
send back only variable mutations after each script and rebuild full scopes
on the host, to cut per-script bridge copying
fixed bugs:
- >-
Requests blocked by `restrictedAddresses` now fail with an error whose
`code` is `ENOTFOUND`, as a real DNS failure does
- GH-1569 Fixed form-data File params after an array `src` File param being skipped
- Fixed a listener leak for nested request events after script execution
chores:
Expand Down
26 changes: 22 additions & 4 deletions lib/requester/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var dns = require('dns'),
SSL_OP_NO = 'SSL_OP_NO_',

ERROR_ADDRESS_RESOLVE = 'NETERR: getaddrinfo ENOTFOUND ',
ERROR_CODE_ADDRESS_RESOLVE = 'ENOTFOUND',

/**
* List of request methods without body.
Expand Down Expand Up @@ -274,6 +275,21 @@ var dns = require('dns'),
socket = null;
},

/**
* Builds the error for a request blocked by `restrictedAddresses`.
*
* @param {String} hostname
* @returns {Error}
*/
restrictedAddressError = function (hostname) {
var error = new Error(ERROR_ADDRESS_RESOLVE + hostname);

// same code Node sets on a real DNS failure, so consumers can tell it apart from a bare `Error`
error.code = ERROR_CODE_ADDRESS_RESOLVE;

return error;
},

/**
* Override DNS lookups in Node, to handle localhost as a special case.
* Chrome tries connecting to IPv6 by default, so we try the same thing.
Expand Down Expand Up @@ -308,7 +324,7 @@ var dns = require('dns'),
if (_.some(addresses, (addr) => {
return self.isAddressRestricted(addr && addr.address, networkOpts);
})) {
return callback(new Error(ERROR_ADDRESS_RESOLVE + hostname));
return callback(restrictedAddressError(hostname));
}

return callback(null, addresses);
Expand All @@ -320,7 +336,7 @@ var dns = require('dns'),

// error out if the resolved address is restricted
if (self.isAddressRestricted(addr, networkOpts)) {
return callback(new Error(ERROR_ADDRESS_RESOLVE + hostname));
return callback(restrictedAddressError(hostname));
}

return callback(null, addr, family);
Expand All @@ -332,7 +348,7 @@ var dns = require('dns'),
(self.isAddressRestricted(LOCALHOST, networkOpts) ||
self.isAddressRestricted(LOCAL_IPV4, networkOpts) ||
self.isAddressRestricted(LOCAL_IPV6, networkOpts))) {
return callback(new Error(ERROR_ADDRESS_RESOLVE + hostname));
return callback(restrictedAddressError(hostname));
}

// Try checking if we can connect to IPv6 localhost ('::1')
Expand Down Expand Up @@ -399,6 +415,8 @@ var dns = require('dns'),

module.exports = {

restrictedAddressError,

/**
* Creates a node request compatible options object from a request.
*
Expand Down Expand Up @@ -637,7 +655,7 @@ module.exports = {

if (self.isAddressRestricted(redirectHostname, networkOptions)) {
this.abort();
this.emit('error', new Error(ERROR_ADDRESS_RESOLVE + redirectHostname));
this.emit('error', restrictedAddressError(redirectHostname));
}
};

Expand Down
4 changes: 1 addition & 3 deletions lib/requester/requester.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ var _ = require('lodash'),
RESPONSE_START = 'responseStart',
RESPONSE_END = 'response',

ERROR_RESTRICTED_ADDRESS = 'NETERR: getaddrinfo ENOTFOUND ',

/**
* Headers which get overwritten by the requester.
*
Expand Down Expand Up @@ -432,7 +430,7 @@ class Requester extends EventEmitter {

// check if host is on the `restrictedAddresses`
if (networkOptions.restrictedAddresses && core.isAddressRestricted(hostname, networkOptions)) {
return onEnd(new Error(ERROR_RESTRICTED_ADDRESS + hostname));
return onEnd(core.restrictedAddressError(hostname));
}

return requests(request, requestOptions, onStart, onData, function (err, res, resBody, debug) {
Expand Down
1 change: 1 addition & 0 deletions test/integration/sanity/restricted-addresses.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ var expect = require('chai').expect;
// response will always be undefined because there is no server on this IP
// the error checks are the more important ones here
expect(error.message).to.equal('NETERR: getaddrinfo ENOTFOUND 169.254.169.254');
expect(error.code).to.equal('ENOTFOUND');
expect(response).to.be.undefined;
});

Expand Down
23 changes: 23 additions & 0 deletions test/unit/requester-core.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,28 @@ describe('requester util', function () {
expect(requesterCore.getRequestOptions(request, options).lookup).to.be.a('function');
});

it('should fail a single-address lookup to a restricted address with code ENOTFOUND', function (done) {
var request = new sdk.Request({
url: 'http://internal.example.test/get'
}),
lookup = requesterCore.getRequestOptions(request, {
network: {
restrictedAddresses: { '10.0.0.0/8': true },
hostLookup: {
type: 'hostIpMap',
hostIpMap: { 'internal.example.test': '10.1.2.3' }
}
}
}).lookup;

lookup('internal.example.test', { all: false }, function (err) {
expect(err).to.be.an('error');
expect(err.code).to.equal('ENOTFOUND');
expect(err.message).to.equal('NETERR: getaddrinfo ENOTFOUND internal.example.test');
done();
});
});

it('should override lookup function for hosts', function () {
var request = new sdk.Request({
url: 'https://postman-echo.com/get'
Expand Down Expand Up @@ -1006,6 +1028,7 @@ describe('requester util', function () {
expect(emittedError).to.be.an('error');
expect(emittedError.message).to.include('NETERR:');
expect(emittedError.message).to.include('127.0.0.2');
expect(emittedError.code).to.equal('ENOTFOUND');
});

it('bindOn.redirect should not abort for a non-restricted raw IP', function () {
Expand Down
Loading