diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ClassResolverConstraint.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ClassResolverConstraint.php
new file mode 100644
index 0000000000000000000000000000000000000000..677de9e73974bc0ac8d7b44782946079918840b8
--- /dev/null
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ClassResolverConstraint.php
@@ -0,0 +1,59 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
+
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\Core\Validation\Attribute\Constraint;
+use Symfony\Component\Validator\Constraint as SymfonyConstraint;
+
+/**
+ * Checks if a method on a service or instantiated object returns true.
+ *
+ *  For example to call the method 'isValidScheme' on the service
+ *  'stream_wrapper_manager', use: ['stream_wrapper_manager', 'isValidScheme'].
+ *  It is also possible to use a class if it implements
+ *  ContainerInjectionInterface. It will use the ClassResolver to resolve the
+ *  class and return an instance. Then it will call the configured method on
+ *  that instance.
+ *
+ *  The called method should return TRUE when the result is valid. All other
+ *  values will be considered as invalid.
+ */
+#[Constraint(
+  id: 'ClassResolver',
+  label: new TranslatableMarkup('Call a method on a service', [], ['context' => 'Validation']),
+  type: FALSE,
+)]
+class ClassResolverConstraint extends SymfonyConstraint {
+
+  /**
+   * The error message if validation fails.
+   *
+   * @var string
+   */
+  public string $message = "Calling '@method' method with value '@value' on '@classOrService' evaluated as invalid.";
+
+  /**
+   * Class or service.
+   *
+   * @var array
+   */
+  public string $classOrService;
+
+  /**
+   * Method to call.
+   *
+   * @var string
+   */
+  public string $method;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRequiredOptions(): array {
+    return ['classOrService', 'method'];
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ClassResolverConstraintValidator.php b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ClassResolverConstraintValidator.php
new file mode 100644
index 0000000000000000000000000000000000000000..6a4215b19f57f485cb774dddacb7aa3082a379f6
--- /dev/null
+++ b/core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint/ClassResolverConstraintValidator.php
@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types = 1);
+
+namespace Drupal\Core\Validation\Plugin\Validation\Constraint;
+
+use Drupal\Core\DependencyInjection\ClassResolver;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+use Symfony\Component\Validator\Exception\UnexpectedTypeException;
+
+/**
+ * Validates if a method on a service or instantiated object returns true.
+ */
+class ClassResolverConstraintValidator extends ConstraintValidator implements ContainerInjectionInterface {
+
+  public function __construct(protected ClassResolver $classResolver) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container): static {
+    return new static(
+      $container->get('class_resolver')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function validate(mixed $value, Constraint $constraint): void {
+
+    if (!$constraint instanceof ClassResolverConstraint) {
+      throw new UnexpectedTypeException($constraint, ClassResolverConstraint::class);
+    }
+    $service = $this->classResolver->getInstanceFromDefinition($constraint->classOrService);
+
+    if (!method_exists($service, $constraint->method)) {
+      throw new \InvalidArgumentException('The method "' . $constraint->method . '" does not exist on the service "' . $constraint->classOrService . '".');
+    }
+
+    $result = $service->{$constraint->method}($value);
+    if ($result !== TRUE) {
+      $this->context->buildViolation($constraint->message)
+        ->setParameter('@classOrService', $constraint->classOrService)
+        ->setParameter('@method', $constraint->method)
+        ->setParameter('@value', $value)
+        ->addViolation();
+    }
+  }
+
+}
diff --git a/core/modules/image/tests/src/Kernel/ImageStyleCustomStreamWrappersTest.php b/core/modules/image/tests/src/Kernel/ImageStyleCustomStreamWrappersTest.php
index f9b6ab641805b4fc4286a5b86a9a86a84f53d34b..88e25d8ee4830bedc58b4eb01bd679ddd4104ade 100644
--- a/core/modules/image/tests/src/Kernel/ImageStyleCustomStreamWrappersTest.php
+++ b/core/modules/image/tests/src/Kernel/ImageStyleCustomStreamWrappersTest.php
@@ -46,7 +46,11 @@ class ImageStyleCustomStreamWrappersTest extends KernelTestBase {
   protected function setUp(): void {
     parent::setUp();
     $this->fileSystem = $this->container->get('file_system');
-    $this->config('system.file')->set('default_scheme', 'public')->save();
+    $this->config('system.file')
+      ->set('default_scheme', 'public')
+      ->set('allow_insecure_uploads', FALSE)
+      ->set('temporary_maximum_age', 21600)
+      ->save();
     $this->imageStyle = ImageStyle::create([
       'name' => 'test',
       'label' => 'Test',
diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml
index 65ddbe0c32b797062ade56372bd4cc6deb7870a6..8d19b0dd2aa0ad5f386682e15ca3bb6d859a6c9e 100644
--- a/core/modules/system/config/schema/system.schema.yml
+++ b/core/modules/system/config/schema/system.schema.yml
@@ -334,6 +334,8 @@ system.action.*:
 system.file:
   type: config_object
   label: 'File system'
+  constraints:
+    FullyValidatable: ~
   mapping:
     allow_insecure_uploads:
       type: boolean
@@ -341,6 +343,10 @@ system.file:
     default_scheme:
       type: string
       label: 'Default download method'
+      constraints:
+        ClassResolver:
+          classOrService: 'stream_wrapper_manager'
+          method: 'isValidScheme'
     path:
       type: mapping
       label: 'Path settings'
@@ -348,6 +354,9 @@ system.file:
     temporary_maximum_age:
       type: integer
       label: 'Maximum age for temporary files'
+      constraints:
+        Range:
+          min: 0
 
 system.image:
   type: config_object
diff --git a/core/modules/update/tests/src/Kernel/UpdateDeleteFileIfStaleTest.php b/core/modules/update/tests/src/Kernel/UpdateDeleteFileIfStaleTest.php
index 16fc7dfe3381e5939eae01e86436ef335613c354..ff5a8b02d457fbe846decb5acf503685e04490b7 100644
--- a/core/modules/update/tests/src/Kernel/UpdateDeleteFileIfStaleTest.php
+++ b/core/modules/update/tests/src/Kernel/UpdateDeleteFileIfStaleTest.php
@@ -13,6 +13,16 @@
  */
 class UpdateDeleteFileIfStaleTest extends KernelTestBase {
 
+  /**
+   * Disable strict config schema checking.
+   *
+   * This test requires saving invalid configuration. This allows for the
+   * simulation of a temporary file becoming stale.
+   *
+   * @var bool
+   */
+  protected $strictConfigSchema = FALSE;
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/tests/Drupal/KernelTests/Core/File/FileTestBase.php b/core/tests/Drupal/KernelTests/Core/File/FileTestBase.php
index d41860c72f92bf50a263c0965d4ebf0b9d932d79..5745e6a38a91a3b9cd39bc0ad150aefe6a70c1af 100644
--- a/core/tests/Drupal/KernelTests/Core/File/FileTestBase.php
+++ b/core/tests/Drupal/KernelTests/Core/File/FileTestBase.php
@@ -41,7 +41,11 @@ protected function setUp(): void {
     // file_default_scheme(). As we are creating the configuration here remove
     // the global override.
     unset($GLOBALS['config']['system.file']);
-    \Drupal::configFactory()->getEditable('system.file')->set('default_scheme', 'public')->save();
+    \Drupal::configFactory()->getEditable('system.file')
+      ->set('default_scheme', 'public')
+      ->set('allow_insecure_uploads', FALSE)
+      ->set('temporary_maximum_age', 21600)
+      ->save();
   }
 
   /**
diff --git a/core/tests/Drupal/KernelTests/Core/TypedData/ClassResolverConstraintValidatorTest.php b/core/tests/Drupal/KernelTests/Core/TypedData/ClassResolverConstraintValidatorTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..000f831117410d4a682a5cb9442408bb6f844bc5
--- /dev/null
+++ b/core/tests/Drupal/KernelTests/Core/TypedData/ClassResolverConstraintValidatorTest.php
@@ -0,0 +1,139 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\KernelTests\Core\TypedData;
+
+use Drupal\Core\TypedData\DataDefinition;
+use Drupal\Core\TypedData\TypedDataManagerInterface;
+use Drupal\KernelTests\KernelTestBase;
+
+/**
+ * Tests ClassResolver validation constraint with both valid and invalid values.
+ *
+ * @covers \Drupal\Core\Validation\Plugin\Validation\Constraint\ClassResolverConstraintValidator
+ * @group Validation
+ */
+class ClassResolverConstraintValidatorTest extends KernelTestBase {
+
+  /**
+   * The typed data manager to use.
+   *
+   * @var \Drupal\Core\TypedData\TypedDataManager
+   */
+  protected TypedDataManagerInterface $typedData;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+
+    $this->typedData = $this->container->get('typed_data_manager');
+    $this->container->set('test.service', new class() {
+
+      /**
+       * Dummy method to return TRUE.
+       *
+       * @return bool
+       *   TRUE.
+       */
+      public function returnTrue(): bool {
+        return TRUE;
+      }
+
+      /**
+       * Dummy method to return FALSE.
+       *
+       * @return bool
+       *   FALSE.
+       */
+      public function returnFalse(): bool {
+        return FALSE;
+      }
+
+      /**
+       * Dummy method to return a truthy value.
+       *
+       * @return string
+       *   A string that evaluates to TRUE.
+       */
+      public function returnNotTrue(): string {
+        return 'true';
+      }
+
+    });
+
+  }
+
+  /**
+   * Data provider for service validation test cases.
+   */
+  public static function provideServiceValidationCases(): array {
+    return [
+      'false result' => [
+        'method' => 'returnFalse',
+        'expected_violations' => 1,
+        'message' => 'Validation failed when returning FALSE.',
+        'expected_violation_message' => 'Calling \'returnFalse\' method with value \'1\' on \'test.service\' evaluated as invalid.',
+      ],
+      'true result' => [
+        'method' => 'returnTrue',
+        'expected_violations' => 0,
+        'message' => 'Validation succeeds when returning TRUE.',
+      ],
+      'truthy result' => [
+        'method' => 'returnNotTrue',
+        'expected_violations' => 1,
+        'message' => 'Validation fails when returning \'true\'.',
+        'expected_violation_message' => 'Calling \'returnNotTrue\' method with value \'1\' on \'test.service\' evaluated as invalid.',
+      ],
+    ];
+  }
+
+  /**
+   * @dataProvider provideServiceValidationCases
+   */
+  public function testValidationForService(string $method, int $expected_violations, string $message, ?string $expected_violation_message = NULL): void {
+    $definition = DataDefinition::create('integer')
+      ->addConstraint('ClassResolver', ['classOrService' => 'test.service', 'method' => $method]);
+    $typed_data = $this->typedData->create($definition, 1);
+    $violations = $typed_data->validate();
+    $this->assertEquals($expected_violations, $violations->count(), $message);
+    if ($expected_violation_message) {
+      $this->assertEquals($expected_violation_message, $violations->get(0)->getMessage());
+    }
+  }
+
+  /**
+   * Test missing method case.
+   *
+   * Tests that the ClassResolver constraint throws an exception when the
+   * method does not exist.
+   */
+  public function testNonExistingMethod(): void {
+    $definition = DataDefinition::create('integer')
+      ->addConstraint('ClassResolver', ['classOrService' => 'test.service', 'method' => 'missingMethod']);
+    $typed_data = $this->typedData->create($definition, 1);
+
+    $this->expectException(\InvalidArgumentException::class);
+    $this->expectExceptionMessage('The method "missingMethod" does not exist on the service "test.service".');
+    $typed_data->validate();
+  }
+
+  /**
+   * Test missing class case.
+   *
+   * Tests that the ClassResolver constraint throws an exception when the
+   * class does not exist.
+   */
+  public function testNonExistingClass(): void {
+    $definition = DataDefinition::create('integer')
+      ->addConstraint('ClassResolver', ['classOrService' => '\Drupal\NonExisting\Class', 'method' => 'boo']);
+    $typed_data = $this->typedData->create($definition, 1);
+
+    $this->expectExceptionMessage('Class "\Drupal\NonExisting\Class" does not exist.');
+    $typed_data->validate();
+  }
+
+}