forked from saloonphp/saloon
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathURLHelper.php
More file actions
101 lines (80 loc) · 3.07 KB
/
URLHelper.php
File metadata and controls
101 lines (80 loc) · 3.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
<?php
declare(strict_types=1);
namespace Saloon\Helpers;
use InvalidArgumentException;
/**
* @internal
*/
class URLHelper
{
/**
* Check if a URL matches a given pattern
*/
public static function matches(string $pattern, string $value): bool
{
return StringHelpers::matchesPattern(StringHelpers::start($pattern, '*'), $value);
}
/**
* Join a base url and an endpoint together.
*
* When the connector has a base URL, the endpoint must be a relative path (e.g. "/users" or "users"),
* unless $allowBaseUrlOverride is true (e.g. OAuth provider URLs). Allowing override with user-controlled
* endpoints reintroduces SSRF and credential leakage.
* When the base URL is empty (e.g. Solo Request), the endpoint may be an absolute URL.
*
* @throws InvalidArgumentException When the endpoint is an absolute URL, the base URL is not empty, and override is not allowed
*/
public static function join(string $baseUrl, string $endpoint, bool $allowBaseUrlOverride = false): string
{
$baseTrimmed = trim($baseUrl, '/ ');
if ($baseTrimmed !== '' && static::isValidUrl($endpoint)) {
if ($allowBaseUrlOverride) {
return $endpoint;
}
throw new InvalidArgumentException(
'Absolute URLs are not allowed in the endpoint. The endpoint must be a relative path to prevent SSRF and credential leakage. To request a different host, use a connector with that host as the base URL, or enable allowBaseUrlOverride on the connector, request, or OAuth configuration when the endpoint is trusted.'
);
}
if ($baseTrimmed === '' && static::isValidUrl($endpoint)) {
return $endpoint;
}
if ($endpoint !== '/') {
$endpoint = ltrim($endpoint, '/ ');
}
$requiresTrailingSlash = ! empty($endpoint) && $endpoint !== '/';
$baseEndpoint = rtrim($baseUrl, '/ ');
$baseEndpoint = $requiresTrailingSlash ? $baseEndpoint . '/' : $baseEndpoint;
return $baseEndpoint . $endpoint;
}
/**
* Check if the URL is a valid URL
*/
public static function isValidUrl(string $url): bool
{
// The following str_replace is used to get around an issue raised by PHP 8.4
// @see https://github.com/php/php-src/issues/17842
$url = str_replace('_', '-', $url);
return ! empty(filter_var($url, FILTER_VALIDATE_URL));
}
/**
* Parse a query string into an array
*
* @return array<string, mixed>
*/
public static function parseQueryString(string $query): array
{
if ($query === '') {
return [];
}
$parameters = [];
foreach (explode('&', $query) as $parameter) {
$name = urldecode((string)strtok($parameter, '='));
$value = urldecode((string)strtok('='));
if (! $name || str_starts_with($parameter, '=')) {
continue;
}
$parameters[$name] = $value;
}
return $parameters;
}
}