// Configured mail encryption method.
// STARTTLS should still be attempted, but tls/ssl forces TLS usage.
$mailEncryption = env('MAIL_ENCRYPTION', null);
+$mailPort = intval(env('MAIL_PORT', 587));
return [
'transport' => 'smtp',
'scheme' => null,
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
- 'port' => env('MAIL_PORT', 587),
+ 'port' => $mailPort,
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'verify_peer' => env('MAIL_VERIFY_SSL', true),
'timeout' => null,
'local_domain' => null,
- 'tls_required' => ($mailEncryption === 'tls' || $mailEncryption === 'ssl'),
+ 'require_tls' => ($mailEncryption === 'tls' || $mailEncryption === 'ssl' || $mailPort === 465),
],
'sendmail' => [
"socialiteproviders/microsoft-azure": "^5.1",
"socialiteproviders/okta": "^4.2",
"socialiteproviders/twitch": "^5.3",
- "ssddanbrown/htmldiff": "^2.0.0",
- "ssddanbrown/symfony-mailer": "7.2.x-dev"
+ "ssddanbrown/htmldiff": "^2.0.0"
},
"require-dev": {
"fakerphp/faker": "^1.21",
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "d9b8455cf3ec02c21bea7ee94463331b",
+ "content-hash": "b7695cb9945ec550970c67da96934daf",
"packages": [
{
"name": "aws/aws-crt-php",
},
{
"name": "aws/aws-sdk-php",
- "version": "3.349.2",
+ "version": "3.349.3",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
- "reference": "63cc727845f077d17cb94791deb327249e1626ce"
+ "reference": "b2d4718786398f47626add9c29840fc416175ef2"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/63cc727845f077d17cb94791deb327249e1626ce",
- "reference": "63cc727845f077d17cb94791deb327249e1626ce",
+ "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b2d4718786398f47626add9c29840fc416175ef2",
+ "reference": "b2d4718786398f47626add9c29840fc416175ef2",
"shasum": ""
},
"require": {
"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.349.2"
+ "source": "https://github.com/aws/aws-sdk-php/tree/3.349.3"
},
- "time": "2025-07-03T18:08:27+00:00"
+ "time": "2025-07-09T18:10:17+00:00"
},
{
"name": "bacon/bacon-qr-code",
},
{
"name": "laravel/prompts",
- "version": "v0.3.5",
+ "version": "v0.3.6",
"source": {
"type": "git",
"url": "https://github.com/laravel/prompts.git",
- "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1"
+ "reference": "86a8b692e8661d0fb308cec64f3d176821323077"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laravel/prompts/zipball/57b8f7efe40333cdb925700891c7d7465325d3b1",
- "reference": "57b8f7efe40333cdb925700891c7d7465325d3b1",
+ "url": "https://api.github.com/repos/laravel/prompts/zipball/86a8b692e8661d0fb308cec64f3d176821323077",
+ "reference": "86a8b692e8661d0fb308cec64f3d176821323077",
"shasum": ""
},
"require": {
"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.5"
+ "source": "https://github.com/laravel/prompts/tree/v0.3.6"
},
- "time": "2025-02-11T13:34:40+00:00"
+ "time": "2025-07-07T14:17:42+00:00"
},
{
"name": "laravel/serializable-closure",
},
{
"name": "sabberworm/php-css-parser",
- "version": "v8.8.0",
+ "version": "v8.9.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
- "reference": "3de493bdddfd1f051249af725c7e0d2c38fed740"
+ "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/3de493bdddfd1f051249af725c7e0d2c38fed740",
- "reference": "3de493bdddfd1f051249af725c7e0d2c38fed740",
+ "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/d8e916507b88e389e26d4ab03c904a082aa66bb9",
+ "reference": "d8e916507b88e389e26d4ab03c904a082aa66bb9",
"shasum": ""
},
"require": {
"php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
- "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41"
+ "phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41",
+ "rawr/cross-data-providers": "^2.0.0"
},
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
- "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.8.0"
+ "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.9.0"
},
- "time": "2025-03-23T17:59:05+00:00"
+ "time": "2025-07-11T13:20:48+00:00"
},
{
"name": "socialiteproviders/discord",
],
"time": "2025-07-07T11:55:59+00:00"
},
- {
- "name": "ssddanbrown/symfony-mailer",
- "version": "7.2.x-dev",
- "source": {
- "type": "git",
- "url": "https://github.com/ssddanbrown/symfony-mailer.git",
- "reference": "e9de8dccd76a63fc23475016e6574da6f5f12a2d"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/ssddanbrown/symfony-mailer/zipball/e9de8dccd76a63fc23475016e6574da6f5f12a2d",
- "reference": "e9de8dccd76a63fc23475016e6574da6f5f12a2d",
- "shasum": ""
- },
- "require": {
- "egulias/email-validator": "^2.1.10|^3|^4",
- "php": ">=8.2",
- "psr/event-dispatcher": "^1",
- "psr/log": "^1|^2|^3",
- "symfony/event-dispatcher": "^6.4|^7.0",
- "symfony/mime": "^7.2",
- "symfony/service-contracts": "^2.5|^3"
- },
- "conflict": {
- "symfony/http-client-contracts": "<2.5",
- "symfony/http-kernel": "<6.4",
- "symfony/messenger": "<6.4",
- "symfony/mime": "<6.4",
- "symfony/twig-bridge": "<6.4"
- },
- "replace": {
- "symfony/mailer": "^7.0"
- },
- "require-dev": {
- "symfony/console": "^6.4|^7.0",
- "symfony/http-client": "^6.4|^7.0",
- "symfony/messenger": "^6.4|^7.0",
- "symfony/twig-bridge": "^6.4|^7.0"
- },
- "default-branch": true,
- "type": "library",
- "autoload": {
- "psr-4": {
- "Symfony\\Component\\Mailer\\": ""
- },
- "exclude-from-classmap": [
- "/Tests/"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Dan Brown",
- "homepage": "https://danb.me"
- },
- {
- "name": "Fabien Potencier",
- },
- {
- "name": "Symfony Community",
- "homepage": "https://symfony.com/contributors"
- }
- ],
- "description": "Helps sending emails",
- "homepage": "https://symfony.com",
- "support": {
- "source": "https://github.com/ssddanbrown/symfony-mailer/tree/7.2"
- },
- "time": "2025-01-11T14:57:07+00:00"
- },
{
"name": "symfony/clock",
"version": "v7.3.0",
],
"time": "2025-06-28T08:24:55+00:00"
},
+ {
+ "name": "symfony/mailer",
+ "version": "v7.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/mailer.git",
+ "reference": "b5db5105b290bdbea5ab27b89c69effcf1cb3368"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/mailer/zipball/b5db5105b290bdbea5ab27b89c69effcf1cb3368",
+ "reference": "b5db5105b290bdbea5ab27b89c69effcf1cb3368",
+ "shasum": ""
+ },
+ "require": {
+ "egulias/email-validator": "^2.1.10|^3|^4",
+ "php": ">=8.2",
+ "psr/event-dispatcher": "^1",
+ "psr/log": "^1|^2|^3",
+ "symfony/event-dispatcher": "^6.4|^7.0",
+ "symfony/mime": "^7.2",
+ "symfony/service-contracts": "^2.5|^3"
+ },
+ "conflict": {
+ "symfony/http-client-contracts": "<2.5",
+ "symfony/http-kernel": "<6.4",
+ "symfony/messenger": "<6.4",
+ "symfony/mime": "<6.4",
+ "symfony/twig-bridge": "<6.4"
+ },
+ "require-dev": {
+ "symfony/console": "^6.4|^7.0",
+ "symfony/http-client": "^6.4|^7.0",
+ "symfony/messenger": "^6.4|^7.0",
+ "symfony/twig-bridge": "^6.4|^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Mailer\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Helps sending emails",
+ "homepage": "https://symfony.com",
+ "support": {
+ "source": "https://github.com/symfony/mailer/tree/v7.3.1"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-27T19:55:54+00:00"
+ },
{
"name": "symfony/mime",
"version": "v7.3.0",
},
{
"name": "phpunit/phpunit",
- "version": "11.5.26",
+ "version": "11.5.27",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "4ad8fe263a0b55b54a8028c38a18e3c5bef312e0"
+ "reference": "446d43867314781df7e9adf79c3ec7464956fd8f"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4ad8fe263a0b55b54a8028c38a18e3c5bef312e0",
- "reference": "4ad8fe263a0b55b54a8028c38a18e3c5bef312e0",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/446d43867314781df7e9adf79c3ec7464956fd8f",
+ "reference": "446d43867314781df7e9adf79c3ec7464956fd8f",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"ext-xml": "*",
"ext-xmlwriter": "*",
- "myclabs/deep-copy": "^1.13.1",
+ "myclabs/deep-copy": "^1.13.3",
"phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1",
"php": ">=8.2",
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.26"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.27"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2025-07-04T05:58:21+00:00"
+ "time": "2025-07-11T04:10:06+00:00"
},
{
"name": "sebastian/cli-parser",
],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": {
- "ssddanbrown/symfony-mailer": 20
- },
+ "stability-flags": {},
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
public function test_cookies_samesite_none_when_iframe_hosts_set()
{
- $this->runWithEnv('ALLOWED_IFRAME_HOSTS', 'http://example.com', function () {
+ $this->runWithEnv(['ALLOWED_IFRAME_HOSTS' => 'http://example.com'], function () {
$resp = $this->get('/');
foreach ($resp->headers->getCookies() as $cookie) {
$this->assertEquals('none', $cookie->getSameSite());
public function test_secure_cookies_controlled_by_app_url()
{
- $this->runWithEnv('APP_URL', 'http://example.com', function () {
+ $this->runWithEnv(['APP_URL' => 'http://example.com'], function () {
$resp = $this->get('/');
foreach ($resp->headers->getCookies() as $cookie) {
$this->assertFalse($cookie->isSecure());
}
});
- $this->runWithEnv('APP_URL', 'https://example.com', function () {
+ $this->runWithEnv(['APP_URL' => 'https://example.com'], function () {
$resp = $this->get('/');
foreach ($resp->headers->getCookies() as $cookie) {
$this->assertTrue($cookie->isSecure());
public function test_iframe_csp_includes_extra_hosts_if_configured()
{
- $this->runWithEnv('ALLOWED_IFRAME_HOSTS', 'https://a.example.com https://b.example.com', function () {
+ $this->runWithEnv(['ALLOWED_IFRAME_HOSTS' => 'https://a.example.com https://b.example.com'], function () {
$resp = $this->get('/');
$frameHeader = $this->getCspHeader($resp, 'frame-ancestors');
* Database config is juggled so the value can be restored when
* parallel testing are used, where multiple databases exist.
*/
- protected function runWithEnv(string $name, $value, callable $callback, bool $handleDatabase = true)
+ protected function runWithEnv(array $valuesByKey, callable $callback, bool $handleDatabase = true): void
{
Env::disablePutenv();
- $originalVal = $_SERVER[$name] ?? null;
-
- if (is_null($value)) {
- unset($_SERVER[$name]);
- } else {
- $_SERVER[$name] = $value;
+ $originals = [];
+ foreach ($valuesByKey as $key => $value) {
+ $originals[$key] = $_SERVER[$key] ?? null;
+
+ if (is_null($value)) {
+ unset($_SERVER[$key]);
+ } else {
+ $_SERVER[$key] = $value;
+ }
}
$database = config('database.connections.mysql_testing.database');
DB::rollBack();
}
- if (is_null($originalVal)) {
- unset($_SERVER[$name]);
- } else {
- $_SERVER[$name] = $originalVal;
+ foreach ($originals as $key => $value) {
+ if (is_null($value)) {
+ unset($_SERVER[$key]);
+ } else {
+ $_SERVER[$key] = $value;
+ }
}
}
$functionsFile = theme_path('functions.php');
app()->alias('cat', 'dog');
file_put_contents($functionsFile, "<?php\nTheme::listen(\BookStack\Theming\ThemeEvents::APP_BOOT, function(\$app) { \$app->alias('cat', 'dog');});");
- $this->runWithEnv('APP_THEME', $themeFolder, function () {
+ $this->runWithEnv(['APP_THEME' => $themeFolder], function () {
$this->assertEquals('cat', $this->app->getAlias('dog'));
});
});
$this->expectException(ThemeException::class);
$this->expectExceptionMessageMatches('/Failed loading theme functions file at ".*?" with error: Class "BookStack\\\\Biscuits" not found/');
- $this->runWithEnv('APP_THEME', $themeFolder, fn() => null);
+ $this->runWithEnv(['APP_THEME' => $themeFolder], fn() => null);
});
}
$this->beforeApplicationDestroyed(fn() => File::deleteDirectory($themeFolderPath));
// Run provided callback with theme env option set
- $this->runWithEnv('APP_THEME', $themeFolderName, function () use ($callback, $themeFolderName) {
+ $this->runWithEnv(['APP_THEME' => $themeFolderName], function () use ($callback, $themeFolderName) {
call_user_func($callback, $themeFolderName);
});
}
{
public function test_filesystem_images_falls_back_to_storage_type_var()
{
- $this->runWithEnv('STORAGE_TYPE', 'local_secure', function () {
+ $this->runWithEnv(['STORAGE_TYPE' => 'local_secure'], function () {
$this->checkEnvConfigResult('STORAGE_IMAGE_TYPE', 's3', 'filesystems.images', 's3');
$this->checkEnvConfigResult('STORAGE_IMAGE_TYPE', null, 'filesystems.images', 'local_secure');
});
public function test_filesystem_attachments_falls_back_to_storage_type_var()
{
- $this->runWithEnv('STORAGE_TYPE', 'local_secure', function () {
+ $this->runWithEnv(['STORAGE_TYPE' => 'local_secure'], function () {
$this->checkEnvConfigResult('STORAGE_ATTACHMENT_TYPE', 's3', 'filesystems.attachments', 's3');
$this->checkEnvConfigResult('STORAGE_ATTACHMENT_TYPE', null, 'filesystems.attachments', 'local_secure');
});
$this->assertEmpty($getStreamOptions());
- $this->runWithEnv('MAIL_VERIFY_SSL', 'false', function () use ($getStreamOptions) {
+ $this->runWithEnv(['MAIL_VERIFY_SSL' => 'false'], function () use ($getStreamOptions) {
$options = $getStreamOptions();
$this->assertArrayHasKey('ssl', $options);
$this->assertFalse($options['ssl']['verify_peer']);
public function test_non_null_mail_encryption_options_enforce_smtp_scheme()
{
- $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'tls', 'mail.mailers.smtp.tls_required', true);
- $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'ssl', 'mail.mailers.smtp.tls_required', true);
- $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'null', 'mail.mailers.smtp.tls_required', false);
+ $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'tls', 'mail.mailers.smtp.require_tls', true);
+ $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'ssl', 'mail.mailers.smtp.require_tls', true);
+ $this->checkEnvConfigResult('MAIL_ENCRYPTION', 'null', 'mail.mailers.smtp.require_tls', false);
}
public function test_smtp_scheme_and_certain_port_forces_tls_usage()
/** @var EsmtpTransport $transport */
$transport = Mail::mailer('smtp')->getSymfonyTransport();
Mail::purge('smtp');
- return $transport->getTlsRequirement();
+ return $transport->isTlsRequired();
};
- config()->set([
- 'mail.mailers.smtp.tls_required' => null,
- 'mail.mailers.smtp.port' => 587,
- ]);
-
- $this->assertFalse($isMailTlsRequired());
-
- config()->set([
- 'mail.mailers.smtp.tls_required' => 'tls',
- 'mail.mailers.smtp.port' => 587,
- ]);
-
- $this->assertTrue($isMailTlsRequired());
-
- config()->set([
- 'mail.mailers.smtp.tls_required' => null,
- 'mail.mailers.smtp.port' => 465,
- ]);
+ $runTest = function (string $tlsOption, int $port, bool $expectedResult) use ($isMailTlsRequired) {
+ $this->runWithEnv(['MAIL_ENCRYPTION' => $tlsOption, 'MAIL_PORT' => $port], function () use ($isMailTlsRequired, $port, $expectedResult) {
+ $this->assertEquals($expectedResult, $isMailTlsRequired());
+ });
+ };
- $this->assertTrue($isMailTlsRequired());
+ $runTest('null', 587, false);
+ $runTest('tls', 587, true);
+ $runTest('null', 465, true);
}
public function test_mysql_host_parsed_as_expected()
];
foreach ($cases as $host => [$expectedHost, $expectedPort]) {
- $this->runWithEnv("DB_HOST", $host, function () use ($expectedHost, $expectedPort) {
+ $this->runWithEnv(["DB_HOST" => $host], function () use ($expectedHost, $expectedPort) {
$this->assertEquals($expectedHost, config("database.connections.mysql.host"));
$this->assertEquals($expectedPort, config("database.connections.mysql.port"));
}, false);
* Set an environment variable of the given name and value
* then check the given config key to see if it matches the given result.
* Providing a null $envVal clears the variable.
- *
- * @param mixed $expectedResult
*/
- protected function checkEnvConfigResult(string $envName, ?string $envVal, string $configKey, $expectedResult)
+ protected function checkEnvConfigResult(string $envName, ?string $envVal, string $configKey, mixed $expectedResult): void
{
- $this->runWithEnv($envName, $envVal, function () use ($configKey, $expectedResult) {
+ $this->runWithEnv([$envName => $envVal], function () use ($configKey, $expectedResult) {
$this->assertEquals($expectedResult, config($configKey));
});
}
{
public function test_url_helper_takes_custom_url_into_account()
{
- $this->runWithEnv('APP_URL', 'http://example.com/bookstack', function () {
+ $this->runWithEnv(['APP_URL' => 'http://example.com/bookstack'], function () {
$this->assertEquals('http://example.com/bookstack/books', url('/books'));
});
}
public function test_url_helper_sets_correct_scheme_even_when_request_scheme_is_different()
{
- $this->runWithEnv('APP_URL', 'https://example.com/', function () {
+ $this->runWithEnv(['APP_URL' => 'https://example.com/'], function () {
$this->get('http://example.com/login')->assertSee('https://example.com/dist/styles.css');
});
}