<?php

/**
 * Ahead WebSoft Technologies Pvt Ltd.
 * @copyright  Copyright 2006-2026 Ahead WebSoft Technologies Pvt Ltd.
 * @license    https://www.socialengine.com/policies
 */

class Core_Api_Security extends Core_Api_Abstract
{

	/**
	 * Validate external URL against SSRF
	 *
	 * @param string $url
	 * @return bool
	 */
	public function isValidExternalUrl($url)
	{
		// Empty check
		if (empty($url)) {
			return false;
		}

		// Basic URL validation
		if (!filter_var($url, FILTER_VALIDATE_URL)) {
			return false;
		}

		// Parse URL
		$parts = parse_url($url);

		if (empty($parts['scheme']) || empty($parts['host'])) {
			return false;
		}

		// Allow only HTTPS
		if (strtolower($parts['scheme']) !== 'https') {
			return false;
		}

		$host = strtolower(trim($parts['host']));

		// Block localhost-style hosts
    $blockedHosts = array(
			'localhost',
			'localhost.localdomain',
			'127.0.0.1',
			'0.0.0.0',
			'::1',
			'metadata.google.internal',
    );

		if (in_array($host, $blockedHosts)) {
			return false;
		}

		// Block direct IP access
    if (filter_var($host, FILTER_VALIDATE_IP)) {

      // Block private/reserved CIDRs
      if ($this->isPrivateIp($host)) {
        return false;
      }

      // Block ALL direct IP usage
      return false;
    }

		// Resolve DNS
		$ip = gethostbyname($host);

		// Failed resolution
		if ($ip === $host) {
			return false;
		}

		// Block private/internal/reserved IPs
    if ($this->isPrivateIp($ip)) {
      return false;
    }

		// Validate resolved IP
		if (
			!filter_var(
				$ip,
				FILTER_VALIDATE_IP,
				FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
			)
		) {
			return false;
		}

		// Optional blocked ports
    if (!empty($parts['port'])) {
			$blockedPorts = array(22, 23,25, 3306, 6379,11211,9200,27017,8080,8443,5000,7001);
			if (in_array($parts['port'], $blockedPorts)) {
				return false;
			}
    }

		return true;
	}

	/**
   * Check if IP belongs to private/internal ranges
   *
   * @param string $ip
   * @return bool
   */
  public function isPrivateIp($ip)
  {

    $privateRanges = array(

      // localhost
      '127.0.0.0|127.255.255.255',

      // private
      '10.0.0.0|10.255.255.255',
      '172.16.0.0|172.31.255.255',
      '192.168.0.0|192.168.255.255',

      // link local
      '169.254.0.0|169.254.255.255',

      // invalid
      '0.0.0.0|0.255.255.255',

      // carrier NAT
      '100.64.0.0|100.127.255.255',

      // multicast
      '224.0.0.0|239.255.255.255',

      // reserved
      '240.0.0.0|255.255.255.254',
    );

    $ipLong = ip2long($ip);

    if ($ipLong === false) {
      return true;
    }

    foreach ($privateRanges as $range) {

      list($start, $end) = explode('|', $range);

      $startLong = ip2long($start);
      $endLong = ip2long($end);

      if ($ipLong >= $startLong && $ipLong <= $endLong) {
        return true;
      }
    }
    return false;
  }
}
