 core/lib/Drupal/Core/Template/TwigExtension.php    | 23 ++++++++++++++++++++++
 .../modules/update/src/Tests/UpdateContribTest.php |  3 +++
 core/modules/update/src/Tests/UpdateCoreTest.php   | 11 +++++++++--
 core/modules/update/src/Tests/UpdateTestBase.php   |  3 ++-
 core/modules/update/src/Tests/UpdateUploadTest.php |  3 +++
 core/modules/update/update.install                 | 17 ++++++++++++++++
 core/modules/update/update.routing.yml             |  1 +
 7 files changed, 58 insertions(+), 3 deletions(-)

diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php
index e204ba9..b311347 100644
--- a/core/lib/Drupal/Core/Template/TwigExtension.php
+++ b/core/lib/Drupal/Core/Template/TwigExtension.php
@@ -4,7 +4,9 @@
 
 use Drupal\Component\Utility\Html;
 use Drupal\Component\Render\MarkupInterface;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Datetime\DateFormatterInterface;
+use Drupal\Core\Render\AttachmentsInterface;
 use Drupal\Core\Render\RenderableInterface;
 use Drupal\Core\Render\RendererInterface;
 use Drupal\Core\Routing\UrlGeneratorInterface;
@@ -410,6 +412,8 @@ public function escapeFilter(\Twig_Environment $env, $arg, $strategy = 'html', $
       return NULL;
     }
 
