Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,16 @@ jobs:
run: |
EXTENSIONS=$(jq -r '[(.require, ."require-dev", .suggest | select(. != null)) | keys[] | select(startswith("ext-")) | ltrimstr("ext-")] | unique | join(", ")' packages/${{ matrix.package }}/composer.json)
echo "extensions=$EXTENSIONS" >> "$GITHUB_OUTPUT"
# Test each package on the higher of its declared PHP floor and the
# baseline 8.4, so a package that requires a newer PHP (e.g. >=8.5)
# is tested there without dropping siblings below the baseline.
FLOOR=$(jq -r '.require.php // ""' packages/${{ matrix.package }}/composer.json | grep -oE '[0-9]+\.[0-9]+' | head -1)
PHP=$(printf '%s\n8.4\n' "$FLOOR" | grep -E '^[0-9]+\.[0-9]+$' | sort -V | tail -1)
echo "php=$PHP" >> "$GITHUB_OUTPUT"

- uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # ratchet:shivammathur/setup-php@v2
with:
php-version: '8.4'
php-version: ${{ steps.meta.outputs.php }}
extensions: ${{ steps.meta.outputs.extensions }}
coverage: none

Expand Down
2 changes: 1 addition & 1 deletion packages/validators/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
"test": "phpunit --configuration phpunit.xml"
},
"require": {
"php": ">=8.0"
"php": ">=8.5"
}
}
4 changes: 2 additions & 2 deletions packages/validators/composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions packages/validators/src/Validator/Contains.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Utopia\Validator;

use Utopia\Validator;
Expand Down Expand Up @@ -65,9 +67,9 @@ public function isValid($value): bool
}

