Skip to content

Latest commit

 

History

History
173 lines (123 loc) · 13.7 KB

5.1.0.md

File metadata and controls

173 lines (123 loc) · 13.7 KB
title
5.1.0 (unreleased)

5.1.0 (unreleased)

Overview

Security considerations {#security-considerations}

This release includes several security fixes. Review the individual vulnerability disclosure for more detailed descriptions of each security fix. We highly encourage upgrading your project to include the latest security patches.

We have provided a severity rating of the vulnerabilities below based on the CVSS score. Note that the impact of each vulnerability could vary based on the specifics of each project. You can read the severity rating definitions in the Silverstripe CMS release process.

  • CVE-2023-32302 - Members with no password can be created and bypass custom login forms Severity: Low When a new Member record was created in the cms it was possible to set a blank password. If an attacker knows the email address of the user with the blank password then they can attempt to log in using an empty password. The default member authenticator, login form and basic auth all require a non-empty password, however if a custom authentication method is used it may allow a successful login with the empty password. Starting with this release, blank passwords are no no longer allowed when members are created in the CMS. Programatically created Member records, such as those used in unit tests, still allow blank passwords. You may have some Member records in your system already which have empty passwords. To detect these, you can loop over all Member records with Member::get() and pass each record into the below method. It might be sensible to create a BuildTask for this purpose.
      private function memberHasBlankPassword(Member $member): bool
      {
          // skip default admin as this is created programatically
          if ($member->isDefaultAdmin()) {
              return false;
          }
          // return true if a blank password is valid for this member
          $authenticator = new MemberAuthenticator();
          return $authenticator->checkPassword($member, '')->isValid();
      }
    Once you have identified the records with empty passwords, it's up to you how to handle this. The most sensible way to resolve this is probably to generate a new secure password for each of these members, mark it as immediately expired, and email each affected member (assuming they have a valid email address in the system).

Features and enhancements

Eager loading

When looping over nested relationships the ORM is prone to the N + 1 query problem where excessive database calls are made. Eager loading has been introduced via the new DataList::eagerLoad() method which alleviates the N + 1 problem by querying the nested relationship tables before they are needed using a single large WHERE ID in ($ids) SQL query instead of many WHERE RelationID = $id queries.

Imagine the following example where there is a Team model with 20 records, with a has_many relation "Players"

// Regular ORM usage without eager loading
// This would result in 21 SQL SELECT queries, 1 for Teams and 20 for Players
$teams = Team::get();

// Using the `eagerLoad()` method to eager load data from nested models (up to 3 relations deep)
// This will result in only 2 SQL SELECT queries, 1 for Teams and 1 for Players
$teams = Team::get()->eagerLoad('Players');

foreach ($teams as $team) {
    foreach ($team->Players() as $player) {
        echo $player->FirstName;
    }
}

In a test setup with looping through 100 DataObjects each with 100 related DataObjects for a total of 10,000 records per test run, the following performance improvements were observed for different types of relations (eager-loading vs not eager-loading):

  • HasOne - 3227% faster (0.0078s vs 0.2595s)
  • HasMany - 25% faster (0.1453s vs 0.1819s)
  • ManyMany - 25% faster (0.1664s vs 0.2083s)
  • ManyManyThrough - 16% faster (0.6586s vs 0.7681s)

Read more about eager loading including its limitations in the developer docs.

Improvement to page search performance with Elemental {#cms-search-performance}

  • The CMS search has been optimised to reduce the number of database queries made when searching for pages with elemental content blocks. This has resulted in a small performance improvement. In our test environment, with 1,000 pages each with 5 content blocks we observed a 9% performance improvement. Performance will vary with your environment.
  • A new opt-in behaviour is available that makes a very large difference to performance when using elemental content blocks. In testing, this behaviour was more than 100% faster (i.e. halving the response time) of the sitetree search request. The opt-in feature disables the default behaviour of rendering all content blocks for CMS search. Instead, it simply extracts the database contents of the elements from its text and html fields. There is a downside to consider which is that any related content not directly on the element will not be matched against the search query. Note this does not use the $searchable_fields config. To opt-in to this behaviour, use the following config:
DNADesign\Elemental\Controllers\ElementSiteTreeFilterSearch:
  render_elements: false

If render_elements is set to false then individual fields on elements can be excluded from search by adding them to a config array:

App\MyElement:
  fields_excluded_from_cms_search:
    - MyFieldToExclude
    - AnotherFieldToExclude

New InheritedPermissions option - only these members {#only-these-members}

Applying the InheritedPermissionsExtension to a DataObject class gives you the ability to declare that only users in certain groups can view or edit those records. This extension is applied by default to the File and SiteTree classes.

A new permission has been added to InheritedPermissions, which powers that extension. The new permission (InheritedPermissions::ONLY_THESE_MEMBERS) allows you to define which specific Member records should have access to your records, regardless of which groups those members belong to.

In the CMS, this new permission is available for files and pages by setting "Who can view/edit this page/file" to "Only these users".

Optimised queries when filtering against IDs {#filter-by-ids}

DataList queries filtering against a list of IDs have been optimised when all of the following criteria are met:

  • the column being filtered is a DBPrimarykey or a DBForiegnKey
  • the values being filtered are all either integers or valid integer strings
  • using placeholders for integer ids has been configured off, which is the default config value.

If you want to disable this optimisation you can do so with this configuration:

SilverStripe\ORM\DataList:
  use_placeholders_for_integer_ids: true

The following performance improvements were measured in a test setup where 10,000 record IDs were passed in:

  • DataList::byIDs() - 198% faster - (0.0608s vs 0.1812s)
  • RelationList::foreignIDFilter()
    • HasManyList::foreignIDFilter() - 108% faster (0.1584s vs 0.3304s)
    • ManyManyList::foreignIDFilter() - 108% faster (0.1529s vs 0.3119s)
    • ManyManyThroughList::foreignIDFilter() - 27% faster (0.6901s vs (0.8766s)

Session manager anonymize stored IP addresses {#session-manager-ip}

A configuration option has been added to Session Manager to anonymize stored IP addresses for enhanced privacy and compliance.

If you want to anonymize stored IP addresses then use the following configuration:

SilverStripe\SessionManager\Models\LoginSession:
  anonymize_ip: true

Other new features

  • You can now exclude specific DataObject models from the check and repair step of dev/build - see ORM Performance for more information.
  • You can change what SearchFilter the TreeDropdownField uses with yaml configuration - see ORM Performance for more information.
  • The i18nTextCollector now collects strings for ORM properties (e.g. $db fields) in DataObject and Extension classes, and from themes. See i18n - collecting text for more details.
  • Extensions which modify permissions for Group records which return true will be respected, the same as when modifying permissions for any other DataObject record.
  • The ListboxField now has a react component, and can be used in react-powered contexts such as within elemental blocks
  • A new FieldsValidator class has been added, which simply calls validate() on all data fields in the form to ensure fields have valid values. Functionally equivalent to an empty RequiredFields validator.
  • A configuration option has been added to GarbageCollectionService to limit the number of items it removes each time you run garbage collection on session data. See garbage collection for more details.
  • In your GraphQL schemas, you can now define the pagination limit at a schema level. See limiting pagination in the GraphQL documentation for more details.

API changes

silverstripe/framework

  • BuildTask now has boolean is_enabled configuration option which has precedence over the existing BuildTask::enabled protected class property. The BuildTask::enabled property has been marked as deprecated and will be removed in CMS 6 if favour of using is_enabled instead.
  • Passing an argument for $limit that is not array|string|null in SilverStripe\ORM\Search\SearchContext::getQuery() will throw a deprecation warning. In CMS 6 the parameter type will be changed from dynamic to array|string|null.

silverstripe/elemental-fileblock

  • The FileBlock::getSummaryThumbnail() method has been marked as deprecated and will be removed in CMS 6 without equivalent functionality to replace it, as it is no longer required for the elemental block's preview summary.

Dependency changes

  • The unsupported modules silverstripe/widgets and silverstripe/content-widget were removed from silverstripe/recipe-blog. They were accidentally included in the 2.0.0 release of silverstripe/recipe-blog. The silverstripe/widgets and silverstripe/content-widget modules are CMS-5-compatible though unsupported. If your project relies on silverstripe/widgets or silverstripe/content-widget, manually update your project's composer.json file to explicitly require these modules.

Bug fixes

  • DataList::filterAny() queries on many-many relations that use an aggregate HAVING clause now correctly use an OR conjunction rather than an incorrect AND conjunction.
  • At some point shortly before the release of Silverstripe CMS 4.0.0, SSL support for database connections was accidentally removed. This has now been reinstated - see Using SSL in database connections for more information.
  • The cascade_duplicates property was added to the InheritedPermissionsExtension class so that now when duplicating any object that has the InheritedPermissionsExtension applied, the GroupID values in the ViewerGroups and EditGroups mapping tables will also be duplicated so that new object retains the same viewer and editor groups as the original.
  • Any fields added to a model's $summary_fields configuration which are not backed by database fields (such as method calls) will no longer be pulled through when searchableFields() calls back on it (i.e. because $searchable_fields configuration has not been explicitly declared). This means you do not need to explicitly declare $searchable_fields for models which should only use the summary fields to filter by.

This release includes a number of bug fixes to improve a broad range of areas. Check the change logs for full details of these fixes split by module. Thank you to the community members that helped contribute these fixes as part of the release!