Explorar o código

Implement setting for iframe referrer allowlist (#8672)

* Implement setting for iframe referrer allowlist

* Improve config label
Inverle hai 15 horas
pai
achega
cc64991c16

+ 1 - 1
README.fr.md

@@ -235,7 +235,7 @@ Voir le [dépôt dédié à ces extensions](https://github.com/FreshRSS/Extensio
 | Español (es) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fes+%2F%28TODO%7CDIRTY%29%24%2F) |
 | فارسی (fa) | ■■■■■■■■■・ 91% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffa+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Suomi (fi) | ■■■■■■■■■・ 93% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffi+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Français (fr) | ■■■■■■■■■■ 100% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffr+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Français (fr) | ■■■■■■■■■・ 99% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffr+%2F%28TODO%7CDIRTY%29%24%2F) |
 | עברית (he) | ■■■■・・・・・・ 41% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhe+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Magyar (hu) | ■■■■■■■■■・ 97% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhu+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Bahasa Indonesia (id) | ■■■■■■■■■・ 90% | [contribuer](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fid+%2F%28TODO%7CDIRTY%29%24%2F) |

+ 1 - 1
README.md

@@ -131,7 +131,7 @@ See the [repository dedicated to those extensions](https://github.com/FreshRSS/E
 | Español (es) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fes+%2F%28TODO%7CDIRTY%29%24%2F) |
 | فارسی (fa) | ■■■■■■■■■・ 91% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffa+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Suomi (fi) | ■■■■■■■■■・ 93% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffi+%2F%28TODO%7CDIRTY%29%24%2F) |
