Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
613d243760 Bump the minor-updates group across 1 directory with 17 updates
Bumps the minor-updates group with 15 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [dedoc/scramble](https://github.com/dedoc/scramble) | `0.12.36` | `0.13.36` |
| [flowframe/laravel-trend](https://github.com/Flowframe/laravel-trend) | `0.4.0` | `0.5.0` |
| [gotenberg/gotenberg-php](https://github.com/gotenberg/gotenberg-php) | `2.21.0` | `2.24.0` |
| [laravel/fortify](https://github.com/laravel/fortify) | `1.37.2` | `1.37.3` |
| [laravel/octane](https://github.com/laravel/octane) | `2.17.4` | `2.18.0` |
| [league/flysystem-aws-s3-v3](https://github.com/thephpleague/flysystem-aws-s3-v3) | `3.34.0` | `3.35.2` |
| [owen-it/laravel-auditing](https://github.com/owen-it/laravel-auditing) | `14.0.3` | `14.0.6` |
| [spatie/temporary-directory](https://github.com/spatie/temporary-directory) | `2.3.1` | `2.4.0` |
| [tightenco/ziggy](https://github.com/tighten/ziggy) | `2.6.2` | `2.6.3` |
| [fumeapp/modeltyper](https://github.com/fumeapp/modeltyper) | `3.10.0` | `3.12.0` |
| [larastan/larastan](https://github.com/larastan/larastan) | `3.9.6` | `3.10.0` |
| [laravel/pint](https://github.com/laravel/pint) | `1.29.1` | `1.30.0` |
| [laravel/sail](https://github.com/laravel/sail) | `1.60.0` | `1.64.0` |
| [laravel/telescope](https://github.com/laravel/telescope) | `5.20.0` | `5.21.0` |
| [nunomaduro/collision](https://github.com/nunomaduro/collision) | `8.9.4` | `8.9.5` |



Updates `dedoc/scramble` from 0.12.36 to 0.13.36
- [Release notes](https://github.com/dedoc/scramble/releases)
- [Commits](https://github.com/dedoc/scramble/compare/v0.12.36...v0.13.36)

Updates `flowframe/laravel-trend` from 0.4.0 to 0.5.0
- [Release notes](https://github.com/Flowframe/laravel-trend/releases)
- [Changelog](https://github.com/Flowframe/laravel-trend/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Flowframe/laravel-trend/compare/v0.4.0...v0.5.0)

Updates `gotenberg/gotenberg-php` from 2.21.0 to 2.24.0
- [Release notes](https://github.com/gotenberg/gotenberg-php/releases)
- [Commits](https://github.com/gotenberg/gotenberg-php/compare/v2.21.0...v2.24.0)

Updates `guzzlehttp/guzzle` from 7.10.3 to 7.15.2
- [Release notes](https://github.com/guzzle/guzzle/releases)
- [Changelog](https://github.com/guzzle/guzzle/blob/7.15.2/CHANGELOG.md)
- [Commits](https://github.com/guzzle/guzzle/compare/7.10.3...7.15.2)

Updates `laravel/fortify` from 1.37.2 to 1.37.3
- [Release notes](https://github.com/laravel/fortify/releases)
- [Changelog](https://github.com/laravel/fortify/blob/1.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/fortify/compare/v1.37.2...v1.37.3)

Updates `laravel/framework` from 12.60.2 to 12.64.0
- [Release notes](https://github.com/laravel/framework/releases)
- [Changelog](https://github.com/laravel/framework/blob/13.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/framework/compare/v12.60.2...v12.64.0)

Updates `laravel/octane` from 2.17.4 to 2.18.0
- [Release notes](https://github.com/laravel/octane/releases)
- [Changelog](https://github.com/laravel/octane/blob/2.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/octane/compare/v2.17.4...v2.18.0)

Updates `league/flysystem-aws-s3-v3` from 3.34.0 to 3.35.2
- [Commits](https://github.com/thephpleague/flysystem-aws-s3-v3/compare/3.34.0...3.35.2)

Updates `owen-it/laravel-auditing` from 14.0.3 to 14.0.6
- [Release notes](https://github.com/owen-it/laravel-auditing/releases)
- [Changelog](https://github.com/owen-it/laravel-auditing/blob/master/CHANGELOG.md)
- [Commits](https://github.com/owen-it/laravel-auditing/compare/v14.0.3...v14.0.6)

Updates `spatie/temporary-directory` from 2.3.1 to 2.4.0
- [Release notes](https://github.com/spatie/temporary-directory/releases)
- [Changelog](https://github.com/spatie/temporary-directory/blob/main/CHANGELOG.md)
- [Commits](https://github.com/spatie/temporary-directory/compare/2.3.1...2.4.0)

Updates `tightenco/ziggy` from 2.6.2 to 2.6.3
- [Release notes](https://github.com/tighten/ziggy/releases)
- [Changelog](https://github.com/tighten/ziggy/blob/2.x/CHANGELOG.md)
- [Commits](https://github.com/tighten/ziggy/compare/v2.6.2...v2.6.3)

Updates `fumeapp/modeltyper` from 3.10.0 to 3.12.0
- [Release notes](https://github.com/fumeapp/modeltyper/releases)
- [Commits](https://github.com/fumeapp/modeltyper/compare/v3.10.0...v3.12.0)

Updates `larastan/larastan` from 3.9.6 to 3.10.0
- [Release notes](https://github.com/larastan/larastan/releases)
- [Changelog](https://github.com/larastan/larastan/blob/3.x/RELEASE.md)
- [Commits](https://github.com/larastan/larastan/compare/v3.9.6...v3.10.0)

Updates `laravel/pint` from 1.29.1 to 1.30.0
- [Release notes](https://github.com/laravel/pint/releases)
- [Changelog](https://github.com/laravel/pint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/laravel/pint/compare/v1.29.1...v1.30.0)

Updates `laravel/sail` from 1.60.0 to 1.64.0
- [Release notes](https://github.com/laravel/sail/releases)
- [Changelog](https://github.com/laravel/sail/blob/1.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/sail/compare/v1.60.0...v1.64.0)

Updates `laravel/telescope` from 5.20.0 to 5.21.0
- [Release notes](https://github.com/laravel/telescope/releases)
- [Changelog](https://github.com/laravel/telescope/blob/5.x/CHANGELOG.md)
- [Commits](https://github.com/laravel/telescope/compare/v5.20.0...v5.21.0)

Updates `nunomaduro/collision` from 8.9.4 to 8.9.5
- [Commits](https://github.com/nunomaduro/collision/compare/v8.9.4...v8.9.5)

---
updated-dependencies:
- dependency-name: dedoc/scramble
  dependency-version: 0.13.35
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: flowframe/laravel-trend
  dependency-version: 0.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: fumeapp/modeltyper
  dependency-version: 3.11.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: gotenberg/gotenberg-php
  dependency-version: 2.24.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: guzzlehttp/guzzle
  dependency-version: 7.15.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: larastan/larastan
  dependency-version: 3.10.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: laravel/fortify
  dependency-version: 1.37.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-updates
- dependency-name: laravel/framework
  dependency-version: 12.64.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: laravel/octane
  dependency-version: 2.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: laravel/pint
  dependency-version: 1.29.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor-updates
- dependency-name: laravel/sail
  dependency-version: 1.64.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: laravel/telescope
  dependency-version: 5.21.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: league/flysystem-aws-s3-v3
  dependency-version: 3.35.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: nunomaduro/collision
  dependency-version: 8.9.5
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: minor-updates
- dependency-name: owen-it/laravel-auditing
  dependency-version: 14.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-updates
- dependency-name: spatie/temporary-directory
  dependency-version: 2.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: minor-updates
- dependency-name: tightenco/ziggy
  dependency-version: 2.6.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: minor-updates
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-08-03 08:12:38 +00:00
8 changed files with 133 additions and 558 deletions

View File

@@ -6,10 +6,7 @@ namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Throwable;
class Handler extends ExceptionHandler
@@ -33,29 +30,6 @@ class Handler extends ExceptionHandler
$this->reportable(function (Throwable $e): void {
//
});
// A request on an untrusted host (see App\Http\Middleware\TrustHosts)
// otherwise renders as a bare "Bad request." 400. Show a message that
// says how to fix it instead. The framework has already converted the
// SuspiciousOperationException into a BadRequestHttpException by the time
// renderables run, so we match that and inspect the original.
$this->renderable(function (BadRequestHttpException $e, Request $request): ?Response {
$previous = $e->getPrevious();
if (! $previous instanceof SuspiciousOperationException
|| ! str_starts_with($previous->getMessage(), 'Untrusted Host')) {
return null; // any other bad request keeps the default response
}
$message = 'This hostname is not configured for this instance. '
.'Set APP_URL, or add the host to TRUSTED_HOSTS.';
if ($request->expectsJson()) {
return response()->json(['message' => $message], 400);
}
return response()->view('errors.untrusted-host', ['message' => $message], 400);
});
}
public function render($request, Throwable $e): Response|RedirectResponse

View File

@@ -15,7 +15,6 @@ use App\Http\Middleware\PreventRequestsDuringMaintenance;
use App\Http\Middleware\RedirectIfAuthenticated;
use App\Http\Middleware\ShareInertiaData;
use App\Http\Middleware\TrimStrings;
use App\Http\Middleware\TrustHosts;
use App\Http\Middleware\TrustProxies;
use App\Http\Middleware\ValidateSignature;
use App\Http\Middleware\VerifyCsrfToken;
@@ -48,7 +47,6 @@ class Kernel extends HttpKernel
*/
protected $middleware = [
ForceHttps::class,
TrustHosts::class,
TrustProxies::class,
HandleCors::class,
PreventRequestsDuringMaintenance::class,

View File

@@ -1,56 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as BaseTrustHosts;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
/**
* Rejects requests whose Host is not trusted, preventing Host-header poisoning of
* generated URLs (password reset, SSO callback, invitations). Trusted = the
* APP_URL host and its subdomains, plus TRUSTED_HOSTS (for multi-host access such
* as a Tailscale name). Health-check endpoints are exempt (probed by IP).
*/
class TrustHosts extends BaseTrustHosts
{
/**
* @return array<int, string|null>
*/
public function hosts(): array
{
/** @var array<int, string> $configured */
$configured = config('app.trusted_hosts', []);
$extra = array_map(function (string $host): string {
$host = trim($host);
// "*.example.com" matches any subdomain, not the apex.
if (str_starts_with($host, '*.')) {
return '^.+\.'.preg_quote(substr($host, 2), '#').'$';
}
return '^'.preg_quote($host, '#').'$';
}, $configured);
return array_merge([$this->allSubdomainsOfApplicationUrl()], $extra);
}
/**
* @param \Closure(Request): Response $next
*/
public function handle(Request $request, $next)
{
// Exempt health checks (probed by IP). Also reset the trusted hosts,
// since Octane leaks the static state across requests.
if ($request->is('health-check/*')) {
Request::setTrustedHosts([]);
return $next($request);
}
return parent::handle($request, $next);
}
}

View File

@@ -9,7 +9,7 @@
"ext-zip": "*",
"brick/money": "^0.10.0",
"datomatic/laravel-enum-helper": "^2.0.0",
"dedoc/scramble": "^0.13.39",
"dedoc/scramble": "^0.13.36",
"filament/filament": "^3.2",
"flowframe/laravel-trend": "^0.5.0",
"gotenberg/gotenberg-php": "^2.8",

264
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "02270cef79c6a49140db75d66627b9cd",
"content-hash": "34b85d5c7b2fe2916c70370611327926",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@@ -127,16 +127,16 @@
},
{
"name": "aws/aws-sdk-php",
"version": "3.391.1",
"version": "3.390.2",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "90920ce5518aa864e04da0224bb0ba0ee9e31d7f"
"reference": "a09e95bf062e15b7343f9c1362fd45c7a0dec39b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/90920ce5518aa864e04da0224bb0ba0ee9e31d7f",
"reference": "90920ce5518aa864e04da0224bb0ba0ee9e31d7f",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a09e95bf062e15b7343f9c1362fd45c7a0dec39b",
"reference": "a09e95bf062e15b7343f9c1362fd45c7a0dec39b",
"shasum": ""
},
"require": {
@@ -218,9 +218,9 @@
"support": {
"forum": "https://github.com/aws/aws-sdk-php/discussions",
"issues": "https://github.com/aws/aws-sdk-php/issues",
"source": "https://github.com/aws/aws-sdk-php/tree/3.391.1"
"source": "https://github.com/aws/aws-sdk-php/tree/3.390.2"
},
"time": "2026-08-07T20:18:18+00:00"
"time": "2026-07-31T18:06:07+00:00"
},
{
"name": "bacon/bacon-qr-code",
@@ -1501,16 +1501,16 @@
},
{
"name": "dedoc/scramble",
"version": "v0.13.39",
"version": "v0.13.36",
"source": {
"type": "git",
"url": "https://github.com/dedoc/scramble.git",
"reference": "7fcc4758d62f22d326637a578168bd5eb67a8625"
"reference": "9acdb5e54f808c5bd506369f8e84322d322cc6c9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dedoc/scramble/zipball/7fcc4758d62f22d326637a578168bd5eb67a8625",
"reference": "7fcc4758d62f22d326637a578168bd5eb67a8625",
"url": "https://api.github.com/repos/dedoc/scramble/zipball/9acdb5e54f808c5bd506369f8e84322d322cc6c9",
"reference": "9acdb5e54f808c5bd506369f8e84322d322cc6c9",
"shasum": ""
},
"require": {
@@ -1570,7 +1570,7 @@
],
"support": {
"issues": "https://github.com/dedoc/scramble/issues",
"source": "https://github.com/dedoc/scramble/tree/v0.13.39"
"source": "https://github.com/dedoc/scramble/tree/v0.13.36"
},
"funding": [
{
@@ -1578,7 +1578,7 @@
"type": "github"
}
],
"time": "2026-08-06T06:42:48+00:00"
"time": "2026-07-27T09:42:22+00:00"
},
{
"name": "defuse/php-encryption",
@@ -3021,21 +3021,21 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "7.15.3",
"version": "7.15.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc"
"reference": "744101956d78b7c1384d0cbf379db13e859167bf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc",
"reference": "ae311b8f045ea93ce7b1c9cdb7cec06c53f944bc",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/744101956d78b7c1384d0cbf379db13e859167bf",
"reference": "744101956d78b7c1384d0cbf379db13e859167bf",
"shasum": ""
},
"require": {
"ext-json": "*",
"guzzlehttp/promises": "^2.5.2",
"guzzlehttp/promises": "^2.5.1",
"guzzlehttp/psr7": "^2.13",
"php": "^7.2.5 || ^8.0",
"psr/http-client": "^1.0",
@@ -3129,7 +3129,7 @@
],
"support": {
"issues": "https://github.com/guzzle/guzzle/issues",
"source": "https://github.com/guzzle/guzzle/tree/7.15.3"
"source": "https://github.com/guzzle/guzzle/tree/7.15.2"
},
"funding": [
{
@@ -3145,20 +3145,20 @@
"type": "tidelift"
}
],
"time": "2026-08-05T19:48:21+00:00"
"time": "2026-07-26T23:23:20+00:00"
},
{
"name": "guzzlehttp/promises",
"version": "2.5.2",
"version": "2.5.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "2823687acff28b2dbe67b2508a6b300e2c3fa4ce"
"reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/2823687acff28b2dbe67b2508a6b300e2c3fa4ce",
"reference": "2823687acff28b2dbe67b2508a6b300e2c3fa4ce",
"url": "https://api.github.com/repos/guzzle/promises/zipball/9ad1e4fc607446a055b95870c7f668e93b5cff29",
"reference": "9ad1e4fc607446a055b95870c7f668e93b5cff29",
"shasum": ""
},
"require": {
@@ -3213,7 +3213,7 @@
],
"support": {
"issues": "https://github.com/guzzle/promises/issues",
"source": "https://github.com/guzzle/promises/tree/2.5.2"
"source": "https://github.com/guzzle/promises/tree/2.5.1"
},
"funding": [
{
@@ -3229,7 +3229,7 @@
"type": "tidelift"
}
],
"time": "2026-08-05T19:30:54+00:00"
"time": "2026-07-08T15:48:39+00:00"
},
{
"name": "guzzlehttp/psr7",
@@ -4198,16 +4198,16 @@
},
{
"name": "laravel/framework",
"version": "v12.65.0",
"version": "v12.64.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "99a8fb3153f962a323377d6742be08da86bcccb8"
"reference": "727a8ea2949c23ca8b5316b86a00984b6017b7a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/99a8fb3153f962a323377d6742be08da86bcccb8",
"reference": "99a8fb3153f962a323377d6742be08da86bcccb8",
"url": "https://api.github.com/repos/laravel/framework/zipball/727a8ea2949c23ca8b5316b86a00984b6017b7a0",
"reference": "727a8ea2949c23ca8b5316b86a00984b6017b7a0",
"shasum": ""
},
"require": {
@@ -4416,7 +4416,7 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
"time": "2026-08-05T15:33:16+00:00"
"time": "2026-07-14T14:25:37+00:00"
},
{
"name": "laravel/octane",
@@ -4652,16 +4652,16 @@
},
{
"name": "laravel/prompts",
"version": "v0.3.22",
"version": "v0.3.21",
"source": {
"type": "git",
"url": "https://github.com/laravel/prompts.git",
"reference": "02b89b39e8972a998db4d5d4ad4719239dd4aee4"
"reference": "7753c65c281c2550c7c183f14e18062073b7d821"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/prompts/zipball/02b89b39e8972a998db4d5d4ad4719239dd4aee4",
"reference": "02b89b39e8972a998db4d5d4ad4719239dd4aee4",
"url": "https://api.github.com/repos/laravel/prompts/zipball/7753c65c281c2550c7c183f14e18062073b7d821",
"reference": "7753c65c281c2550c7c183f14e18062073b7d821",
"shasum": ""
},
"require": {
@@ -4705,9 +4705,9 @@
"description": "Add beautiful and user-friendly forms to your command-line applications.",
"support": {
"issues": "https://github.com/laravel/prompts/issues",
"source": "https://github.com/laravel/prompts/tree/v0.3.22"
"source": "https://github.com/laravel/prompts/tree/v0.3.21"
},
"time": "2026-08-04T14:50:50+00:00"
"time": "2026-06-26T00:11:25+00:00"
},
{
"name": "laravel/serializable-closure",
@@ -4975,16 +4975,16 @@
},
{
"name": "league/commonmark",
"version": "2.9.1",
"version": "2.8.3",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/commonmark.git",
"reference": "73cb188c785abfa7a7bec73487148202968274c6"
"reference": "1902f60f984235023acbe03db6ad614a37b3c3e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/73cb188c785abfa7a7bec73487148202968274c6",
"reference": "73cb188c785abfa7a7bec73487148202968274c6",
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/1902f60f984235023acbe03db6ad614a37b3c3e7",
"reference": "1902f60f984235023acbe03db6ad614a37b3c3e7",
"shasum": ""
},
"require": {
@@ -5021,7 +5021,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.10-dev"
"dev-main": "2.9-dev"
}
},
"autoload": {
@@ -5078,7 +5078,7 @@
"type": "tidelift"
}
],
"time": "2026-08-09T14:10:38+00:00"
"time": "2026-07-12T15:29:16+00:00"
},
{
"name": "league/config",
@@ -6677,16 +6677,16 @@
},
{
"name": "nesbot/carbon",
"version": "3.13.2",
"version": "3.13.1",
"source": {
"type": "git",
"url": "https://github.com/CarbonPHP/carbon.git",
"reference": "a1c54919f5fff9800cd03c32bd01defd5a4061cb"
"reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/a1c54919f5fff9800cd03c32bd01defd5a4061cb",
"reference": "a1c54919f5fff9800cd03c32bd01defd5a4061cb",
"url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/2937ad3d1d2c506fd2bc97d571438a95641f44e2",
"reference": "2937ad3d1d2c506fd2bc97d571438a95641f44e2",
"shasum": ""
},
"require": {
@@ -6778,7 +6778,7 @@
"type": "tidelift"
}
],
"time": "2026-08-08T11:40:35+00:00"
"time": "2026-07-09T18:23:49+00:00"
},
{
"name": "nette/schema",
@@ -9799,20 +9799,20 @@
},
{
"name": "spomky-labs/pki-framework",
"version": "1.6.0",
"version": "1.5.0",
"source": {
"type": "git",
"url": "https://github.com/Spomky-Labs/pki-framework.git",
"reference": "80778a25426288acd2e3a7cde2def41a3d59cddf"
"reference": "e0d61661962560c1cedfef02b51b431e720aae78"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Spomky-Labs/pki-framework/zipball/80778a25426288acd2e3a7cde2def41a3d59cddf",
"reference": "80778a25426288acd2e3a7cde2def41a3d59cddf",
"url": "https://api.github.com/repos/Spomky-Labs/pki-framework/zipball/e0d61661962560c1cedfef02b51b431e720aae78",
"reference": "e0d61661962560c1cedfef02b51b431e720aae78",
"shasum": ""
},
"require": {
"brick/math": "^0.10|^0.11|^0.12|^0.13|^0.14|^0.15|^0.16|^0.17|^0.18|^0.19",
"brick/math": "^0.10|^0.11|^0.12|^0.13|^0.14|^0.15|^0.16|^0.17|^0.18",
"ext-mbstring": "*",
"php": ">=8.1"
},
@@ -9892,7 +9892,7 @@
],
"support": {
"issues": "https://github.com/Spomky-Labs/pki-framework/issues",
"source": "https://github.com/Spomky-Labs/pki-framework/tree/1.6.0"
"source": "https://github.com/Spomky-Labs/pki-framework/tree/1.5.0"
},
"funding": [
{
@@ -9904,7 +9904,7 @@
"type": "patreon"
}
],
"time": "2026-08-06T16:21:11+00:00"
"time": "2026-07-16T10:28:45+00:00"
},
{
"name": "staudenmeir/eloquent-has-many-deep-contracts",
@@ -10133,16 +10133,16 @@
},
{
"name": "symfony/console",
"version": "v7.4.16",
"version": "v7.4.15",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "f4c69c9aed03abf933b294257d618bdd9b30a06d"
"reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/f4c69c9aed03abf933b294257d618bdd9b30a06d",
"reference": "f4c69c9aed03abf933b294257d618bdd9b30a06d",
"url": "https://api.github.com/repos/symfony/console/zipball/088ec6fe0ef6819cbc301174093b6bfa4ad26930",
"reference": "088ec6fe0ef6819cbc301174093b6bfa4ad26930",
"shasum": ""
},
"require": {
@@ -10207,7 +10207,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.4.16"
"source": "https://github.com/symfony/console/tree/v7.4.15"
},
"funding": [
{
@@ -10227,7 +10227,7 @@
"type": "tidelift"
}
],
"time": "2026-07-31T12:37:14+00:00"
"time": "2026-07-27T13:51:00+00:00"
},
{
"name": "symfony/css-selector",
@@ -10830,16 +10830,16 @@
},
{
"name": "symfony/http-foundation",
"version": "v7.4.16",
"version": "v7.4.15",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-foundation.git",
"reference": "b676451bb638e99a7d34d8a2be90406822e301eb"
"reference": "1f898ee8188adda9417fb52cf8425a8342c254e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/b676451bb638e99a7d34d8a2be90406822e301eb",
"reference": "b676451bb638e99a7d34d8a2be90406822e301eb",
"url": "https://api.github.com/repos/symfony/http-foundation/zipball/1f898ee8188adda9417fb52cf8425a8342c254e7",
"reference": "1f898ee8188adda9417fb52cf8425a8342c254e7",
"shasum": ""
},
"require": {
@@ -10888,7 +10888,7 @@
"description": "Defines an object-oriented layer for the HTTP specification",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-foundation/tree/v7.4.16"
"source": "https://github.com/symfony/http-foundation/tree/v7.4.15"
},
"funding": [
{
@@ -10908,20 +10908,20 @@
"type": "tidelift"
}
],
"time": "2026-08-07T11:50:27+00:00"
"time": "2026-07-29T07:12:33+00:00"
},
{
"name": "symfony/http-kernel",
"version": "v7.4.16",
"version": "v7.4.15",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-kernel.git",
"reference": "f5e728670fa2218ae8be8ea91f2b44b7d6e5304c"
"reference": "403275d94f94d5626c3288c599b3b48093ba24f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/f5e728670fa2218ae8be8ea91f2b44b7d6e5304c",
"reference": "f5e728670fa2218ae8be8ea91f2b44b7d6e5304c",
"url": "https://api.github.com/repos/symfony/http-kernel/zipball/403275d94f94d5626c3288c599b3b48093ba24f7",
"reference": "403275d94f94d5626c3288c599b3b48093ba24f7",
"shasum": ""
},
"require": {
@@ -11007,7 +11007,7 @@
"description": "Provides a structured process for converting a Request into a Response",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/http-kernel/tree/v7.4.16"
"source": "https://github.com/symfony/http-kernel/tree/v7.4.15"
},
"funding": [
{
@@ -11027,7 +11027,7 @@
"type": "tidelift"
}
],
"time": "2026-08-07T18:00:13+00:00"
"time": "2026-07-29T11:40:42+00:00"
},
{
"name": "symfony/mailer",
@@ -11115,16 +11115,16 @@
},
{
"name": "symfony/mime",
"version": "v7.4.16",
"version": "v7.4.15",
"source": {
"type": "git",
"url": "https://github.com/symfony/mime.git",
"reference": "20094b76a7106dbe978d31cd3bd7aa1ed248d0e5"
"reference": "0c1daf58bc931628df0bea26840d1fc8b9a3d34b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/mime/zipball/20094b76a7106dbe978d31cd3bd7aa1ed248d0e5",
"reference": "20094b76a7106dbe978d31cd3bd7aa1ed248d0e5",
"url": "https://api.github.com/repos/symfony/mime/zipball/0c1daf58bc931628df0bea26840d1fc8b9a3d34b",
"reference": "0c1daf58bc931628df0bea26840d1fc8b9a3d34b",
"shasum": ""
},
"require": {
@@ -11180,7 +11180,7 @@
"mime-type"
],
"support": {
"source": "https://github.com/symfony/mime/tree/v7.4.16"
"source": "https://github.com/symfony/mime/tree/v7.4.15"
},
"funding": [
{
@@ -11200,7 +11200,7 @@
"type": "tidelift"
}
],
"time": "2026-08-07T14:56:57+00:00"
"time": "2026-07-29T07:59:49+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -12258,16 +12258,16 @@
},
{
"name": "symfony/property-access",
"version": "v7.4.16",
"version": "v7.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/property-access.git",
"reference": "c3dce76a6697c6428f7d80d5cd1cf0aa5d9679f3"
"reference": "b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/property-access/zipball/c3dce76a6697c6428f7d80d5cd1cf0aa5d9679f3",
"reference": "c3dce76a6697c6428f7d80d5cd1cf0aa5d9679f3",
"url": "https://api.github.com/repos/symfony/property-access/zipball/b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc",
"reference": "b7dad9dae8b8a47ef7ecc76c8569e7d8c7d90cfc",
"shasum": ""
},
"require": {
@@ -12315,7 +12315,7 @@
"reflection"
],
"support": {
"source": "https://github.com/symfony/property-access/tree/v7.4.16"
"source": "https://github.com/symfony/property-access/tree/v7.4.8"
},
"funding": [
{
@@ -12335,20 +12335,20 @@
"type": "tidelift"
}
],
"time": "2026-07-30T12:37:26+00:00"
"time": "2026-03-24T13:12:05+00:00"
},
{
"name": "symfony/property-info",
"version": "v7.4.16",
"version": "v7.4.15",
"source": {
"type": "git",
"url": "https://github.com/symfony/property-info.git",
"reference": "c79afdb2e720db8ee4f7050a07b3116b92cb896b"
"reference": "fce3f4d9cfeb4ddc674c357b5a4c3a23ccf408ad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/property-info/zipball/c79afdb2e720db8ee4f7050a07b3116b92cb896b",
"reference": "c79afdb2e720db8ee4f7050a07b3116b92cb896b",
"url": "https://api.github.com/repos/symfony/property-info/zipball/fce3f4d9cfeb4ddc674c357b5a4c3a23ccf408ad",
"reference": "fce3f4d9cfeb4ddc674c357b5a4c3a23ccf408ad",
"shasum": ""
},
"require": {
@@ -12405,7 +12405,7 @@
"validator"
],
"support": {
"source": "https://github.com/symfony/property-info/tree/v7.4.16"
"source": "https://github.com/symfony/property-info/tree/v7.4.15"
},
"funding": [
{
@@ -12425,7 +12425,7 @@
"type": "tidelift"
}
],
"time": "2026-08-07T14:56:57+00:00"
"time": "2026-07-28T07:09:44+00:00"
},
{
"name": "symfony/psr-http-message-bridge",
@@ -12602,16 +12602,16 @@
},
{
"name": "symfony/serializer",
"version": "v7.4.16",
"version": "v7.4.15",
"source": {
"type": "git",
"url": "https://github.com/symfony/serializer.git",
"reference": "5ef7afacc15dfa503e9dcab7c3eda0b4460a15d9"
"reference": "917f1575bec2853f45e012d8718f518365a9d258"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/serializer/zipball/5ef7afacc15dfa503e9dcab7c3eda0b4460a15d9",
"reference": "5ef7afacc15dfa503e9dcab7c3eda0b4460a15d9",
"url": "https://api.github.com/repos/symfony/serializer/zipball/917f1575bec2853f45e012d8718f518365a9d258",
"reference": "917f1575bec2853f45e012d8718f518365a9d258",
"shasum": ""
},
"require": {
@@ -12682,7 +12682,7 @@
"description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/serializer/tree/v7.4.16"
"source": "https://github.com/symfony/serializer/tree/v7.4.15"
},
"funding": [
{
@@ -12702,7 +12702,7 @@
"type": "tidelift"
}
],
"time": "2026-08-06T10:16:07+00:00"
"time": "2026-07-29T07:59:49+00:00"
},
{
"name": "symfony/service-contracts",
@@ -12884,16 +12884,16 @@
},
{
"name": "symfony/translation",
"version": "v7.4.16",
"version": "v7.4.14",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "501e0ff4553c744209ca1a68790d8a4541563710"
"reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/501e0ff4553c744209ca1a68790d8a4541563710",
"reference": "501e0ff4553c744209ca1a68790d8a4541563710",
"url": "https://api.github.com/repos/symfony/translation/zipball/a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
"reference": "a1af4dacb24eb7ef4f1ca71b94da8ddbce572281",
"shasum": ""
},
"require": {
@@ -12960,7 +12960,7 @@
"description": "Provides tools to internationalize your application",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/translation/tree/v7.4.16"
"source": "https://github.com/symfony/translation/tree/v7.4.14"
},
"funding": [
{
@@ -12980,7 +12980,7 @@
"type": "tidelift"
}
],
"time": "2026-07-30T12:37:26+00:00"
"time": "2026-06-06T09:33:19+00:00"
},
{
"name": "symfony/translation-contracts",
@@ -14385,16 +14385,16 @@
},
{
"name": "fumeapp/modeltyper",
"version": "v3.13.0",
"version": "v3.12.0",
"source": {
"type": "git",
"url": "https://github.com/fumeapp/modeltyper.git",
"reference": "f06dc8df1607cbf716d1c9d5ccef998545278012"
"reference": "8a3e61a73a1af627bf42518fb315172ebe133fa7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fumeapp/modeltyper/zipball/f06dc8df1607cbf716d1c9d5ccef998545278012",
"reference": "f06dc8df1607cbf716d1c9d5ccef998545278012",
"url": "https://api.github.com/repos/fumeapp/modeltyper/zipball/8a3e61a73a1af627bf42518fb315172ebe133fa7",
"reference": "8a3e61a73a1af627bf42518fb315172ebe133fa7",
"shasum": ""
},
"require": {
@@ -14443,9 +14443,9 @@
"description": "Generate TypeScript interfaces from Laravel Models",
"support": {
"issues": "https://github.com/fumeapp/modeltyper/issues",
"source": "https://github.com/fumeapp/modeltyper/tree/v3.13.0"
"source": "https://github.com/fumeapp/modeltyper/tree/v3.12.0"
},
"time": "2026-08-01T04:02:40+00:00"
"time": "2026-07-28T14:20:28+00:00"
},
{
"name": "hamcrest/hamcrest-php",
@@ -14691,16 +14691,16 @@
},
{
"name": "laravel/pint",
"version": "v1.30.4",
"version": "v1.30.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "a96cb6eee2961905d2fce7207aefb80945bf6b28"
"reference": "72a0540d1aa10b6c146bda2a22f3ae003123c0ea"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/a96cb6eee2961905d2fce7207aefb80945bf6b28",
"reference": "a96cb6eee2961905d2fce7207aefb80945bf6b28",
"url": "https://api.github.com/repos/laravel/pint/zipball/72a0540d1aa10b6c146bda2a22f3ae003123c0ea",
"reference": "72a0540d1aa10b6c146bda2a22f3ae003123c0ea",
"shasum": ""
},
"require": {
@@ -14712,12 +14712,12 @@
},
"require-dev": {
"composer/semver": "^3.4.4",
"friendsofphp/php-cs-fixer": "^3.95.18",
"illuminate/view": "^12.65.0",
"friendsofphp/php-cs-fixer": "^3.95.17",
"illuminate/view": "^12.64.0",
"larastan/larastan": "^3.10.0",
"laravel-zero/framework": "^12.1.0",
"laravel/agent-detector": "^2.0.2",
"laravel/prompts": "^0.3.22",
"laravel/prompts": "^0.3.21",
"mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^2.4.0",
"pestphp/pest": "^3.8.7"
@@ -14757,20 +14757,20 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2026-08-05T16:47:22+00:00"
"time": "2026-07-28T20:48:56+00:00"
},
{
"name": "laravel/sail",
"version": "v1.65.0",
"version": "v1.64.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/sail.git",
"reference": "d4b92139858d2a189a6302ca133e494f58afe20b"
"reference": "08cacd3e72d6798df3fa8bd4b5d55d0f7f920625"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/sail/zipball/d4b92139858d2a189a6302ca133e494f58afe20b",
"reference": "d4b92139858d2a189a6302ca133e494f58afe20b",
"url": "https://api.github.com/repos/laravel/sail/zipball/08cacd3e72d6798df3fa8bd4b5d55d0f7f920625",
"reference": "08cacd3e72d6798df3fa8bd4b5d55d0f7f920625",
"shasum": ""
},
"require": {
@@ -14820,7 +14820,7 @@
"issues": "https://github.com/laravel/sail/issues",
"source": "https://github.com/laravel/sail"
},
"time": "2026-08-03T18:00:17+00:00"
"time": "2026-07-17T15:09:35+00:00"
},
{
"name": "laravel/sentinel",
@@ -14880,16 +14880,16 @@
},
{
"name": "laravel/telescope",
"version": "v5.22.0",
"version": "v5.21.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/telescope.git",
"reference": "571b08633d86086da3899474378050b16c4c6680"
"reference": "f00f45a59843329b97aa238f28da594f3b7d4aa7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/telescope/zipball/571b08633d86086da3899474378050b16c4c6680",
"reference": "571b08633d86086da3899474378050b16c4c6680",
"url": "https://api.github.com/repos/laravel/telescope/zipball/f00f45a59843329b97aa238f28da594f3b7d4aa7",
"reference": "f00f45a59843329b97aa238f28da594f3b7d4aa7",
"shasum": ""
},
"require": {
@@ -14943,9 +14943,9 @@
],
"support": {
"issues": "https://github.com/laravel/telescope/issues",
"source": "https://github.com/laravel/telescope/tree/v5.22.0"
"source": "https://github.com/laravel/telescope/tree/v5.21.0"
},
"time": "2026-07-29T14:10:20+00:00"
"time": "2026-07-20T19:44:45+00:00"
},
{
"name": "mockery/mockery",
@@ -15246,11 +15246,11 @@
},
{
"name": "phpstan/phpstan",
"version": "2.2.8",
"version": "2.2.7",
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/e285254e60f33c21902efef4a926ca0987c06804",
"reference": "e285254e60f33c21902efef4a926ca0987c06804",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/692db47b9dddb0487934e5236e77d48594aef921",
"reference": "692db47b9dddb0487934e5236e77d48594aef921",
"shasum": ""
},
"require": {
@@ -15306,7 +15306,7 @@
"type": "github"
}
],
"time": "2026-08-04T22:21:45+00:00"
"time": "2026-07-29T17:39:32+00:00"
},
{
"name": "phpunit/php-code-coverage",

View File

@@ -75,27 +75,6 @@ return [
'url' => env('APP_URL', 'http://localhost'),
/*
|--------------------------------------------------------------------------
| Trusted Hosts
|--------------------------------------------------------------------------
|
| Additional hostnames (besides the APP_URL host and its subdomains) that
| the application is allowed to respond on. This is needed for multi-host
| setups, e.g. reaching the instance over both a public domain and a
| Tailscale name. A request arriving on any host that is neither APP_URL
| (nor a subdomain of it) nor listed here is rejected, which prevents
| Host-header poisoning of password reset and other out-of-band links.
|
| See App\Http\Middleware\TrustHosts.
|
*/
'trusted_hosts' => array_values(array_filter(array_map(
'trim',
explode(',', (string) env('TRUSTED_HOSTS', ''))
))),
'asset_url' => env('ASSET_URL'),
'force_https' => (bool) env('APP_FORCE_HTTPS', false),

View File

@@ -1,49 +0,0 @@
{{-- Self-contained on purpose: this page is rendered for a request on an
untrusted host, so it must not call url()/route()/asset(), which would
re-trigger Host validation and throw again. --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Untrusted host</title>
<style>
html, body { height: 100%; margin: 0; }
body {
display: flex;
align-items: center;
justify-content: center;
background: #f5f5f5;
color: #1f2937;
font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
.card {
max-width: 32rem;
margin: 1.5rem;
padding: 2rem;
background: #fff;
border: 1px solid #e5e7eb;
border-radius: 0.75rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
h1 { margin: 0 0 0.75rem; font-size: 1.25rem; }
p { margin: 0; line-height: 1.6; color: #4b5563; }
code {
padding: 0.1rem 0.35rem;
background: #f3f4f6;
border-radius: 0.25rem;
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.9em;
}
</style>
</head>
<body>
<div class="card">
<h1>Untrusted host</h1>
<p>
This hostname is not configured for this instance. Set
<code>APP_URL</code>, or add the host to <code>TRUSTED_HOSTS</code>.
</p>
</div>
</body>
</html>

View File

@@ -1,271 +0,0 @@
<?php
declare(strict_types=1);
namespace Tests\Unit\Middleware;
use App\Http\Middleware\TrustHosts;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Http\Request;
use PHPUnit\Framework\Attributes\CoversClass;
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
use Symfony\Component\HttpFoundation\Response;
use Tests\TestCase;
#[CoversClass(TrustHosts::class)]
class TrustHostsTest extends TestCase
{
private const string CANONICAL = 'https://app.example.com';
protected function setUp(): void
{
parent::setUp();
config(['app.url' => self::CANONICAL]);
}
protected function tearDown(): void
{
Request::setTrustedHosts([]); // don't leak static state between tests
parent::tearDown();
}
/**
* The real middleware, with only the environment gate forced on (it
* self-exempts in the testing environment).
*/
private function middleware(): TrustHosts
{
return new class($this->app) extends TrustHosts
{
protected function shouldSpecifyTrustedHosts(): bool
{
return true;
}
};
}
private function accepts(Request $request): bool
{
$this->middleware()->handle($request, fn (Request $request): Response => new Response('passed'));
try {
dump($request->getHost());
return true;
} catch (\Throwable) {
return false;
}
}
public function test_canonical_host_is_accepted(): void
{
// Arrange
$request = Request::create(self::CANONICAL.'/login');
// Act
$accepted = $this->accepts($request);
// Assert
$this->assertTrue($accepted);
}
public function test_subdomain_of_canonical_host_is_accepted(): void
{
// Arrange
$request = Request::create('https://team.app.example.com/login');
// Act
$accepted = $this->accepts($request);
// Assert
$this->assertTrue($accepted);
}
public function test_declared_trusted_host_is_accepted(): void
{
// Arrange
config(['app.trusted_hosts' => ['box.tailnet.ts.net']]);
$request = Request::create('https://box.tailnet.ts.net/login');
// Act
$accepted = $this->accepts($request);
// Assert
$this->assertTrue($accepted);
}
public function test_wildcard_trusted_host_matches_subdomains_only(): void
{
// Arrange
config(['app.trusted_hosts' => ['*.example.net']]);
$subdomainRequest = Request::create('https://foo.example.net/login');
$nestedSubdomainRequest = Request::create('https://a.b.example.net/login');
$apexRequest = Request::create('https://example.net/login');
$suffixInjectionRequest = Request::create('https://example.net.evil.com/login');
// Act
$subdomainAccepted = $this->accepts($subdomainRequest);
$nestedSubdomainAccepted = $this->accepts($nestedSubdomainRequest);
$apexAccepted = $this->accepts($apexRequest);
$suffixInjectionAccepted = $this->accepts($suffixInjectionRequest);
// Assert
$this->assertTrue($subdomainAccepted);
$this->assertTrue($nestedSubdomainAccepted);
$this->assertFalse($apexAccepted);
$this->assertFalse($suffixInjectionAccepted);
}
public function test_multiple_trusted_hosts_are_all_accepted(): void
{
// Arrange
config(['app.trusted_hosts' => [
'box.tailnet.ts.net',
'solidtime.internal',
'*.preview.example.com',
]]);
$tailnetRequest = Request::create('https://box.tailnet.ts.net/login');
$internalRequest = Request::create('https://solidtime.internal/login');
$previewRequest = Request::create('https://pr-42.preview.example.com/login');
$unlistedRequest = Request::create('https://evil.example.com/login');
// Act
$tailnetAccepted = $this->accepts($tailnetRequest);
$internalAccepted = $this->accepts($internalRequest);
$previewAccepted = $this->accepts($previewRequest);
$unlistedAccepted = $this->accepts($unlistedRequest);
// Assert
$this->assertTrue($tailnetAccepted);
$this->assertTrue($internalAccepted);
$this->assertTrue($previewAccepted);
$this->assertFalse($unlistedAccepted);
}
public function test_poisoned_host_is_rejected(): void
{
// Arrange
$request = Request::create('https://evil.example.com/login');
// Act
$accepted = $this->accepts($request);
// Assert
$this->assertFalse($accepted);
}
public function test_poisoned_x_forwarded_host_is_rejected(): void
{
// Arrange
$request = Request::create(self::CANONICAL.'/login');
$request->headers->set('X-Forwarded-Host', 'evil.example.com');
$request->setTrustedProxies(
['0.0.0.0/0', '2000::/3'],
Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_PORT
);
// Act
$accepted = $this->accepts($request);
// Assert
$this->assertFalse($accepted);
}
public function test_forwarded_host_from_trusted_proxy_is_accepted(): void
{
// Arrange
$request = Request::create('https://evil.example.com/login');
$request->headers->set('X-Forwarded-Host', 'app.example.com');
$request->setTrustedProxies(
['0.0.0.0/0', '2000::/3'],
Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_PORT
);
// Act
$accepted = $this->accepts($request);
// Assert
$this->assertTrue($accepted);
}
public function test_forwarded_host_from_non_trusted_proxy_is_rejected_if_host_is_allowed(): void
{
// Arrange
$request = Request::create('https://evil.example.com/login');
$request->headers->set('X-Forwarded-Host', 'app.example.com');
$request->setTrustedProxies(
['1.2.3.4/32'], // Not a trusted proxy
Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_PORT
);
// Act
$accepted = $this->accepts($request);
// Assert
$this->assertFalse($accepted);
}
public function test_health_check_endpoint_bypasses_host_validation(): void
{
// Arrange
$internalIpRequest = Request::create('https://0.0.0.0/health-check/up');
$localhostRequest = Request::create('http://localhost/health-check/up');
// Act
$internalIpAccepted = $this->accepts($internalIpRequest);
$localhostAccepted = $this->accepts($localhostRequest);
// Assert
$this->assertTrue($internalIpAccepted);
$this->assertTrue($localhostAccepted);
}
public function test_health_check_endpoint_clears_state_before_other_middleware_reads_the_host(): void
{
// Arrange
Request::setTrustedHosts(['^app\.example\.com$']);
// Act
$response = $this->get(self::CANONICAL.'/health-check/up', ['Host' => '0.0.0.0']);
// Assert
$response->assertSuccessful()
->assertExactJson(['success' => true]);
}
public function test_untrusted_host_renders_a_helpful_error(): void
{
// Arrange
$handler = app(ExceptionHandler::class);
$exception = new SuspiciousOperationException('Untrusted Host "evil.example.com".');
$request = Request::create('https://evil.example.com/login');
// Act
$response = $handler->render($request, $exception);
// Assert
$this->assertSame(400, $response->getStatusCode());
$this->assertStringContainsString('TRUSTED_HOSTS', (string) $response->getContent());
}
public function test_untrusted_host_returns_json_for_api_clients(): void
{
// Arrange
$handler = app(ExceptionHandler::class);
$exception = new SuspiciousOperationException('Untrusted Host "evil.example.com".');
$request = Request::create('https://evil.example.com/api/v1/users');
$request->headers->set('Accept', 'application/json');
// Act
$response = $handler->render($request, $exception);
// Assert
$this->assertSame(400, $response->getStatusCode());
$this->assertJson((string) $response->getContent());
$this->assertStringContainsString('TRUSTED_HOSTS', (string) $response->getContent());
}
}