diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f12615a..c25f9eb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this ### Fixed * [#1618](https://github.com/shlinkio/shlink/issues/1618) Fixed imported short URLs and visits dates not being set to the target server timezone. +* [#1578](https://github.com/shlinkio/shlink/issues/1578) Fixed short URL allowing an empty string as the domain during creation. ## [3.3.2] - 2022-10-18 diff --git a/module/Core/functions/functions.php b/module/Core/functions/functions.php index 814aa56a..e7dff2ad 100644 --- a/module/Core/functions/functions.php +++ b/module/Core/functions/functions.php @@ -78,6 +78,12 @@ function getOptionalBoolFromInputFilter(InputFilter $inputFilter, string $fieldN return $value !== null ? (bool) $value : null; } +function getNonEmptyOptionalValueFromInputFilter(InputFilter $inputFilter, string $fieldName): mixed +{ + $value = $inputFilter->getValue($fieldName); + return empty($value) ? null : $value; +} + function arrayToString(array $array, int $indentSize = 4): string { $indent = str_repeat(' ', $indentSize); diff --git a/module/Core/src/ShortUrl/Model/ShortUrlCreation.php b/module/Core/src/ShortUrl/Model/ShortUrlCreation.php index 9e37234c..bbdd9ab0 100644 --- a/module/Core/src/ShortUrl/Model/ShortUrlCreation.php +++ b/module/Core/src/ShortUrl/Model/ShortUrlCreation.php @@ -10,6 +10,7 @@ use Shlinkio\Shlink\Core\ShortUrl\Helper\TitleResolutionModelInterface; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; use Shlinkio\Shlink\Rest\Entity\ApiKey; +use function Shlinkio\Shlink\Core\getNonEmptyOptionalValueFromInputFilter; use function Shlinkio\Shlink\Core\getOptionalBoolFromInputFilter; use function Shlinkio\Shlink\Core\getOptionalIntFromInputFilter; use function Shlinkio\Shlink\Core\normalizeOptionalDate; @@ -74,7 +75,7 @@ final class ShortUrlCreation implements TitleResolutionModelInterface $this->maxVisits = getOptionalIntFromInputFilter($inputFilter, ShortUrlInputFilter::MAX_VISITS); $this->findIfExists = $inputFilter->getValue(ShortUrlInputFilter::FIND_IF_EXISTS); $this->validateUrl = getOptionalBoolFromInputFilter($inputFilter, ShortUrlInputFilter::VALIDATE_URL) ?? false; - $this->domain = $inputFilter->getValue(ShortUrlInputFilter::DOMAIN); + $this->domain = getNonEmptyOptionalValueFromInputFilter($inputFilter, ShortUrlInputFilter::DOMAIN); $this->shortCodeLength = getOptionalIntFromInputFilter( $inputFilter, ShortUrlInputFilter::SHORT_CODE_LENGTH, diff --git a/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php b/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php index 0f9dc419..51457264 100644 --- a/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php +++ b/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php @@ -146,4 +146,26 @@ class ShortUrlCreationTest extends TestCase yield [str_pad('', 600, 'd'), str_pad('', 512, 'd')]; yield [str_pad('', 800, 'e'), str_pad('', 512, 'e')]; } + + /** + * @test + * @dataProvider provideDomains + */ + public function emptyDomainIsDiscarded(?string $domain, ?string $expectedDomain): void + { + $meta = ShortUrlCreation::fromRawData([ + 'domain' => $domain, + 'longUrl' => '', + ]); + + self::assertSame($expectedDomain, $meta->getDomain()); + } + + public function provideDomains(): iterable + { + yield 'null domain' => [null, null]; + yield 'empty domain' => ['', null]; + yield 'trimmable domain' => [' ', null]; + yield 'valid domain' => ['doma.in', 'doma.in']; + } }