diff --git a/packages/@uppy/companion/package.json b/packages/@uppy/companion/package.json index de1701087b..153c72799a 100644 --- a/packages/@uppy/companion/package.json +++ b/packages/@uppy/companion/package.json @@ -45,7 +45,7 @@ "express-session": "1.17.1", "grant": "4.7.0", "helmet": "^4.6.0", - "ip-address": "6.2.0", + "ipaddr.js": "^2.0.1", "isobject": "3.0.1", "jsonwebtoken": "8.5.1", "lodash.merge": "^4.6.2", diff --git a/packages/@uppy/companion/src/server/helpers/request.js b/packages/@uppy/companion/src/server/helpers/request.js index bdd5bb4a23..48125a9682 100644 --- a/packages/@uppy/companion/src/server/helpers/request.js +++ b/packages/@uppy/companion/src/server/helpers/request.js @@ -1,83 +1,19 @@ +// eslint-disable-next-line max-classes-per-file const http = require('http') const https = require('https') const { URL } = require('url') const dns = require('dns') -const ipAddress = require('ip-address') const request = require('request') +const ipaddr = require('ipaddr.js') + const logger = require('../logger') const FORBIDDEN_IP_ADDRESS = 'Forbidden IP address' -function isIPAddress (address) { - const addressAsV6 = new ipAddress.Address6(address) - const addressAsV4 = new ipAddress.Address4(address) - return addressAsV6.isValid() || addressAsV4.isValid() -} - -/* eslint-disable max-len */ -/** - * Determine if a IP address provided is a private one. - * Return TRUE if it's the case, FALSE otherwise. - * Excerpt from: - * https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html#case-2---application-can-send-requests-to-any-external-ip-address-or-domain-name - * - * @param {string} ipAddress the ip address to validate - * @returns {boolean} - */ -/* eslint-enable max-len */ -function isPrivateIP (ipAddress) { - let isPrivate = false - // Build the list of IP prefix for V4 and V6 addresses - const ipPrefix = [] - // Add prefix for loopback addresses - ipPrefix.push('127.') - ipPrefix.push('0.') - // Add IP V4 prefix for private addresses - // See https://en.wikipedia.org/wiki/Private_network - ipPrefix.push('10.') - ipPrefix.push('172.16.') - ipPrefix.push('172.17.') - ipPrefix.push('172.18.') - ipPrefix.push('172.19.') - ipPrefix.push('172.20.') - ipPrefix.push('172.21.') - ipPrefix.push('172.22.') - ipPrefix.push('172.23.') - ipPrefix.push('172.24.') - ipPrefix.push('172.25.') - ipPrefix.push('172.26.') - ipPrefix.push('172.27.') - ipPrefix.push('172.28.') - ipPrefix.push('172.29.') - ipPrefix.push('172.30.') - ipPrefix.push('172.31.') - ipPrefix.push('192.168.') - ipPrefix.push('169.254.') - // Add IP V6 prefix for private addresses - // See https://en.wikipedia.org/wiki/Unique_local_address - // See https://en.wikipedia.org/wiki/Private_network - // See https://simpledns.com/private-ipv6 - ipPrefix.push('fc') - ipPrefix.push('fd') - ipPrefix.push('fe') - ipPrefix.push('ff') - ipPrefix.push('::1') - // Verify the provided IP address - // Remove whitespace characters from the beginning/end of the string - // and convert it to lower case - // Lower case is for preventing any IPV6 case bypass using mixed case - // depending on the source used to get the IP address - const ipToVerify = ipAddress.trim().toLowerCase() - // Perform the check against the list of prefix - for (const prefix of ipPrefix) { - if (ipToVerify.startsWith(prefix)) { - isPrivate = true - break - } - } - - return isPrivate -} +// Example scary IPs that should return false (ipv6-to-ipv4 mapped): +// ::FFFF:127.0.0.1 +// ::ffff:7f00:1 +const isDisallowedIP = (ipAddress) => ipaddr.parse(ipAddress).range() !== 'unicast' module.exports.FORBIDDEN_IP_ADDRESS = FORBIDDEN_IP_ADDRESS @@ -115,7 +51,7 @@ function dnsLookup (hostname, options, callback) { const toValidate = Array.isArray(addresses) ? addresses : [{ address: addresses }] for (const record of toValidate) { - if (isPrivateIP(record.address)) { + if (isDisallowedIP(record.address)) { callback(new Error(FORBIDDEN_IP_ADDRESS), addresses, maybeFamily) return } @@ -127,7 +63,7 @@ function dnsLookup (hostname, options, callback) { class HttpAgent extends http.Agent { createConnection (options, callback) { - if (isIPAddress(options.host) && isPrivateIP(options.host)) { + if (ipaddr.isValid(options.host) && isDisallowedIP(options.host)) { callback(new Error(FORBIDDEN_IP_ADDRESS)) return undefined } @@ -138,7 +74,7 @@ class HttpAgent extends http.Agent { class HttpsAgent extends https.Agent { createConnection (options, callback) { - if (isIPAddress(options.host) && isPrivateIP(options.host)) { + if (ipaddr.isValid(options.host) && isDisallowedIP(options.host)) { callback(new Error(FORBIDDEN_IP_ADDRESS)) return undefined } diff --git a/yarn.lock b/yarn.lock index bb517c5744..e85818443f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8100,7 +8100,7 @@ __metadata: grant: 4.7.0 helmet: ^4.6.0 into-stream: ^6.0.0 - ip-address: 6.2.0 + ipaddr.js: ^2.0.1 isobject: 3.0.1 jsonwebtoken: 8.5.1 lodash.merge: ^4.6.2 @@ -23125,21 +23125,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard -"ip-address@npm:6.2.0": - version: 6.2.0 - resolution: "ip-address@npm:6.2.0" - dependencies: - jsbn: 1.1.0 - lodash.find: 4.6.0 - lodash.max: 4.0.1 - lodash.merge: 4.6.2 - lodash.padstart: 4.6.1 - lodash.repeat: 4.1.0 - sprintf-js: 1.1.2 - checksum: bcb93b4b13b3bf17d033fafce45850bf14c56366659ab627d3adbcd7a87ec64e219e7c6dc5cf990dddff5ac59db76520f2b1c7b5b05ea8d069162ca8a5112e44 - languageName: node - linkType: hard - "ip-regex@npm:^2.1.0": version: 2.1.0 resolution: "ip-regex@npm:2.1.0" @@ -25047,13 +25032,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard -"jsbn@npm:1.1.0": - version: 1.1.0 - resolution: "jsbn@npm:1.1.0" - checksum: 944f924f2bd67ad533b3850eee47603eed0f6ae425fd1ee8c760f477e8c34a05f144c1bd4f5a5dd1963141dc79a2c55f89ccc5ab77d039e7077f3ad196b64965 - languageName: node - linkType: hard - "jsbn@npm:~0.1.0": version: 0.1.1 resolution: "jsbn@npm:0.1.1" @@ -26431,13 +26409,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard -"lodash.find@npm:4.6.0": - version: 4.6.0 - resolution: "lodash.find@npm:4.6.0" - checksum: b737f849a4fe36f5c3664ea636780dda2fde18335021faf80cdfdcb300ed75441da6f55cfd6de119092d8bb2ddbc4433f4a8de4b99c0b9c8640465b0901c717c - languageName: node - linkType: hard - "lodash.flatten@npm:^4.4.0": version: 4.4.0 resolution: "lodash.flatten@npm:4.4.0" @@ -26543,13 +26514,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard -"lodash.max@npm:4.0.1": - version: 4.0.1 - resolution: "lodash.max@npm:4.0.1" - checksum: f887b68db054edabe3a4f4877a7b7d0bef6e4a29e9371ea4d711a6ed0ca2393659a879158c609649aabd0337528ec58ed5b1e9581fed536ec0b8ae45a6e5f278 - languageName: node - linkType: hard - "lodash.memoize@npm:4.1.2, lodash.memoize@npm:^4.1.2": version: 4.1.2 resolution: "lodash.memoize@npm:4.1.2" @@ -26564,7 +26528,7 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard -"lodash.merge@npm:4.6.2, lodash.merge@npm:^4.6.1, lodash.merge@npm:^4.6.2": +"lodash.merge@npm:^4.6.1, lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" checksum: ad580b4bdbb7ca1f7abf7e1bce63a9a0b98e370cf40194b03380a46b4ed799c9573029599caebc1b14e3f24b111aef72b96674a56cfa105e0f5ac70546cdc005 @@ -26585,13 +26549,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard -"lodash.padstart@npm:4.6.1": - version: 4.6.1 - resolution: "lodash.padstart@npm:4.6.1" - checksum: 0d6ad92c626d351db85de539e41df3238d7d36c5fbfc5f57c4f060c90c73ad9f1db566463487795fdf0bf290a8f133189a0bd91d051032f6eb2d15b7e1863b5e - languageName: node - linkType: hard - "lodash.pick@npm:^4.4.0": version: 4.4.0 resolution: "lodash.pick@npm:4.4.0" @@ -26606,13 +26563,6 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard -"lodash.repeat@npm:4.1.0": - version: 4.1.0 - resolution: "lodash.repeat@npm:4.1.0" - checksum: dac15fc59ed783678e1a9f986fefa180bfdbf95280852165965ecc8e15b871c6f0eaf7b325768a176014594d5186f1d6558fb72a18527bddd82539fb3ef8a4d3 - languageName: node - linkType: hard - "lodash.set@npm:^4.3.2": version: 4.3.2 resolution: "lodash.set@npm:4.3.2" @@ -37701,7 +37651,7 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis: languageName: node linkType: hard -"sprintf-js@npm:1.1.2, sprintf-js@npm:^1.0.3, sprintf-js@npm:^1.1.2": +"sprintf-js@npm:^1.0.3, sprintf-js@npm:^1.1.2": version: 1.1.2 resolution: "sprintf-js@npm:1.1.2" checksum: d4bb46464632b335e5faed381bd331157e0af64915a98ede833452663bc672823db49d7531c32d58798e85236581fb7342fd0270531ffc8f914e186187bf1c90