Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e2482c1
[5.x] Removed a comment from the js code output of the StaticCacher (…
micahhenshaw Mar 12, 2026
a9f513b
[5.x] Fix ensure field has config (#14195)
marcorieser Mar 12, 2026
7c820bb
[5.x] Relationship endpoint authorization (#14254)
jasonvarga Mar 13, 2026
5b0c01c
changelog
jasonvarga Mar 13, 2026
ecc1caa
[5.x] Sanitize SVGs on asset reupload (#14270)
jasonvarga Mar 16, 2026
f243f13
[5.x] Prevent path traversal in file dictionary (#14272)
duncanmcclean Mar 17, 2026
92cb9e2
[5.x] Prevent term creation via fieldtype without permission (#14274)
duncanmcclean Mar 17, 2026
6dd9fb6
changelog
jasonvarga Mar 17, 2026
27105a1
[5.x] Add additional `URL::isExternalToApplication()` tests (#14288)
duncanmcclean Mar 18, 2026
beb2adc
[5.x] Harden password reset (#14296)
jasonvarga Mar 18, 2026
ea0fa7f
changelog
jasonvarga Mar 18, 2026
a0aea71
[5.x] Fix PHP sanitization edge cases (#14300)
duncanmcclean Mar 20, 2026
90a6e0b
[5.x] Fix live preview token scope (#14304)
jasonvarga Mar 20, 2026
5fe9103
[5.x] Handle more cases in external url detection (#14312)
jasonvarga Mar 20, 2026
828bbf9
[5.x] Allow external redirects from Form::getSubmissionRedirect (#14318)
jasonvarga Mar 22, 2026
b193c40
[5.x] Relationship fieldtype authorization tweaks (#14307)
duncanmcclean Mar 23, 2026
3eaa80c
[5.x] Add CSP header to svg route (#14325)
jasonvarga Mar 23, 2026
33e0ceb
[5.x] Add authorization to revision routes (#14301)
duncanmcclean Mar 23, 2026
cdf7ab4
[5.x] Restrict markdown preview endpoint (#14326)
jasonvarga Mar 24, 2026
3259d65
[5.x] Sanitize password reset form redirect value (#14327)
jasonvarga Mar 24, 2026
72768d7
[5.x] Fix config through Antlers views (#14328)
jasonvarga Mar 24, 2026
291a660
changelog
jasonvarga Mar 24, 2026
7a77832
[5.x] Fix term revisions error (#14347)
duncanmcclean Mar 25, 2026
7e37196
changelog
jasonvarga Mar 25, 2026
5dda35e
[5.x] Harden OrderBys (#14421)
jasonvarga Apr 1, 2026
9c22645
changelog
jasonvarga Apr 2, 2026
afd636f
[5.x] Fix form submission types (#14430)
daun Apr 3, 2026
e9edd39
[5.x] Fix Stache index re-entrancy causing null URIs on cold stache (…
o1y Apr 3, 2026
868d5ad
[5.x] Add `@default` support to Antlers content allowlists (#14440)
jasonvarga Apr 6, 2026
73cc75a
[5.x] Add `link` tag to allowed Antlers tags (#14438)
edalzell Apr 6, 2026
7b0694e
changelog
jasonvarga Apr 7, 2026
f3893f6
[5.x] Remove negative assertions from `TestCase` (#14458)
duncanmcclean Apr 8, 2026
ee88420
[5.x] Harden OrderBys (#14474)
duncanmcclean Apr 10, 2026
88dd005
[5.x] Harden query value resolution (#14476)
duncanmcclean Apr 13, 2026
a6ba7c5
changelog
jasonvarga Apr 13, 2026
bc302de
[5.x] Always show success when using forgot password form (#14539)
jasonvarga Apr 23, 2026
d130ae2
changelog
jasonvarga Apr 27, 2026
21da7d1
Merge branch '5.x' into statamic-5.73.21
markjensen-novu May 4, 2026
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
83 changes: 83 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,88 @@
# Release Notes

## 5.73.21 (2026-04-27)

### What's fixed
- Always show success when using forgot password form [#14539](https://github.com/statamic/cms/issues/14539) by @jasonvarga



## 5.73.20 (2026-04-13)

### What's fixed
- Harden query value resolution [#14476](https://github.com/statamic/cms/issues/14476) by @duncanmcclean
- Harden OrderBys [#14474](https://github.com/statamic/cms/issues/14474) by @duncanmcclean
- Remove negative assertions from `TestCase` [#14458](https://github.com/statamic/cms/issues/14458) by @duncanmcclean



## 5.73.19 (2026-04-07)

### What's fixed
- Add `link` tag to allowed Antlers tags [#14438](https://github.com/statamic/cms/issues/14438) by @edalzell
- Add `@default` support to Antlers content allowlists [#14440](https://github.com/statamic/cms/issues/14440) by @jasonvarga
- Fix Stache index re-entrancy causing null URIs on cold stache [#14181](https://github.com/statamic/cms/issues/14181) by @o1y
- Fix form submission types [#14430](https://github.com/statamic/cms/issues/14430) by @daun



## 5.73.18 (2026-04-02)

### What's fixed
- Harden OrderBys [#14421](https://github.com/statamic/cms/issues/14421) by @jasonvarga



## 5.73.17 (2026-03-25)

### What's fixed
- Fix term revisions error [#14347](https://github.com/statamic/cms/issues/14347) by @duncanmcclean



## 5.73.16 (2026-03-24)

### What's fixed
- Fix config through Antlers views [#14328](https://github.com/statamic/cms/issues/14328) by @jasonvarga
- Sanitize password reset form redirect value [#14327](https://github.com/statamic/cms/issues/14327) by @jasonvarga
- Restrict markdown preview endpoint [#14326](https://github.com/statamic/cms/issues/14326) by @jasonvarga
- Add authorization to revision routes [#14301](https://github.com/statamic/cms/issues/14301) by @duncanmcclean
- Add CSP header to svg route [#14325](https://github.com/statamic/cms/issues/14325) by @jasonvarga
- Relationship fieldtype authorization tweaks [#14307](https://github.com/statamic/cms/issues/14307) by @duncanmcclean
- Allow external redirects from Form::getSubmissionRedirect [#14318](https://github.com/statamic/cms/issues/14318) by @jasonvarga
- Handle more cases in external url detection [#14312](https://github.com/statamic/cms/issues/14312) by @jasonvarga
- Fix live preview token scope [#14304](https://github.com/statamic/cms/issues/14304) by @jasonvarga
- Fix PHP sanitization edge cases [#14300](https://github.com/statamic/cms/issues/14300) by @duncanmcclean



## 5.73.15 (2026-03-18)

### What's fixed
- Harden password reset [#14296](https://github.com/statamic/cms/issues/14296) by @jasonvarga
- Add additional `URL::isExternalToApplication()` tests [#14288](https://github.com/statamic/cms/issues/14288) by @duncanmcclean



## 5.73.14 (2026-03-17)

### What's fixed
- Prevent term creation via fieldtype without permission [#14274](https://github.com/statamic/cms/issues/14274) by @duncanmcclean
- Prevent path traversal in file dictionary [#14272](https://github.com/statamic/cms/issues/14272) by @duncanmcclean
- Sanitize SVGs on asset reupload [#14270](https://github.com/statamic/cms/issues/14270) by @jasonvarga



## 5.73.13 (2026-03-13)

### What's fixed
- Relationship endpoint authorization [#14254](https://github.com/statamic/cms/issues/14254) by @jasonvarga
- Fix ensure field has config [#14195](https://github.com/statamic/cms/issues/14195) by @marcorieser
- Removed a comment from the js code output of the StaticCacher [#14233](https://github.com/statamic/cms/issues/14233) by @micahhenshaw
- Acquire stache-warming lock in Duplicates::find [#14176](https://github.com/statamic/cms/issues/14176) by @mmodler



## 5.73.12 (2026-03-04)

### What's fixed
Expand Down
21 changes: 21 additions & 0 deletions config/antlers.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,25 @@

],

/*
|--------------------------------------------------------------------------
| User content allowlists
|--------------------------------------------------------------------------
|
| These control which tags and modifiers will be permitted in user-supplied
| Antlers (e.g. fields with `antlers: true`). Include the literal string
| `@default` in the array to merge Statamic's defaults with your own.
|
*/

// 'allowedContentTags' => [
// '@default',
// 'foo:*',
// ],

// 'allowedContentModifiers' => [
// '@default',
// 'foo'
// ],

];
39 changes: 0 additions & 39 deletions resources/js/components/terms/PublishForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -131,45 +131,6 @@
</div>
</div>

<!--
TODO
<div class="flex items-center border-t justify-between px-4 py-2" v-if="!revisionsEnabled">
<label v-text="__('Published')" class="publish-field-label font-medium" />
<toggle-input v-model="published" />
</div>

<div class="border-t p-4" v-if="revisionsEnabled">
<label class="publish-field-label font-medium mb-2" v-text="__('Revisions')"/>
<div class="mb-1 flex items-center" v-if="published">
<span class="text-green-600 w-6 text-center">&check;</span>
<span class="text-2xs" v-text="__('Entry has a published version')"></span>
</div>
<div class="mb-1 flex items-center" v-else="published">
<span class="text-orange w-6 text-center">!</span>
<span class="text-2xs" v-text="__('Entry has not been published')"></span>
</div>
<div class="mb-1 flex items-center" v-if="isWorkingCopy && isDirty">
<span class="text-orange w-6 text-center">!</span>
<span class="text-2xs" v-text="__('Working copy has unsaved changes')"></span>
</div>
<div class="mb-1 flex items-center" v-else-if="isWorkingCopy">
<span class="text-orange w-6 text-center">!</span>
<span class="text-2xs" v-text="__('Entry has unpublished changes')"></span>
</div>
<div class="mb-1 flex items-center" v-if="!isWorkingCopy && published">
<span class="text-green-600 w-6 text-center">&check;</span>
<span class="text-2xs" v-text="__('This is the published version')"></span>
</div>
<button
class="flex items-center justify-center mt-4 btn-flat px-2 w-full"
v-if="!isCreating && revisionsEnabled"
@click="showRevisionHistory = true">
<svg-icon name="history" class="h-4 w-4 rtl:ml-2 ltr:mr-2" />
<span>{{ __('View History') }}</span>
</button>
</div>
-->

<div class="p-4 border-t dark:border-dark-900" v-if="localizations.length > 1">
<label class="publish-field-label font-medium mb-2" v-text="__('Sites')" />
<div
Expand Down
2 changes: 0 additions & 2 deletions resources/views/terms/edit.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@
:initial-origin-values="{{ json_encode($originValues) }}"
:initial-origin-meta="{{ json_encode($originMeta) }}"
initial-site="{{ $locale }}"
:initial-is-working-copy="{{ $str::bool($hasWorkingCopy) }}"
:initial-is-root="{{ $str::bool($isRoot) }}"
:revisions-enabled="{{ $str::bool($revisionsEnabled) }}"
:initial-read-only="{{ $str::bool($readOnly) }}"
:preloaded-assets="{{ json_encode($preloadedAssets) }}"
:breadcrumbs="{{ $breadcrumbs->toJson() }}"
Expand Down
8 changes: 0 additions & 8 deletions routes/cp.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,10 @@
use Statamic\Http\Controllers\CP\StartPageController;
use Statamic\Http\Controllers\CP\Taxonomies\PublishedTermsController;
use Statamic\Http\Controllers\CP\Taxonomies\ReorderTaxonomyBlueprintsController;
use Statamic\Http\Controllers\CP\Taxonomies\RestoreTermRevisionController;
use Statamic\Http\Controllers\CP\Taxonomies\TaxonomiesController;
use Statamic\Http\Controllers\CP\Taxonomies\TaxonomyBlueprintsController;
use Statamic\Http\Controllers\CP\Taxonomies\TermActionController;
use Statamic\Http\Controllers\CP\Taxonomies\TermPreviewController;
use Statamic\Http\Controllers\CP\Taxonomies\TermRevisionsController;
use Statamic\Http\Controllers\CP\Taxonomies\TermsController;
use Statamic\Http\Controllers\CP\Updater\UpdateProductController;
use Statamic\Http\Controllers\CP\Updater\UpdaterController;
Expand Down Expand Up @@ -202,12 +200,6 @@
Route::post('/', [PublishedTermsController::class, 'store'])->name('taxonomies.terms.published.store');
Route::delete('/', [PublishedTermsController::class, 'destroy'])->name('taxonomies.terms.published.destroy');

Route::resource('revisions', TermRevisionsController::class, [
'as' => 'taxonomies.terms',
'only' => ['index', 'store', 'show'],
]);

Route::post('restore-revision', RestoreTermRevisionController::class)->name('taxonomies.terms.restore-revision');
Route::post('preview', [TermPreviewController::class, 'edit'])->name('taxonomies.terms.preview.edit');
Route::get('preview', [TermPreviewController::class, 'show'])->name('taxonomies.terms.preview.popout');
Route::patch('/', [TermsController::class, 'update'])->name('taxonomies.terms.update');
Expand Down
25 changes: 24 additions & 1 deletion src/Assets/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use League\Flysystem\PathTraversalDetected;
use Rhukster\DomSanitizer\DOMSanitizer;
use Statamic\Assets\AssetUploader as Uploader;
use Statamic\Contracts\Assets\Asset as AssetContract;
use Statamic\Contracts\Assets\AssetContainer as AssetContainerContract;
Expand Down Expand Up @@ -945,6 +946,17 @@ public function reupload(ReplacementFile $file)

$file->writeTo($this->disk()->filesystem(), $this->path());

if ($this->isSvg() && config('statamic.assets.svg_sanitization_on_upload', true)) {
$contents = $this->disk()->get($this->path());

$this->disk()->put(
$this->path(),
(new DOMSanitizer(DOMSanitizer::SVG))->sanitize($contents, [
'remove-xml-tags' => ! Str::startsWith($contents, '<?xml'),
])
);
}

$this->clearCaches();
$this->writeMeta($this->generateMeta());

Expand Down Expand Up @@ -1104,7 +1116,7 @@ public function hasDuration()

public function getQueryableValue(string $field)
{
if (method_exists($this, $method = Str::camel($field))) {
if (in_array($method = Str::camel($field), $this->queryableMethods())) {
return $this->{$method}();
}

Expand All @@ -1117,6 +1129,17 @@ public function getQueryableValue(string $field)
return $field->fieldtype()->toQueryableValue($value);
}

private function queryableMethods(): array
{
return [
'absoluteUrl', 'apiUrl', 'basename', 'blueprint', 'containerId', 'containerHandle', 'dimensions',
'duration', 'editUrl', 'exists', 'extension', 'filename', 'folder', 'guessedExtension',
'hasDimensions', 'hasDuration', 'height', 'id', 'isAudio', 'isImage', 'isMedia', 'isPdf',
'isPreviewable', 'isSvg', 'isVideo', 'lastModified', 'mimeType', 'orientation', 'path', 'pdfUrl',
'ratio', 'reference', 'size', 'thumbnailUrl', 'title', 'url', 'width',
];
}

public function getCurrentDirtyStateAttributes(): array
{
return array_merge([
Expand Down
22 changes: 21 additions & 1 deletion src/Assets/AssetContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Statamic\Contracts\Assets\AssetContainer as AssetContainerContract;
use Statamic\Contracts\Data\Augmentable;
use Statamic\Contracts\Data\Augmented;
use Statamic\Contracts\Query\ContainsQueryableValues;
use Statamic\Data\ExistsAsFile;
use Statamic\Data\HasAugmentedInstance;
use Statamic\Events\AssetContainerBlueprintFound;
Expand All @@ -27,9 +28,10 @@
use Statamic\Facades\Stache;
use Statamic\Facades\URL;
use Statamic\Support\Arr;
use Statamic\Support\Str;
use Statamic\Support\Traits\FluentlyGetsAndSets;

class AssetContainer implements Arrayable, ArrayAccess, AssetContainerContract, Augmentable
class AssetContainer implements Arrayable, ArrayAccess, AssetContainerContract, Augmentable, ContainsQueryableValues
{
use ExistsAsFile, FluentlyGetsAndSets, HasAugmentedInstance;

Expand Down Expand Up @@ -693,6 +695,24 @@ public static function __callStatic($method, $parameters)
return Facades\AssetContainer::{$method}(...$parameters);
}

public function getQueryableValue(string $field)
{
if (in_array($method = Str::camel($field), $this->queryableMethods())) {
return $this->{$method}();
}

return null;
}

private function queryableMethods(): array
{
return [
'absoluteUrl', 'accessible', 'allowDownloading', 'allowMoving', 'allowRenaming', 'allowUploads',
'blueprint', 'createFolders', 'diskHandle', 'diskPath', 'editUrl', 'handle', 'hasSearchIndex',
'id', 'path', 'private', 'searchIndex', 'showUrl', 'sortDirection', 'sortField', 'title', 'url',
];
}

public function __toString()
{
return $this->handle();
Expand Down
19 changes: 18 additions & 1 deletion src/Assets/AssetFolder.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
use League\Flysystem\PathTraversalDetected;
use Statamic\Assets\AssetUploader as Uploader;
use Statamic\Contracts\Assets\AssetFolder as Contract;
use Statamic\Contracts\Query\ContainsQueryableValues;
use Statamic\Events\AssetFolderDeleted;
use Statamic\Events\AssetFolderSaved;
use Statamic\Facades\AssetContainer;
use Statamic\Facades\Path;
use Statamic\Support\Str;
use Statamic\Support\Traits\FluentlyGetsAndSets;

class AssetFolder implements Arrayable, Contract
class AssetFolder implements Arrayable, ContainsQueryableValues, Contract
{
use FluentlyGetsAndSets;

Expand Down Expand Up @@ -237,4 +238,20 @@ public function toArray()
'basename' => (string) $this->basename(),
];
}

public function getQueryableValue(string $field)
{
if (in_array($method = Str::camel($field), $this->queryableMethods())) {
return $this->{$method}();
}

return null;
}

private function queryableMethods(): array
{
return [
'basename', 'count', 'lastModified', 'path', 'resolvedPath', 'size', 'title',
];
}
}
2 changes: 1 addition & 1 deletion src/Auth/Protect/Protectors/Password/PasswordProtector.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function protect()
throw new ForbiddenHttpException();
}

if (request()->isLivePreview()) {
if (request()->isLivePreviewOf($this->data)) {
return;
}

Expand Down
39 changes: 5 additions & 34 deletions src/Auth/SendsPasswordResetEmails.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,12 @@ public function sendResetLinkEmail(Request $request)
{
$this->validateEmail($request);

// We will send the password reset link to this user. Once we have attempted
// to send the link, we will examine the response then see the message we
// need to show to the user. Finally, we'll send out a proper response.
$response = $this->broker()->sendResetLink(
$this->credentials($request)
);
// Always return the generic "reset link sent" response regardless of the broker's
// actual result. INVALID_USER and RESET_THROTTLED would each reveal whether the
// email belongs to a registered account (the broker only throttles real users).
$this->broker()->sendResetLink($this->credentials($request));

return $response == Password::RESET_LINK_SENT
? $this->sendResetLinkResponse($request, $response)
: $this->sendResetLinkFailedResponse($request, $response);
return $this->sendResetLinkResponse($request, Password::RESET_LINK_SENT);
}

/**
Expand Down Expand Up @@ -97,31 +93,6 @@ protected function sendResetLinkResponse(Request $request, $response)
: $redirect->with('status', trans($response));
}

/**
* Get the response for a failed password reset link.
*
* @param string $response
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\JsonResponse
*/
protected function sendResetLinkFailedResponse(Request $request, $response)
{
$errorRedirect = $request->input('_error_redirect');

$redirect = $errorRedirect && ! URL::isExternalToApplication($errorRedirect)
? redirect($errorRedirect)
: back();

if ($request->wantsJson()) {
throw ValidationException::withMessages([
'email' => [trans($response)],
]);
}

return $redirect
->withInput($request->only('email'))
->withErrors(['email' => trans($response)], 'user.forgot_password');
}

/**
* Get the broker to be used during password reset.
*
Expand Down
Loading
Loading