+    $this->bubbleArgMetadata($arg);
+
     // Keep Twig_Markup objects intact to support autoescaping.
     if ($autoescape && ($arg instanceof \Twig_Markup || $arg instanceof MarkupInterface)) {
       return $arg;
@@ -462,6 +466,24 @@ public function escapeFilter(\Twig_Environment $env, $arg, $strategy = 'html', $
     return $this->renderer->render($arg);
   }
 
+  protected function bubbleArgMetadata($arg) {
+    if ($arg instanceof CacheableDependencyInterface || $arg instanceof AttachmentsInterface) {
+      $arg_bubbleable = [];
+      if ($arg instanceof CacheableDependencyInterface) {
+        $arg_bubbleable['#cache']['contexts'] = $arg->getCacheContexts();
+        $arg_bubbleable['#cache']['tags'] = $arg->getCacheTags();
+        $arg_bubbleable['#cache']['max-age'] = $arg->getCacheMaxAge();
+      }
+      if ($arg instanceof AttachmentsInterface) {
+        $arg_bubbleable['#attached'] = $arg->getAttachments();
+      }
+      if ($arg_bubbleable) {
+        // @todo Inject the Renderer in https://www.drupal.org/node/2529438.
+        drupal_render($arg_bubbleable);
+      }
+    }
+  }
+
   /**
    * Wrapper around render() for twig printed output.
    *
@@ -504,6 +526,7 @@ public function renderVar($arg) {
     }
 
     if (is_object($arg)) {
+      $this->bubbleArgMetadata($arg);
       if ($arg instanceof RenderableInterface) {
         $arg = $arg->toRenderable();
       }
diff --git a/core/modules/update/src/Tests/UpdateContribTest.php b/core/modules/update/src/Tests/UpdateContribTest.php
index 923b5d5..88cf378 100644
--- a/core/modules/update/src/Tests/UpdateContribTest.php
+++ b/core/modules/update/src/Tests/UpdateContribTest.php
@@ -345,6 +345,9 @@ function testUpdateBrokenFetchURL() {
     );
     $this->config('update_test.settings')->set('system_info', $system_info)->save();
 
+    // Ensure that the update information is correct before testing.
+    $this->drupalGet('admin/reports/updates');
+
     $xml_mapping = array(
       'drupal' => '0.0',
       'aaa_update_test' => '1_0',
diff --git a/core/modules/update/src/Tests/UpdateCoreTest.php b/core/modules/update/src/Tests/UpdateCoreTest.php
index 799d5a9..baecde8 100644
--- a/core/modules/update/src/Tests/UpdateCoreTest.php
+++ b/core/modules/update/src/Tests/UpdateCoreTest.php
@@ -65,11 +65,17 @@ function testNoUpdatesAvailable() {
    */
   function testNormalUpdateAvailable() {
     $this->setSystemInfo('8.0.0');
+
+    // Ensure that the update check requires a token.
+    $this->drupalGet('admin/reports/updates/check');
+    $this->assertResponse(403, 'Accessing admin/reports/updates/check without a CSRF token results in access denied.');
+
     foreach (array(0, 1) as $minor_version) {
       foreach (array('-alpha1', '-beta1', '') as $extra_version) {
         $this->refreshUpdateStatus(array('drupal' => "$minor_version.1" . $extra_version));
         $this->standardTests();
-        $this->drupalGet('admin/reports/updates/check');
+        $this->drupalGet('admin/reports/updates');
+        $this->clickLink(t('Check manually'));
         $this->assertNoText(t('Security update required!'));
         $this->assertRaw(\Drupal::l("8.$minor_version.1" . $extra_version, Url::fromUri("http://example.com/drupal-8-$minor_version-1$extra_version-release")), 'Link to release appears.');
         $this->assertRaw(\Drupal::l(t('Download'), Url::fromUri("http://example.com/drupal-8-$minor_version-1$extra_version.tar.gz")), 'Link to download appears.');
@@ -131,7 +137,8 @@ function testMajorUpdateAvailable() {
           $this->setSystemInfo("8.$minor_version.$patch_version" . $extra_version);
           $this->refreshUpdateStatus(array('drupal' => '9'));
           $this->standardTests();
-          $this->drupalGet('admin/reports/updates/check');
+          $this->drupalGet('admin/reports/updates');
+          $this->clickLink(t('Check manually'));
           $this->assertNoText(t('Security update required!'));
           $this->assertRaw(\Drupal::l('9.0.0', Url::fromUri("http://example.com/drupal-9-0-0-release")), 'Link to release appears.');
           $this->assertRaw(\Drupal::l(t('Download'), Url::fromUri("http://example.com/drupal-9-0-0.tar.gz")), 'Link to download appears.');
diff --git a/core/modules/update/src/Tests/UpdateTestBase.php b/core/modules/update/src/Tests/UpdateTestBase.php
index 24eb2aa..7db5b23 100644
--- a/core/modules/update/src/Tests/UpdateTestBase.php
+++ b/core/modules/update/src/Tests/UpdateTestBase.php
@@ -67,7 +67,8 @@ protected function refreshUpdateStatus($xml_map, $url = 'update-test') {
     // Save the map for UpdateTestController::updateTest() to use.
     $this->config('update_test.settings')->set('xml_map', $xml_map)->save();
     // Manually check the update status.
-    $this->drupalGet('admin/reports/updates/check');
+    $this->drupalGet('admin/reports/updates');
+    $this->clickLink(t('Check manually'));
   }
 
   /**
diff --git a/core/modules/update/src/Tests/UpdateUploadTest.php b/core/modules/update/src/Tests/UpdateUploadTest.php
index 1605f2e..39b6944 100644
--- a/core/modules/update/src/Tests/UpdateUploadTest.php
+++ b/core/modules/update/src/Tests/UpdateUploadTest.php
@@ -30,6 +30,9 @@ protected function setUp() {
    * Tests upload, extraction, and update of a module.
    */
   public function testUploadModule() {
+    // Ensure that the update information is correct before testing.
+    update_get_available(TRUE);
+
     // Images are not valid archives, so get one and try to install it. We
     // need an extra variable to store the result of drupalGetTestFiles()
     // since reset() takes an argument by reference and passing in a constant
diff --git a/core/modules/update/update.install b/core/modules/update/update.install
index b2223f1..e810129 100644
--- a/core/modules/update/update.install
+++ b/core/modules/update/update.install
@@ -148,3 +148,20 @@ function _update_requirement_check($project, $type) {
   $requirement['value'] = \Drupal::l($requirement_label, new Url(_update_manager_access() ? 'update.report_update' : 'update.status'));
   return $requirement;
 }
+
+/**
+ * @addtogroup updates-8.1.x
+ * @{
+ */
+
+/**
+ * Rebuild the router to ensure admin/reports/updates/check has CSRF protection.
+ */
+function update_update_8001() {
+  // Empty update forces a call to drupal_flush_all_caches() which rebuilds the
+  // router.
+}
+
+/**
+ * @} End of "addtogroup updates-8.1.x".
+ */
diff --git a/core/modules/update/update.routing.yml b/core/modules/update/update.routing.yml
index 0049396..d5b79ea 100644
--- a/core/modules/update/update.routing.yml
+++ b/core/modules/update/update.routing.yml
@@ -21,6 +21,7 @@ update.manual_status:
     _controller: '\Drupal\update\Controller\UpdateController::updateStatusManually'
   requirements:
     _permission: 'administer site configuration'
+    _csrf_token: 'TRUE'
 
 update.report_install:
   path: '/admin/reports/updates/install'
