mirror of
https://github.com/shlinkio/shlink.git
synced 2026-03-09 16:53:11 +08:00
Validate IP address patterns when creating ip-address redirect conditions
This commit is contained in:
@@ -12,6 +12,7 @@ use Shlinkio\Shlink\Common\Validation\InputFactory;
|
||||
use Shlinkio\Shlink\Core\Model\DeviceType;
|
||||
use Shlinkio\Shlink\Core\RedirectRule\Model\RedirectConditionType;
|
||||
use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter;
|
||||
use Shlinkio\Shlink\Core\Util\IpAddressUtils;
|
||||
|
||||
use function Shlinkio\Shlink\Core\ArrayUtils\contains;
|
||||
use function Shlinkio\Shlink\Core\enumValues;
|
||||
@@ -71,13 +72,14 @@ class RedirectRulesInputFilter extends InputFilter
|
||||
$redirectConditionInputFilter->add($type);
|
||||
|
||||
$value = InputFactory::basic(self::CONDITION_MATCH_VALUE, required: true);
|
||||
$value->getValidatorChain()->attach(new Callback(function (string $value, array $context) {
|
||||
if ($context[self::CONDITION_TYPE] === RedirectConditionType::DEVICE->value) {
|
||||
return contains($value, enumValues(DeviceType::class));
|
||||
}
|
||||
|
||||
return true;
|
||||
}));
|
||||
$value->getValidatorChain()->attach(new Callback(
|
||||
fn (string $value, array $context) => match ($context[self::CONDITION_TYPE]) {
|
||||
RedirectConditionType::DEVICE->value => contains($value, enumValues(DeviceType::class)),
|
||||
RedirectConditionType::IP_ADDRESS->value => IpAddressUtils::isStaticIpCidrOrWildcard($value),
|
||||
// RedirectConditionType::LANGUAGE->value => TODO,
|
||||
default => true,
|
||||
},
|
||||
));
|
||||
$redirectConditionInputFilter->add($value);
|
||||
|
||||
$redirectConditionInputFilter->add(
|
||||
|
||||
@@ -18,6 +18,11 @@ use function str_contains;
|
||||
|
||||
final class IpAddressUtils
|
||||
{
|
||||
public static function isStaticIpCidrOrWildcard(string $candidate): bool
|
||||
{
|
||||
return self::candidateToRange($candidate, ['0', '0', '0', '0']) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an IP address matches any of provided groups.
|
||||
* Every group can be a static IP address (100.200.80.40), a CIDR block (192.168.10.0/24) or a wildcard pattern
|
||||
@@ -40,15 +45,29 @@ final class IpAddressUtils
|
||||
|
||||
$ipAddressParts = explode('.', $ipAddress);
|
||||
|
||||
return some($groups, function (string $value) use ($ip, $ipAddressParts): bool {
|
||||
$range = str_contains($value, '*')
|
||||
? self::parseValueWithWildcards($value, $ipAddressParts)
|
||||
: Factory::parseRangeString($value);
|
||||
|
||||
return $range !== null && $ip->matches($range);
|
||||
return some($groups, function (string $group) use ($ip, $ipAddressParts): bool {
|
||||
$range = self::candidateToRange($group, $ipAddressParts);
|
||||
return $range !== null && $range->contains($ip);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a static IP, CIDR block or wildcard pattern into a Range object
|
||||
*
|
||||
* @param string[] $ipAddressParts
|
||||
*/
|
||||
private static function candidateToRange(string $candidate, array $ipAddressParts): ?RangeInterface
|
||||
{
|
||||
return str_contains($candidate, '*')
|
||||
? self::parseValueWithWildcards($candidate, $ipAddressParts)
|
||||
: Factory::parseRangeString($candidate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to generate an IP range from a wildcard pattern.
|
||||
* Factory::parseRangeString can usually do this automatically, but only if wildcards are at the end. This also
|
||||
* covers cases where wildcards are in between.
|
||||
*/
|
||||
private static function parseValueWithWildcards(string $value, array $ipAddressParts): ?RangeInterface
|
||||
{
|
||||
$octets = explode('.', $value);
|
||||
|
||||
Reference in New Issue
Block a user