You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

55 lines
2.3 KiB

11 months ago
  1. /** ATTN: SECURITY RESEARCHERS
  2. * To Security researchers about to submit an SSRF report CVE - please don't.
  3. * We are aware that the code below is does not defend against any of the thousands of ways
  4. * you can map a hostname to another IP via tunneling, hosts editing, etc. The code below does not have intention of blocking this
  5. * and is simply to prevent the user from accidentally putting in non-valid websites, which is all this protects
  6. * since _all urls must be submitted by the user anyway_ and cannot be done with authentication and manager or admin roles.
  7. * If an attacker has those roles then the system is already vulnerable and this is not a primary concern.
  8. *
  9. * We have gotten this report may times, marked them as duplicate or information and continue to get them. We communicate
  10. * already that deployment (and security) of an instance is on the deployer and system admin deploying it. This would include
  11. * isolation, firewalls, and the general security of the instance.
  12. */
  13. const VALID_PROTOCOLS = ["https:", "http:"];
  14. const INVALID_OCTETS = [192, 172, 10, 127];
  15. /**
  16. * If an ip address is passed in the user is attempting to collector some internal service running on internal/private IP.
  17. * This is not a security feature and simply just prevents the user from accidentally entering invalid IP addresses.
  18. * @param {URL} param0
  19. * @param {URL['hostname']} param0.hostname
  20. * @returns {boolean}
  21. */
  22. function isInvalidIp({ hostname }) {
  23. const IPRegex = new RegExp(
  24. /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/gi
  25. );
  26. // Not an IP address at all - passthrough
  27. if (!IPRegex.test(hostname)) return false;
  28. const [octetOne, ..._rest] = hostname.split(".");
  29. // If fails to validate to number - abort and return as invalid.
  30. if (isNaN(Number(octetOne))) return true;
  31. // Allow localhost loopback and 0.0.0.0 for scraping convenience
  32. // for locally hosted services or websites
  33. if (["127.0.0.1", "0.0.0.0"].includes(hostname)) return false;
  34. return INVALID_OCTETS.includes(Number(octetOne));
  35. }
  36. function validURL(url) {
  37. try {
  38. const destination = new URL(url);
  39. if (!VALID_PROTOCOLS.includes(destination.protocol)) return false;
  40. if (isInvalidIp(destination)) return false;
  41. return true;
  42. } catch {}
  43. return false;
  44. }
  45. module.exports = {
  46. validURL,
  47. };