1<?php 2/** 3 * Zend Framework (http://framework.zend.com/) 4 * 5 * @link http://github.com/zendframework/zf2 for the canonical source repository 6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 7 * @license http://framework.zend.com/license/new-bsd New BSD License 8 */ 9 10namespace Zend\Validator; 11 12use RecursiveArrayIterator; 13use RecursiveIteratorIterator; 14 15class InArray extends AbstractValidator 16{ 17 const NOT_IN_ARRAY = 'notInArray'; 18 19 // Type of Strict check 20 /** 21 * standard in_array strict checking value and type 22 */ 23 const COMPARE_STRICT = 1; 24 25 /** 26 * Non strict check but prevents "asdf" == 0 returning TRUE causing false/positive. 27 * This is the most secure option for non-strict checks and replaces strict = false 28 * This will only be effective when the input is a string 29 */ 30 const COMPARE_NOT_STRICT_AND_PREVENT_STR_TO_INT_VULNERABILITY = 0; 31 32 /** 33 * Standard non-strict check where "asdf" == 0 returns TRUE 34 * This will be wanted when comparing "0" against int 0 35 */ 36 const COMPARE_NOT_STRICT = -1; 37 38 /** 39 * @var array 40 */ 41 protected $messageTemplates = array( 42 self::NOT_IN_ARRAY => 'The input was not found in the haystack', 43 ); 44 45 /** 46 * Haystack of possible values 47 * 48 * @var array 49 */ 50 protected $haystack; 51 52 /** 53 * Type of strict check to be used. Due to "foo" == 0 === TRUE with in_array when strict = false, 54 * an option has been added to prevent this. When $strict = 0/false, the most 55 * secure non-strict check is implemented. if $strict = -1, the default in_array non-strict 56 * behaviour is used 57 * 58 * @var int 59 */ 60 protected $strict = self::COMPARE_NOT_STRICT_AND_PREVENT_STR_TO_INT_VULNERABILITY; 61 62 /** 63 * Whether a recursive search should be done 64 * 65 * @var bool 66 */ 67 protected $recursive = false; 68 69 /** 70 * Returns the haystack option 71 * 72 * @return mixed 73 * @throws Exception\RuntimeException if haystack option is not set 74 */ 75 public function getHaystack() 76 { 77 if ($this->haystack === null) { 78 throw new Exception\RuntimeException('haystack option is mandatory'); 79 } 80 return $this->haystack; 81 } 82 83 /** 84 * Sets the haystack option 85 * 86 * @param mixed $haystack 87 * @return InArray Provides a fluent interface 88 */ 89 public function setHaystack(array $haystack) 90 { 91 $this->haystack = $haystack; 92 return $this; 93 } 94 95 /** 96 * Returns the strict option 97 * 98 * @return bool|int 99 */ 100 public function getStrict() 101 { 102 // To keep BC with new strict modes 103 if ($this->strict == self::COMPARE_NOT_STRICT_AND_PREVENT_STR_TO_INT_VULNERABILITY 104 || $this->strict == self::COMPARE_STRICT 105 ) { 106 return (bool) $this->strict; 107 } 108 return $this->strict; 109 } 110 111 /** 112 * Sets the strict option mode 113 * InArray::COMPARE_STRICT | InArray::COMPARE_NOT_STRICT_AND_PREVENT_STR_TO_INT_VULNERABILITY | InArray::COMPARE_NOT_STRICT 114 * 115 * @param int $strict 116 * @return InArray Provides a fluent interface 117 * @throws Exception\InvalidArgumentException 118 */ 119 public function setStrict($strict) 120 { 121 $checkTypes = array( 122 self::COMPARE_NOT_STRICT_AND_PREVENT_STR_TO_INT_VULNERABILITY, // 0 123 self::COMPARE_STRICT, // 1 124 self::COMPARE_NOT_STRICT // -1 125 ); 126 127 // validate strict value 128 if (!in_array($strict, $checkTypes)) { 129 throw new Exception\InvalidArgumentException('Strict option must be one of the COMPARE_ constants'); 130 } 131 132 $this->strict = $strict; 133 return $this; 134 } 135 136 /** 137 * Returns the recursive option 138 * 139 * @return bool 140 */ 141 public function getRecursive() 142 { 143 return $this->recursive; 144 } 145 146 /** 147 * Sets the recursive option 148 * 149 * @param bool $recursive 150 * @return InArray Provides a fluent interface 151 */ 152 public function setRecursive($recursive) 153 { 154 $this->recursive = (bool) $recursive; 155 return $this; 156 } 157 158 /** 159 * Returns true if and only if $value is contained in the haystack option. If the strict 160 * option is true, then the type of $value is also checked. 161 * 162 * @param mixed $value 163 * See {@link http://php.net/manual/function.in-array.php#104501} 164 * @return bool 165 */ 166 public function isValid($value) 167 { 168 // we create a copy of the haystack in case we need to modify it 169 $haystack = $this->getHaystack(); 170 171 // if the input is a string or float, and vulnerability protection is on 172 // we type cast the input to a string 173 if (self::COMPARE_NOT_STRICT_AND_PREVENT_STR_TO_INT_VULNERABILITY == $this->strict 174 && (is_int($value) || is_float($value))) { 175 $value = (string) $value; 176 } 177 178 $this->setValue($value); 179 180 if ($this->getRecursive()) { 181 $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($haystack)); 182 foreach ($iterator as $element) { 183 if (self::COMPARE_STRICT == $this->strict) { 184 if ($element === $value) { 185 return true; 186 } 187 } else { 188 // add protection to prevent string to int vuln's 189 $el = $element; 190 if (self::COMPARE_NOT_STRICT_AND_PREVENT_STR_TO_INT_VULNERABILITY == $this->strict 191 && is_string($value) && (is_int($el) || is_float($el)) 192 ) { 193 $el = (string) $el; 194 } 195 196 if ($el == $value) { 197 return true; 198 } 199 } 200 } 201 } else { 202 /** 203 * If the check is not strict, then, to prevent "asdf" being converted to 0 204 * and returning a false positive if 0 is in haystack, we type cast 205 * the haystack to strings. To prevent "56asdf" == 56 === TRUE we also 206 * type cast values like 56 to strings as well. 207 * 208 * This occurs only if the input is a string and a haystack member is an int 209 */ 210 if (self::COMPARE_NOT_STRICT_AND_PREVENT_STR_TO_INT_VULNERABILITY == $this->strict 211 && is_string($value) 212 ) { 213 foreach ($haystack as &$h) { 214 if (is_int($h) || is_float($h)) { 215 $h = (string) $h; 216 } 217 } 218 } 219 220 if (in_array($value, $haystack, self::COMPARE_STRICT == $this->strict)) { 221 return true; 222 } 223 } 224 225 $this->error(self::NOT_IN_ARRAY); 226 return false; 227 } 228} 229