-| Français (fr) | ■■■■■■■■■■ 100% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffr+%2F%28TODO%7CDIRTY%29%24%2F) |
+| Français (fr) | ■■■■■■■■■・ 99% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Ffr+%2F%28TODO%7CDIRTY%29%24%2F) |
 | עברית (he) | ■■■■・・・・・・ 41% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhe+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Magyar (hu) | ■■■■■■■■■・ 97% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fhu+%2F%28TODO%7CDIRTY%29%24%2F) |
 | Bahasa Indonesia (id) | ■■■■■■■■■・ 90% | [contribute](https://github.com/search?q=repo%3AFreshRSS%2FFreshRSS+path%3Aapp%2Fi18n%2Fid+%2F%28TODO%7CDIRTY%29%24%2F) |

+ 1 - 0
app/Controllers/configureController.php

@@ -682,6 +682,7 @@ class FreshRSS_configure_Controller extends FreshRSS_ActionController {
 	public function privacyAction(): void {
 		if (Minz_Request::isPost()) {
 			FreshRSS_Context::userConf()->retrieve_extension_list = Minz_Request::paramBoolean('retrieve_extension_list');
+			FreshRSS_Context::userConf()->send_referrer_allowlist = Minz_Request::paramTextToArray('send_referrer_allowlist');
 			FreshRSS_Context::userConf()->save();
 			invalidateHttpCache();
 

+ 1 - 0
app/Models/UserConfiguration.php

@@ -83,6 +83,7 @@ declare(strict_types=1);
  * @property array<string,bool|int|string> $volatile
  * @property array<string,array<string,mixed>> $extensions
  * @property bool $retrieve_extension_list
+ * @property array<string> $send_referrer_allowlist
  */
 final class FreshRSS_UserConfiguration extends Minz_Configuration {
 	use FreshRSS_FilterActionsTrait;

+ 1 - 0
app/i18n/cs/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// TODO
 		'retrieve_extension_list' => 'Retrieve extension list',	// TODO
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Správa profilu',

+ 1 - 0
app/i18n/de/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privatsphäre',
 		'retrieve_extension_list' => 'Erweiterungsliste abrufen',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Profil-Verwaltung',

+ 1 - 0
app/i18n/el/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// TODO
 		'retrieve_extension_list' => 'Retrieve extension list',	// TODO
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Profile management',	// TODO

+ 1 - 0
app/i18n/en-US/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// IGNORE
 		'retrieve_extension_list' => 'Retrieve extension list',	// IGNORE
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// IGNORE
 	),
 	'profile' => array(
 		'_' => 'Profile management',	// IGNORE

+ 1 - 0
app/i18n/en/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',
 		'retrieve_extension_list' => 'Retrieve extension list',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',
 	),
 	'profile' => array(
 		'_' => 'Profile management',

+ 1 - 0
app/i18n/es/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacidad',
 		'retrieve_extension_list' => 'Obtener lista de extensiones',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Administración de perfiles',

+ 1 - 0
app/i18n/fa/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'حریم خصوصی',
 		'retrieve_extension_list' => 'بازیابی لیست افزونه‌ها',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => ' مدیریت پروفایل',

+ 1 - 0
app/i18n/fi/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Tietosuoja',
 		'retrieve_extension_list' => 'Nouda laajennusluettelo',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Profiilien hallinta',

+ 1 - 0
app/i18n/fr/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Vie privée',
 		'retrieve_extension_list' => 'Récupération de la liste des extensions',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Gestion du profil',

+ 1 - 0
app/i18n/he/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// TODO
 		'retrieve_extension_list' => 'Retrieve extension list',	// TODO
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Profile management',	// TODO

+ 1 - 0
app/i18n/hu/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Adatvédelem',
 		'retrieve_extension_list' => 'Kiterjesztés lista beszerzése',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Profil kezelés',

+ 1 - 0
app/i18n/id/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privasi',
 		'retrieve_extension_list' => 'Ambil daftar ekstensi',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Pengelolaan Profil',

+ 1 - 0
app/i18n/it/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// IGNORE
 		'retrieve_extension_list' => 'Recupero dell’elenco delle estensioni',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Gestione profili',

+ 1 - 0
app/i18n/ja/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'プライバシー',
 		'retrieve_extension_list' => '拡張機能リストを取得する',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'プロフィール',

+ 1 - 0
app/i18n/ko/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// TODO
 		'retrieve_extension_list' => 'Retrieve extension list',	// TODO
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => '프로필 관리',

+ 1 - 0
app/i18n/lv/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// TODO
 		'retrieve_extension_list' => 'Retrieve extension list',	// TODO
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Profila pārvalde',

+ 1 - 0
app/i18n/nl/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// IGNORE
 		'retrieve_extension_list' => 'Extensielijst ophalen',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Profielbeheer',

+ 1 - 0
app/i18n/oc/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// TODO
 		'retrieve_extension_list' => 'Retrieve extension list',	// TODO
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Gestion del perfil',

+ 1 - 0
app/i18n/pl/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Prywatność',
 		'retrieve_extension_list' => 'Pobieraj listę rozszerzeń',
+		'send_referrer_allowlist' => 'Strony, które mogą zobaczyć twój adres serwera (%s)',
 	),
 	'profile' => array(
 		'_' => 'Zarządzanie profilem',

+ 1 - 0
app/i18n/pt-BR/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacidade',
 		'retrieve_extension_list' => 'Recuperar lista de extensões',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Gerenciamento de perfil',

+ 1 - 0
app/i18n/pt-PT/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// TODO
 		'retrieve_extension_list' => 'Retrieve extension list',	// TODO
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Gestão de perfil',

+ 1 - 0
app/i18n/ru/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Конфиденциальность',
 		'retrieve_extension_list' => 'Получить список расширений',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Настройки профиля',

+ 1 - 0
app/i18n/sk/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// TODO
 		'retrieve_extension_list' => 'Retrieve extension list',	// TODO
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Správca profilu',

+ 1 - 0
app/i18n/tr/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Gizlilik',
 		'retrieve_extension_list' => 'Eklenti listesini al',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Profil yönetimi',

+ 1 - 0
app/i18n/uk/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Приватність',
 		'retrieve_extension_list' => 'Завантажувати список розширень',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => 'Керування профілем',

+ 1 - 0
app/i18n/zh-CN/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => '隐私',
 		'retrieve_extension_list' => '获取扩展列表',
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => '账户管理',

+ 1 - 0
app/i18n/zh-TW/conf.php

@@ -122,6 +122,7 @@ return array(
 	'privacy' => array(
 		'_' => 'Privacy',	// TODO
 		'retrieve_extension_list' => 'Retrieve extension list',	// TODO
+		'send_referrer_allowlist' => 'Sites allowed to see your server address (%s)',	// TODO
 	),
 	'profile' => array(
 		'_' => '個人資料管理',

+ 14 - 0
app/views/configure/privacy.phtml

@@ -1,6 +1,13 @@
 <?php
 	/** @var FreshRSS_View $this */
 	$this->partial('aside_configure');
+
+	$host = parse_url(FreshRSS_Context::systemConf()->base_url);
+	if ($host !== false) {
+		$host = $host['host'] ?? '';
+	} else {
+		$host = '';
+	}
 ?>
 
 <main class="post">
@@ -17,6 +24,13 @@
 			</div>
 		</div>
 
+		<div class="form-group">
+			<label class="group-name" for="send_referrer_allowlist"><?= _t('conf.privacy.send_referrer_allowlist', $host) ?></label>
+			<div class="group-controls">
+				<textarea id="send_referrer_allowlist" name="send_referrer_allowlist"><?= implode("\n", FreshRSS_Context::userConf()->send_referrer_allowlist) ?></textarea>
+			</div>
+		</div>
+
 		<div class="form-group form-actions">
 			<div class="group-controls">
 				<button type="submit" class="btn btn-important"><?= _t('gen.action.submit') ?></button>

+ 3 - 1
app/views/helpers/javascript_vars.phtml

@@ -4,6 +4,7 @@ declare(strict_types=1);
 $mark = FreshRSS_Context::userConf()->mark_when;
 $s = FreshRSS_Context::userConf()->shortcuts;
 $extData = Minz_ExtensionManager::callHook(Minz_HookType::JsVars, []);
+$canView = FreshRSS_Auth::hasAccess() || FreshRSS_Context::systemConf()->allow_anonymous;
 echo json_encode([
 	'context' => [
 		'anonymous' => !FreshRSS_Auth::hasAccess(),
@@ -21,7 +22,7 @@ echo json_encode([
 		'auto_load_more' => FreshRSS_Context::userConf()->auto_load_more && FreshRSS_Context::$sort !== 'rand',
 		'auto_actualize_feeds' => Minz_Session::paramBoolean('actualize_feeds'),
 		'nb_parallel_refresh' => max(1, FreshRSS_Context::systemConf()->nb_parallel_refresh),
-		'does_lazyload' => !!FreshRSS_Context::userConf()->lazyload ,
+		'does_lazyload' => FreshRSS_Context::userConf()->lazyload && !Minz_Request::is('index', 'reader'), // TODO: currently no lazy-loading is being done in reader view
 		'sides_close_article' => !!FreshRSS_Context::userConf()->sides_close_article,
 		'sidebar_hidden_by_default' => !!FreshRSS_Context::userConf()->sidebar_hidden_by_default,
 		'sticky_post' => !!FreshRSS_Context::isStickyPostEnabled(),
@@ -39,6 +40,7 @@ echo json_encode([
 			'extra.js' => @filemtime(PUBLIC_PATH . '/scripts/extra.js'),
 			'feed.js' => @filemtime(PUBLIC_PATH . '/scripts/feed.js'),
 		],
+		'send_referrer_allowlist' => $canView ? FreshRSS_Context::userConf()->send_referrer_allowlist : [],
 		'max_favicon_upload_size' => FreshRSS_Context::systemConf()->limits['max_favicon_upload_size'],
 		'version' => FRESHRSS_VERSION,
 	],

+ 2 - 0
config-user.default.php

@@ -138,7 +138,9 @@ return array (
 	'sidebar_hidden_by_default' => false,
 	# List of enabled FreshRSS extensions.
 	'extensions_enabled' => [],
+	# Privacy settings
 	'retrieve_extension_list' => true,
+	'send_referrer_allowlist' => [],
 	# Extensions configurations
 	'extensions' => [],
 );

+ 2 - 1
p/scripts/global_view.js

@@ -1,6 +1,6 @@
 // @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0
 'use strict';
-/* globals context, init_load_more, init_posts, init_stream */
+/* globals context, init_load_more, init_posts, init_stream, enforce_referrer_allowlist */
 
 let panel_loading = false;
 
@@ -28,6 +28,7 @@ function load_panel(link) {
 			el.remove();
 		});
 
+		enforce_referrer_allowlist(panel);
 		init_load_more(panel);
 		init_posts();
 

+ 20 - 0
p/scripts/main.js

@@ -2154,6 +2154,7 @@ function load_more_posts() {
 		let lastTransition = transitions.length > 0 ? transitions[transitions.length - 1] : null;
 
 		const streamAdopted = document.adoptNode(html.getElementById('stream'));
+		enforce_referrer_allowlist(streamAdopted);
 		streamAdopted.querySelectorAll('.flux, .transition').forEach(function (div) {
 			if (lastTransition !== null && div.classList.contains('transition') && div.textContent === lastTransition.textContent) {
 				lastTransition = null;
@@ -2295,6 +2296,24 @@ function removeFirstLoadSpinner() {
 	}
 }
 
+function enforce_referrer_allowlist(stream) {
+	for (const iframe of stream.querySelectorAll('div.content iframe[src], div.content iframe[data-original]')) {
+		let hostname;
+		try {
+			hostname = new URL(context.does_lazyload ? iframe.getAttribute('data-original') : iframe.src).hostname;
+		} catch (_) {
+			continue;
+		}
+		if (context.send_referrer_allowlist.includes(hostname) && !iframe.hasAttribute('referrerpolicy')) {
+			iframe.setAttribute('referrerpolicy', 'strict-origin-when-cross-origin');
+			if (!context.does_lazyload) {
+				// iframe must be reloaded to apply the `referrerpolicy` change
+				iframe.src = iframe.src; // eslint-disable-line no-self-assign
+			}
+		}
+	}
+}
+
 function init_normal() {
 	const stream = document.getElementById('stream');
 	if (!stream) {
@@ -2306,6 +2325,7 @@ function init_normal() {
 	}
 	init_column_categories();
 	init_stream(stream);
+	enforce_referrer_allowlist(stream);
 	init_actualize();
 	faviconNbUnread();