diff --git a/README.md b/README.md index 83e2f67..5398a92 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ The field under validation must be a valid [creditcard number](https://en.wikipe The field under validation must be a valid [Data URI](https://en.wikipedia.org/wiki/Data_URI_scheme). - public Intervention\Validation\Rules\DataUri::__construct() + public Intervention\Validation\Rules\DataUri::__construct(?array $media_types = null) ### Domain name diff --git a/src/Rules/DataUri.php b/src/Rules/DataUri.php index 1c16d50..e3c6023 100644 --- a/src/Rules/DataUri.php +++ b/src/Rules/DataUri.php @@ -6,6 +6,16 @@ class DataUri extends AbstractRule { + /** + * Create new instance with allowed media types or null for all valid media types + * + * @param null|array $media_types + * @return void + */ + public function __construct(protected ?array $media_types = null) + { + } + /** * Determine if the validation rule passes. * @@ -15,7 +25,7 @@ class DataUri extends AbstractRule public function isValid(mixed $value): bool { $info = $this->dataUriInfo($value); - if (! $info->isValid()) { + if (!$info->isValid()) { return false; } @@ -23,6 +33,14 @@ public function isValid(mixed $value): bool return false; } + if ($this->expectsMediaType() && !$info->hasMediaType()) { + return false; + } + + if ($this->expectsMediaType() && !$this->isAllowedMimeType($info->mediaType())) { + return false; + } + if ($info->isBase64Encoded()) { return $this->isValidBase64EncodedValue($info->data()); } @@ -30,11 +48,52 @@ public function isValid(mixed $value): bool return true; } + /** + * Determine if the rule expects a set mime type in the data url + * + * @return bool + */ + protected function expectsMediaType(): bool + { + return is_array($this->media_types); + } + + /** + * Check for validity of given mime type + * + * @param mixed $value + * @return bool + */ protected function isValidMimeType(mixed $value): bool { return (new MimeType())->isValid($value); } + /** + * Check if give mime type is allowed + * + * @param mixed $type + * @return bool + */ + protected function isAllowedMimeType(mixed $type): bool + { + if (is_null($this->media_types)) { + return true; + } + + if (count($this->media_types) === 0) { + return false; + } + + foreach ($this->media_types as $allowed) { + if ($type === $allowed) { + return true; + } + } + + return false; + } + protected function isValidBase64EncodedValue(mixed $value): bool { return (new Base64())->isValid($value); @@ -50,7 +109,7 @@ protected function dataUriInfo($value): object $pattern = "/^data:(?P\w+\/[-+.\w]+)?(?P(;[-\w]+=[-\w]+)*)(?P;base64)?,(?P.*)/"; $result = preg_match($pattern, $value, $matches); - return new class ($matches, $result) + return new class($matches, $result) { private $matches; private $result; diff --git a/tests/Rules/DataUriTest.php b/tests/Rules/DataUriTest.php index 1b8f4af..579b57d 100644 --- a/tests/Rules/DataUriTest.php +++ b/tests/Rules/DataUriTest.php @@ -16,6 +16,15 @@ public function testValidation($result, $value) $this->assertEquals($result, $valid); } + /** + * @dataProvider dataProviderImages + */ + public function testValidationWithMimeTypes($result, $value) + { + $valid = (new DataUri(['image/jpeg', 'image/png']))->isValid($value); + $this->assertEquals($result, $valid); + } + public function dataProvider() { return [ @@ -44,4 +53,33 @@ public function dataProvider() [false, 'data:text;base64,SGVsbG8sIFdvcmxkIQ=='], ]; } + + public function dataProviderImages() + { + return [ + [false, 'data:,'], + [false, 'data:,foo'], + [false, 'data:;base64,Zm9v'], + [false, 'data:,foo%20bar'], + [true, 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='], + [false, 'data:text/vnd-example+xyz;foo=bar;base64,R0lGODdh'], + [false, 'data:text/vnd-example+xyz;foo=bar;bar-baz=false;base64,R0lGODdh'], + [false, 'data:text/plain;charset=UTF-8;page=21,the%20data:1234,5678'], + [false, 'data:text/plain;charset=US-ASCII,foobar'], + [false, 'data:text/plain,foobar'], + [false, 'data:,VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy='], + [false, 'data:,Hello%2C%20World%21'], + [false, 'data:text/plain;base64,SGVsbG8sIFdvcmxkIQ=='], + [false, 'data:text/html,'], + [false, 'foo'], + [false, 'bar'], + [false, 'data:'], + [false, 'data:;base64,foo'], + [false, 'data:foo/plain,foobar'], + [false, 'data:;base64,VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy='], + [false, 'data:image/jpeg;base64,VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy='], + [false, 'VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZy4='], + [false, 'data:text;base64,SGVsbG8sIFdvcmxkIQ=='], + ]; + } }