Merge pull request #21281 from civicrm/5.41
[civicrm-core.git] / CRM / Utils / Rule.php
index 46402eebdca70c8a1b2c970751974d08aa1600ec..862e4603f74740b6c3137c55609e2e3d226d00fd 100644 (file)
@@ -625,6 +625,10 @@ class CRM_Utils_Rule {
    * @return bool
    */
   public static function boolean($value) {
+    if ($value === TRUE || $value === FALSE) {
+      return TRUE;
+    }
+    // This is intentionally not using === comparison - but will fail on FALSE.
     return preg_match(
       '/(^(1|0)$)|(^(Y(es)?|N(o)?)$)|(^(T(rue)?|F(alse)?)$)/i', $value
     ) ? TRUE : FALSE;
@@ -635,12 +639,46 @@ class CRM_Utils_Rule {
    *
    * @return bool
    */
-  public static function email($value) {
+  public static function email($value): bool {
+    if (function_exists('idn_to_ascii')) {
+      $parts = explode('@', $value);
+      foreach ($parts as &$part) {
+        // if the function returns FALSE then let filter_var have at it.
+        $part = self::idnToAsci($part) ?: $part;
+        if ($part === 'localhost') {
+          // if we are in a dev environment add .com to trick it into accepting localhost.
+          // this is a bit best-effort - ie we don't really care that it's in a bigger if.
+          $part .= '.com';
+        }
+      }
+      $value = implode('@', $parts);
+    }
     return (bool) filter_var($value, FILTER_VALIDATE_EMAIL);
   }
 
   /**
-   * @param $list
+   * Convert domain string to ascii.
+   *
+   * See https://lab.civicrm.org/dev/core/-/issues/2769
+   * and also discussion over in guzzle land
+   * https://github.com/guzzle/guzzle/pull/2454
+   *
+   * @param string $string
+   *
+   * @return string|false
+   */
+  private static function idnToAsci(string $string) {
+    if (!\extension_loaded('intl')) {
+      return $string;
+    }
+    if (defined('INTL_IDNA_VARIANT_UTS46')) {
+      return idn_to_ascii($string, 0, INTL_IDNA_VARIANT_UTS46);
+    }
+    return idn_to_ascii($string);
+  }
+
+  /**
+   * @param string $list
    *
    * @return bool
    */
@@ -801,22 +839,6 @@ class CRM_Utils_Rule {
     return FALSE;
   }
 
-  /**
-   * @param $value
-   *
-   * @return bool
-   */
-  public static function xssString($value) {
-    if (is_string($value)) {
-      return preg_match('!<(vb)?script[^>]*>.*</(vb)?script.*>!ims',
-        $value
-      ) ? FALSE : TRUE;
-    }
-    else {
-      return TRUE;
-    }
-  }
-
   /**
    * Validate json string for xss
    *
@@ -826,9 +848,6 @@ class CRM_Utils_Rule {
    *   False if invalid, true if valid / safe.
    */
   public static function json($value) {
-    if (!self::xssString($value)) {
-      return FALSE;
-    }
     $array = json_decode($value, TRUE);
     if (!$array || !is_array($array)) {
       return FALSE;
@@ -974,13 +993,10 @@ class CRM_Utils_Rule {
   protected static function arrayValue($array) {
     foreach ($array as $key => $item) {
       if (is_array($item)) {
-        if (!self::xssString($key) || !self::arrayValue($item)) {
+        if (!self::arrayValue($item)) {
           return FALSE;
         }
       }
-      if (!self::xssString($key) || !self::xssString($item)) {
-        return FALSE;
-      }
     }
     return TRUE;
   }