foreach ($this->patterns as $pattern) {
$pattern = $this->strict ? $pattern : mb_strtolower($pattern, 'UTF-8');
$pattern = $this->strict ? $pattern : mb_strtolower((string) $pattern, 'UTF-8');

if (str_contains($value, $pattern)) {
if (str_contains($value, (string) $pattern)) {
return true;
}
}
Expand Down
6 changes: 4 additions & 2 deletions packages/validators/src/Validator/Domain.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Utopia\Validator;

use Utopia\Validator;
Expand Down Expand Up @@ -93,7 +95,7 @@ public function isValid($value): bool
$prefixDenyList = $restriction['prefixDenyList'];

// Only apply restriction rules to relevant domains
if (!str_ends_with($value, $hostname)) {
if (!str_ends_with($value, (string) $hostname)) {
continue;
}

Expand All @@ -109,7 +111,7 @@ public function isValid($value): bool
// Domain prefix (beginning) restriction
if (!empty($prefixDenyList)) {
foreach ($prefixDenyList as $deniedPrefix) {
if (str_starts_with($value, $deniedPrefix)) {
if (str_starts_with($value, (string) $deniedPrefix)) {
return false;
}
}
Expand Down
18 changes: 6 additions & 12 deletions packages/validators/src/Validator/Globstar.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,31 +50,25 @@ public function isValid($value): bool

$hasInclusions = false;
foreach ($this->patterns as $p) {
if (!str_starts_with($p, '!')) {
if (!str_starts_with((string) $p, '!')) {
$hasInclusions = true;
break;
}
}

// Pure-exclusion mode: default to valid; any matching exclusion invalidates.
if (!$hasInclusions) {
foreach ($this->patterns as $pattern) {
if ($this->match($value, substr($pattern, 1))) {
return false;
}
}

return true;
return array_all($this->patterns, fn($pattern): bool => !$this->match($value, substr((string) $pattern, 1)));
}

// Inclusion mode.
//
// Step 1 — literal (no *, ?, [) inclusion patterns always win:
// if any specific inclusion matches, the value is valid regardless of later exclusions.
$isWildcard = fn($p): bool => str_contains($p, '*') || str_contains($p, '?') || str_contains($p, '[');
$isWildcard = fn($p): bool => str_contains((string) $p, '*') || str_contains((string) $p, '?') || str_contains((string) $p, '[');

foreach ($this->patterns as $pattern) {
if (!str_starts_with($pattern, '!') && !$isWildcard($pattern) && $this->match($value, $pattern)) {
if (!str_starts_with((string) $pattern, '!') && !$isWildcard($pattern) && $this->match($value, $pattern)) {
return true;
}
}
Expand All @@ -85,8 +79,8 @@ public function isValid($value): bool
// Literal inclusions already handled above; skip them here.
$state = false;
foreach ($this->patterns as $pattern) {
if (str_starts_with($pattern, '!')) {
if ($this->match($value, substr($pattern, 1))) {
if (str_starts_with((string) $pattern, '!')) {
if ($this->match($value, substr((string) $pattern, 1))) {
$state = false;
}
} elseif ($isWildcard($pattern)) {
Expand Down
4 changes: 3 additions & 1 deletion packages/validators/src/Validator/Host.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Utopia\Validator;

use Utopia\Validator;
Expand Down Expand Up @@ -42,7 +44,7 @@ public function isValid($value): bool

$hostnameValidator = new Hostname($this->whitelist);

return $hostnameValidator->isValid(parse_url($value, PHP_URL_HOST));
return $hostnameValidator->isValid(parse_url((string) $value, PHP_URL_HOST));
}

/**
Expand Down
4 changes: 4 additions & 0 deletions packages/validators/src/Validator/Range.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public function getFormat(): string
*
* Returns validator description
*/
#[\Override]
public function getDescription(): string
{
return 'Value must be a valid range between ' . number_format($this->min) . ' and ' . number_format($this->max);
Expand All @@ -54,6 +55,7 @@ public function getDescription(): string
*
* Function will return true if object is array.
*/
#[\Override]
public function isArray(): bool
{
return false;
Expand All @@ -64,6 +66,7 @@ public function isArray(): bool
*
* Returns validator type.
*/
#[\Override]
public function getType(): string
{
return $this->format;
Expand All @@ -76,6 +79,7 @@ public function getType(): string
* Not strict, considers any valid integer to be a valid float
* Considers infinity to be a valid integer
*/
#[\Override]
public function isValid(mixed $value): bool
{
if (!parent::isValid($value)) {
Expand Down
36 changes: 32 additions & 4 deletions packages/validators/src/Validator/URL.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Utopia\Validator;

use Utopia\Validator;
Expand All @@ -13,7 +15,7 @@
*/
class URL extends Validator
{
public function __construct(protected array $allowedSchemes = [], protected bool $allowEmpty = false, protected bool $allowFragments = true) {}
public function __construct(protected array $allowedSchemes = [], protected bool $allowEmpty = false, protected bool $allowFragments = true, protected bool $allowPrivateUseSchemes = false) {}

/**
* Get Description
Expand Down Expand Up @@ -52,21 +54,47 @@ public function isValid($value): bool
return true;
}

if (filter_var($value, FILTER_VALIDATE_URL) === false) {
// FILTER_VALIDATE_URL rejects authority-less private-use URI schemes
// (e.g. "com.example.app:/oauth", RFC 8252 §7.1). Optionally accept those.
if (filter_var($value, FILTER_VALIDATE_URL) === false && (!$this->allowPrivateUseSchemes || !$this->isPrivateUseSchemeURI($value))) {
return false;
}

if ($this->allowedSchemes !== [] && !\in_array(parse_url($value, PHP_URL_SCHEME), $this->allowedSchemes)) {
if ($this->allowedSchemes !== [] && !\in_array(parse_url((string) $value, PHP_URL_SCHEME), $this->allowedSchemes)) {
return false;
}

if (!$this->allowFragments && parse_url($value, PHP_URL_FRAGMENT) !== null) {
if (!$this->allowFragments && parse_url((string) $value, PHP_URL_FRAGMENT) !== null) {
return false;
}

return true;
}

/**
* Is private-use URI scheme
* Returns true when $value is an authority-less private-use URI scheme
* redirect URI as defined by RFC 8252 §7.1, e.g. "com.example.app:/oauth"
* @param mixed $value
*/
private function isPrivateUseSchemeURI($value): bool
{
if (!\is_string($value)) {
return false;
}

$uri = \Uri\Rfc3986\Uri::parse($value);
if (!$uri instanceof \Uri\Rfc3986\Uri) {
return false;
}

$scheme = $uri->getScheme();

return $scheme !== null
&& str_contains($scheme, '.')
&& $uri->getHost() === null;
}

/**
* Is array
*
Expand Down
6 changes: 4 additions & 2 deletions packages/validators/src/Validator/WhiteList.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Utopia\Validator;

use Utopia\Validator;
Expand All @@ -23,7 +25,7 @@ public function __construct(protected array $list, protected bool $strict = fals
{
if (!$this->strict) {
foreach ($this->list as $key => &$value) {
$this->list[$key] = strtolower($value);
$this->list[$key] = strtolower((string) $value);
}
}
}
Expand Down Expand Up @@ -77,7 +79,7 @@ public function isValid(mixed $value): bool
return false;
}

$value = ($this->strict) ? $value : strtolower($value);
$value = ($this->strict) ? $value : strtolower((string) $value);
return \in_array($value, $this->list, $this->strict);
}
}
Loading