From c0e623bbbea03eedc22375bd7a44a7eb288708d9 Mon Sep 17 00:00:00 2001 From: Carlos Knippschild Date: Thu, 11 Oct 2018 01:50:47 +0000 Subject: [PATCH] Securely handles textual web content presented on the dino page Update the dino page code to add all downloaded textual content using the `innerText` property of elements to ensure they are handled as plain text. Also adds some comments in related areas explaining the steps taken for safely handling downloaded texts and images. TBR=palmer@chromium.org Bug: 852872 Change-Id: I8ce2827a657350100e5965cb38ceb85a8979c10f Reviewed-on: https://chromium-review.googlesource.com/c/1270002 Reviewed-by: Carlos Knippschild Reviewed-by: Edward Jung Reviewed-by: Dan H Commit-Queue: Carlos Knippschild Cr-Commit-Position: refs/heads/master@{#598628} --- .../offline_pages/thumbnail_decoder_impl.h | 4 ++ chrome/common/available_offline_content.mojom | 3 ++ components/neterror/resources/neterror.css | 4 ++ components/neterror/resources/neterror.js | 39 +++++++++++++------ 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/chrome/browser/offline_pages/thumbnail_decoder_impl.h b/chrome/browser/offline_pages/thumbnail_decoder_impl.h index 577fde8e7ffe0f..69c9092d30f7c9 100644 --- a/chrome/browser/offline_pages/thumbnail_decoder_impl.h +++ b/chrome/browser/offline_pages/thumbnail_decoder_impl.h @@ -12,6 +12,10 @@ namespace offline_pages { +// Decodes the downloaded JPEG image, crops it and re-encode it as a PNG +// file to be used as the thumbnail of an offlined suggested article. +// Note: the local decoding in a separate process and local re-encoding as a PNG +// are important security measures as these downloaded images are web content. class ThumbnailDecoderImpl : public ThumbnailDecoder { public: explicit ThumbnailDecoderImpl( diff --git a/chrome/common/available_offline_content.mojom b/chrome/common/available_offline_content.mojom index f6ac36e6385576..b2a97e1c3f4b5d 100644 --- a/chrome/common/available_offline_content.mojom +++ b/chrome/common/available_offline_content.mojom @@ -23,6 +23,9 @@ enum AvailableContentType { }; // A single piece of content that is available offline. +// Note: Some of the content pieces stored in this struct are web content and +// must be properly handled for securing their presentation on the net error +// page. struct AvailableOfflineContent { // Together id and name_space define a unique ID for this item. string id; diff --git a/components/neterror/resources/neterror.css b/components/neterror/resources/neterror.css index 9655508e804d81..b0cfddabad2eae 100644 --- a/components/neterror/resources/neterror.css +++ b/components/neterror/resources/neterror.css @@ -507,6 +507,10 @@ div.offline-content-suggestion { word-break: break-all; } +.no-attribution .offline-content-suggestion-attribution { + display: none; +} + .offline-content-suggestion-freshness:before { content: '-'; display: inline-block; diff --git a/components/neterror/resources/neterror.js b/components/neterror/resources/neterror.js index 86b1b344515117..bbbe8e77cb3fbd 100644 --- a/components/neterror/resources/neterror.js +++ b/components/neterror/resources/neterror.js @@ -174,11 +174,14 @@ function launchDownloadsPage() { // Populates a summary of suggested offline content. function offlineContentSummaryAvailable(summary) { + // Note: See AvailableContentSummaryToValue in + // available_offline_content_helper.cc for the data contained in |summary|. if (!summary || summary.total_items == 0 || !loadTimeData.valueExists('offlineContentSummary')) { return; } - + // TODO(https://crbug.com/852872): Customize presented icons based on the + // types of available offline content. document.getElementById('offline-content-summary').hidden = false; } @@ -190,14 +193,14 @@ function getIconForSuggestedItem(item) { return 'image-video'; case 2: // kAudio return 'image-music-note'; - case 0: // kPrefetchedUnopenedPage + case 0: // kPrefetchedPage case 3: // kOtherPage return 'image-earth'; } return 'image-file'; } -function getSuggestedContentDiv(item) { +function getSuggestedContentDiv(item, index) { // Note: See AvailableContentToValue in available_offline_content_helper.cc // for the data contained in an |item|. var visual = ''; @@ -221,12 +224,12 @@ function getSuggestedContentDiv(item) {
-
- ${item.title} +
-
- ${item.attribution} +
${item.date_modified} @@ -242,20 +245,34 @@ function getSuggestedContentDiv(item) { } // Populates a list of suggested offline content. +// Note: For security reasons all content downloaded from the web is considered +// unsafe and must be securely handled to be presented on the dino page. The +// image content is already safely re-encoded after being downloaded but the +// textual content, like title and attribution, must be properly handled here. function offlineContentAvailable(suggestions) { if (!suggestions || !loadTimeData.valueExists('offlineContentList')) return; var suggestionsHTML = []; - for (var item of suggestions) - suggestionsHTML.push(getSuggestedContentDiv(item)); + for (var index = 0; index < suggestions.length; index++) + suggestionsHTML.push(getSuggestedContentDiv(suggestions[index], index)); + document.getElementById('offline-content-suggestions').innerHTML = suggestionsHTML.join('\n'); - var contentListElement = document.getElementById('offline-content-list') - contentListElement.hidden = false; + // Sets textual web content using |textContent| to make sure it's handled as + // plain text. + for (var index = 0; index < suggestions.length; index++) { + document.getElementById(`offline-content-suggestion-title-${index}`) + .textContent = suggestions[index].title; + document.getElementById(`offline-content-suggestion-attribution-${index}`) + .textContent = suggestions[index].attribution; + } + + var contentListElement = document.getElementById('offline-content-list'); if (document.dir == 'rtl') contentListElement.classList.add('is-rtl'); + contentListElement.hidden = false; } function onDocumentLoad() {