Repository navigation
Sync package updates and check QUERY requests for request forgery #681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2fe3f00
0f10b28
b4763f4
8c91c61
981edba
40c847e
54936e7
9c1e1a8
959f9ab
b493df7
1a0886e
78b10b7
5e8a1a9
a103dbf
572cf41
bc18bc5
871f241
b5197b1
1a88332
648b66a
70957b5
d743afa
130cba2
47d51a7
48c1c8a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,15 +14,17 @@ | |
| use Hypervel\Http\Client\Response; | ||
| use Hypervel\Support\Facades\Http; | ||
| use InvalidArgumentException; | ||
| use Psr\Http\Message\ResponseInterface; | ||
| use RuntimeException; | ||
| use Swoole\Coroutine\CanceledException; | ||
| use Throwable; | ||
|
|
||
| class UntrustedUrl | ||
| { | ||
| public const int DEFAULT_MAX_SIZE = 32 * 1024 * 1024; | ||
|
|
||
| protected const int MAX_REDIRECTS = 5; | ||
|
|
||
| public const int MAX_KILOBYTES = 32 * 1024; | ||
|
|
||
| // REMOVED: hostname blocklists; the HTTP destination policy checks the resolved addresses. | ||
|
|
||
| protected static ?Closure $resolver = null; | ||
|
|
@@ -36,16 +38,18 @@ public static function fetch(string $url): Response | |
| { | ||
| $allowedHosts = config()->array('ai.remote_files.allowed_hosts', []); | ||
| $policy = new RemoteFileDestinationPolicy(static::$resolver); | ||
| $maxSize = config()->get('ai.remote_files.max_size', static::DEFAULT_MAX_SIZE); | ||
| $kilobytes = config()->get('ai.remote_files.max_size', static::MAX_KILOBYTES); | ||
|
|
||
| if ($maxSize !== null && (! is_int($maxSize) || $maxSize <= 0)) { | ||
| if ($kilobytes !== null && (! is_int($kilobytes) || $kilobytes <= 0)) { | ||
| throw new InvalidArgumentException('The remote file maximum size must be a positive integer or null.'); | ||
| } | ||
|
|
||
| $limit = $kilobytes === null ? null : $kilobytes * 1024; | ||
|
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Preserve the old byte-valued Prompt for AI agents
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Declining. The byte-valued setting was never released: the AI package isn't in any tagged release, and the setting only existed on the unreleased 0.4 branch, which makes breaking changes without compatibility layers. Upstream reads this key in kilobytes, so there's no published value to migrate. The Laravel porting guide covers the different default. |
||
|
|
||
| for ($hop = 0; $hop <= static::MAX_REDIRECTS; ++$hop) { | ||
| $uri = new Uri($url); | ||
| $options = []; | ||
| $tooLarge = null; | ||
| $tooLarge = false; | ||
| $trusted = in_array(rtrim(trim($uri->getHost(), '[]'), '.'), $allowedHosts, true); | ||
|
|
||
| if ($trusted) { | ||
|
|
@@ -54,20 +58,30 @@ public static function fetch(string $url): Response | |
| } | ||
| } | ||
|
|
||
| if ($maxSize !== null) { | ||
| $buffer = Utils::streamFor(); | ||
| if ($limit !== null) { | ||
| $body = Utils::streamFor(); | ||
|
|
||
| // The transport drops a compressed response's Content-Length before this runs, so only decoded lengths are checked. | ||
| $options['on_headers'] = static function (ResponseInterface $response) use ($limit, &$tooLarge): void { | ||
| if ((int) $response->getHeaderLine('Content-Length') > $limit) { | ||
| $tooLarge = true; | ||
|
|
||
| // Count decoded bytes at the sink; transfer sizes may describe compressed data. | ||
| $options['sink'] = FnStream::decorate($buffer, [ | ||
| 'write' => static function (string $chunk) use ($buffer, $maxSize, &$tooLarge): int { | ||
| if (strlen($chunk) > $maxSize - $buffer->tell()) { | ||
| $tooLarge = new InvalidArgumentException("The remote file exceeds the maximum size of [{$maxSize}] bytes."); | ||
| throw new RuntimeException('The remote file declares a body larger than the size limit.'); | ||
|
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
|
||
| } | ||
| }; | ||
|
|
||
| // Count decoded bytes at the sink, which also bounds compressed and chunked bodies that declare no decoded length. | ||
| $options['sink'] = FnStream::decorate($body, [ | ||
| 'write' => static function (string $chunk) use ($body, $limit, &$tooLarge): int { | ||
| // The sink appends sequentially, so tell() gives its size without a stat call for every chunk. | ||
| if (strlen($chunk) > $limit - $body->tell()) { | ||
| $tooLarge = true; | ||
|
|
||
| // A short write aborts the transfer while preserving transport cleanup. | ||
| return 0; | ||
| } | ||
|
|
||
| return $buffer->write($chunk); | ||
| return $body->write($chunk); | ||
| }, | ||
| ]); | ||
| } | ||
|
|
@@ -87,7 +101,11 @@ public static function fetch(string $url): Response | |
| try { | ||
| $response = $request->get((string) $uri); | ||
| } catch (Throwable $exception) { | ||
| throw $exception instanceof CanceledException ? $exception : $tooLarge ?? $exception; | ||
| if ($tooLarge && ! $exception instanceof CanceledException) { | ||
| throw new InvalidArgumentException("The remote file exceeds the maximum size of {$kilobytes} kilobytes."); | ||
| } | ||
|
|
||
| throw $exception; | ||
| } | ||
|
|
||
| if (! $response->redirect() || blank($response->header('Location'))) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: This replacement removes the public
UntrustedUrl::DEFAULT_MAX_SIZEconstant without a compatibility alias. Keep the old byte-valued constant as an alias, or document and deliberately version this breaking API removal.Prompt for AI agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Declining.
DEFAULT_MAX_SIZEwas never released either, so there's nothing to keep compatible.MAX_KILOBYTESreplaces it and matches the shipped config value.