CVE-2022-0086 (JSON, published 2022-01-04T18:15Z)
Description: value: uppy is vulnerable to Server-Side Request Forgery (SSRF)
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-918 \u2014 Server-Side Request Forgery (SSRF)
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
Repository: https://github.com/transloadit/uppy
Files changed:
packages/@uppy/companion/package.json
packages/@uppy/companion/src/server/helpers/request.js
yarn.lock
Commit message: improve private ip check (#3403)

https://huntr.dev/bounties/c1c03ef6-3f18-4976-a9ad-08c251279122/

Vulnerable code:
    "ip-address": "6.2.0",
const ipAddress = require('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
}
      if (isPrivateIP(record.address)) {
    if (isIPAddress(options.host) && isPrivateIP(options.host)) {
    if (isIPAddress(options.host) && isPrivateIP(options.host)) {
    ip-address: 6.2.0
"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

"jsbn@npm:1.1.0":
  version: 1.1.0
  resolution: "jsbn@npm:1.1.0"
  checksum: 944f924f2bd67ad533b3850eee47603eed0f6ae425fd1ee8c760f477e8c34a05f144c1bd4f5a5dd1963141dc79a2c55f89ccc5ab77d039e7077f3ad196b64965
  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.max@npm:4.0.1":
  version: 4.0.1
  resolution: "lodash.max@npm:4.0.1"
  checksum: f887b68db054edabe3a4f4877a7b7d0bef6e4a29e9371ea4d711a6ed0ca2393659a879158c609649aabd0337528ec58ed5b1e9581fed536ec0b8ae45a6e5f278
  languageName: node
  linkType: hard

"lodash.merge@npm:4.6.2, lodash.merge@npm:^4.6.1, lodash.merge@npm:^4.6.2":
"lodash.padstart@npm:4.6.1":
  version: 4.6.1
  resolution: "lodash.padstart@npm:4.6.1"
  checksum: 0d6ad92c626d351db85de539e41df3238d7d36c5fbfc5f57c4f060c90c73ad9f1db566463487795fdf0bf290a8f133189a0bd91d051032f6eb2d15b7e1863b5e
  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

"sprintf-js@npm:1.1.2, sprintf-js@npm:^1.0.3, sprintf-js@npm:^1.1.2":

Fixed code:
    "ipaddr.js": "^2.0.1",
// eslint-disable-next-line max-classes-per-file
const ipaddr = require('ipaddr.js')

// 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'
      if (isDisallowedIP(record.address)) {
    if (ipaddr.isValid(options.host) && isDisallowedIP(options.host)) {
    if (ipaddr.isValid(options.host) && isDisallowedIP(options.host)) {
    ipaddr.js: ^2.0.1
"lodash.merge@npm:^4.6.1, lodash.merge@npm:^4.6.2":
"sprintf-js@npm:^1.0.3, sprintf-js@npm:^1.1.2":