Browse Source

Fix HTML encodings in e.g. cURL options (#6821)

* Fix HTML encodings in e.g. cURL options

* Trim headers whitespace
Alexandre Alapetite 1 year ago
parent
commit
1c09408c64

+ 8 - 7
app/Controllers/feedController.php

@@ -170,15 +170,15 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 				$http_auth = $user . ':' . $pass;
 			}
 
-			$cookie = Minz_Request::paramString('curl_params_cookie');
+			$cookie = Minz_Request::paramString('curl_params_cookie', plaintext: true);
 			$cookie_file = Minz_Request::paramBoolean('curl_params_cookiefile');
 			$max_redirs = Minz_Request::paramInt('curl_params_redirects');
-			$useragent = Minz_Request::paramString('curl_params_useragent');
-			$proxy_address = Minz_Request::paramString('curl_params');
-			$proxy_type = Minz_Request::paramString('proxy_type');
-			$request_method = Minz_Request::paramString('curl_method');
-			$request_fields = Minz_Request::paramString('curl_fields', true);
-			$headers = Minz_Request::paramTextToArray('http_headers');
+			$useragent = Minz_Request::paramString('curl_params_useragent', plaintext: true);
+			$proxy_address = Minz_Request::paramString('curl_params', plaintext: true);
+			$proxy_type = Minz_Request::paramString('proxy_type', plaintext: true);
+			$request_method = Minz_Request::paramString('curl_method', plaintext: true);
+			$request_fields = Minz_Request::paramString('curl_fields', plaintext: true);
+			$headers = Minz_Request::paramTextToArray('http_headers', plaintext: true);
 
 			$opts = [];
 			if ($proxy_type !== '') {
@@ -210,6 +210,7 @@ class FreshRSS_feed_Controller extends FreshRSS_ActionController {
 				}
 			}
 			if(!empty($headers)) {
+				$headers = array_filter(array_map('trim', $headers));
 				$opts[CURLOPT_HTTPHEADER] = array_merge($headers, $opts[CURLOPT_HTTPHEADER] ?? []);
 			}
 

+ 8 - 7
app/Controllers/subscriptionController.php

@@ -138,15 +138,15 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 			}
 			$feed->_attribute('read_when_same_title_in_feed', $read_when_same_title_in_feed);
 
-			$cookie = Minz_Request::paramString('curl_params_cookie');
+			$cookie = Minz_Request::paramString('curl_params_cookie', plaintext: true);
 			$cookie_file = Minz_Request::paramBoolean('curl_params_cookiefile');
 			$max_redirs = Minz_Request::paramInt('curl_params_redirects');
-			$useragent = Minz_Request::paramString('curl_params_useragent');
-			$proxy_address = Minz_Request::paramString('curl_params');
-			$proxy_type = Minz_Request::paramString('proxy_type');
-			$request_method = Minz_Request::paramString('curl_method');
-			$request_fields = Minz_Request::paramString('curl_fields', true);
-			$headers = Minz_Request::paramTextToArray('http_headers');
+			$useragent = Minz_Request::paramString('curl_params_useragent', plaintext: true);
+			$proxy_address = Minz_Request::paramString('curl_params', plaintext: true);
+			$proxy_type = Minz_Request::paramString('proxy_type', plaintext: true);
+			$request_method = Minz_Request::paramString('curl_method', plaintext: true);
+			$request_fields = Minz_Request::paramString('curl_fields', plaintext: true);
+			$headers = Minz_Request::paramTextToArray('http_headers', plaintext: true);
 			$opts = [];
 			if ($proxy_type !== '') {
 				$opts[CURLOPT_PROXY] = $proxy_address;
@@ -179,6 +179,7 @@ class FreshRSS_subscription_Controller extends FreshRSS_ActionController {
 			}
 
 			if(!empty($headers)) {
+				$headers = array_filter(array_map('trim', $headers));
 				$opts[CURLOPT_HTTPHEADER] = array_merge($headers, $opts[CURLOPT_HTTPHEADER] ?? []);
 			}
 

+ 1 - 1
app/layout/aside_configure.phtml

@@ -6,7 +6,7 @@
 
 	<ul>
 		<li class="item nav-section">
-			<div class="item nav-header"><?= _t('gen.menu.account') ?>: <?= htmlspecialchars(Minz_User::name() ?? '', ENT_NOQUOTES, 'UTF-8')?></div>
+			<div class="item nav-header"><?= _t('gen.menu.account') ?>: <?= htmlspecialchars(Minz_User::name() ?? '', ENT_NOQUOTES, 'UTF-8') ?></div>
 			<ul>
 				<li class="item<?= Minz_Request::controllerName() === 'user' && Minz_Request::actionName() === 'profile' ? ' active' : '' ?>">
 					<a href="<?= _url('user', 'profile') ?>"><?= _t('gen.menu.user_profile') ?></a>

+ 9 - 6
app/views/helpers/feed/update.phtml

@@ -646,7 +646,7 @@
 				<label class="group-name" for="curl_params_cookie"><?= _t('sub.feed.css_cookie') ?></label>
 				<div class="group-controls">
 					<input type="text" name="curl_params_cookie" id="curl_params_cookie" class="w100" value="<?=
-						!empty($curlParams[CURLOPT_COOKIE]) ? $curlParams[CURLOPT_COOKIE] : ''
+						htmlspecialchars((string)($curlParams[CURLOPT_COOKIE] ?? ''), ENT_COMPAT, 'UTF-8')
 					?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" />
 					<p class="help"><?= _i('help') ?> <?= _t('sub.feed.css_cookie_help') ?></p>
 					<label for="curl_params_cookiefile">
@@ -684,7 +684,7 @@
 				<label class="group-name" for="curl_params_useragent"><?= _t('sub.feed.useragent') ?></label>
 				<div class="group-controls">
 					<input type="text" name="curl_params_useragent" id="curl_params_useragent" class="w100" value="<?=
-						!empty($curlParams[CURLOPT_USERAGENT]) ? $curlParams[CURLOPT_USERAGENT] : ''
+						htmlspecialchars((string)($curlParams[CURLOPT_USERAGENT] ?? ''), ENT_COMPAT, 'UTF-8')
 					?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" />
 					<p class="help"><?= _i('help') ?> <?= _t('sub.feed.useragent_help') ?></p>
 				</div>
@@ -701,7 +701,7 @@
 					?>
 					</select>
 					<input type="text" name="curl_params" id="curl_params" value="<?=
-						!empty($curlParams[CURLOPT_PROXY]) ? $curlParams[CURLOPT_PROXY] : ''
+						htmlspecialchars((string)($curlParams[CURLOPT_PROXY] ?? ''), ENT_COMPAT, 'UTF-8')
 					?>" placeholder="<?= _t('gen.short.blank_to_disable') ?>" />
 					<p class="help"><?= _i('help') ?> <?= _t('sub.feed.proxy_help') ?></p>
 				</div>
@@ -722,8 +722,7 @@
 					</select>
 					<div class="stick">
 						<input type="text" name="curl_fields" id="curl_fields" value="<?=
-							$this->feed->attributeArray('curl_params') !== null && !empty($this->feed->attributeArray('curl_params')[CURLOPT_POSTFIELDS]) ?
-								htmlentities($this->feed->attributeArray('curl_params')[CURLOPT_POSTFIELDS], ENT_COMPAT) : ''
+							htmlspecialchars($this->feed->attributeArray('curl_params')[CURLOPT_POSTFIELDS] ?? '', ENT_COMPAT, 'UTF-8')
 						?>" placeholder="<?= _t('sub.feed.method_postparams') ?>" />
 					</div>
 					<p class="help"><?= _i('help') ?> <?= _t('sub.feed.method_help') ?></p>
@@ -751,7 +750,11 @@
 			<div class="form-group">
 				<label class="group-name" for="http_headers"><?= _t('sub.feed.http_headers') ?></label>
 				<div class="group-controls">
-					<textarea class="valid-json" id="http_headers" name="http_headers" rows="3" cols="64" spellcheck="false"><?= !empty($this->feed->attributeArray('curl_params')) ? implode(PHP_EOL, $this->feed->attributeArray('curl_params')[CURLOPT_HTTPHEADER]) : '' ?></textarea>
+					<textarea class="valid-json" id="http_headers" name="http_headers" rows="3" cols="64" spellcheck="false"><?php
+						foreach ($this->feed->attributeArray('curl_params')[CURLOPT_HTTPHEADER] ?? [] as $header) {
+							echo htmlspecialchars($header, ENT_NOQUOTES, 'UTF-8'), PHP_EOL;
+						}
+					?></textarea>
 					<p class="help"><?= _i('help') ?> <?= _t('sub.feed.http_headers_help') ?></p>
 				</div>
 			</div>

+ 5 - 4
lib/Minz/Request.php

@@ -142,14 +142,15 @@ class Minz_Request {
 	 * It will return an array where each cell contains one line of a text. The new line
 	 * character is used to break the text into lines. This method is well suited to use
 	 * to split textarea content.
-	 * @param array<string> $default
+	 * @param bool $plaintext `true` to return special characters without any escaping (unsafe), `false` (default) to XML-encode them
 	 * @return array<string>
 	 */
-	public static function paramTextToArray(string $key, array $default = []): array {
+	public static function paramTextToArray(string $key, bool $plaintext = false): array {
 		if (isset(self::$params[$key]) && is_string(self::$params[$key])) {
-			return preg_split('/\R/u', self::$params[$key]) ?: [];
+			$result = preg_split('/\R/u', self::$params[$key]) ?: [];
+			return $plaintext ? $result : Minz_Helper::htmlspecialchars_utf8($result);
 		}
-		return $default;
+		return [];
 	}
 
 	public static function defaultControllerName(): string {

+ 2 - 2
lib/core-extensions/UserCSS/extension.php

@@ -22,13 +22,13 @@ final class UserCSSExtension extends Minz_Extension {
 		$this->registerTranslates();
 
 		if (Minz_Request::isPost()) {
-			$css_rules = html_entity_decode(Minz_Request::paramString('css-rules'));
+			$css_rules = Minz_Request::paramString('css-rules', plaintext: true);
 			$this->saveFile(self::FILENAME, $css_rules);
 		}
 
 		$this->css_rules = '';
 		if ($this->hasFile(self::FILENAME)) {
-			$this->css_rules = htmlentities($this->getFile(self::FILENAME) ?? '');
+			$this->css_rules = htmlspecialchars($this->getFile(self::FILENAME) ?? '', ENT_NOQUOTES, 'UTF-8');
 		}
 	}
 }

+ 2 - 2
lib/core-extensions/UserJS/extension.php

@@ -22,13 +22,13 @@ final class UserJSExtension extends Minz_Extension {
 		$this->registerTranslates();
 
 		if (Minz_Request::isPost()) {
-			$js_rules = html_entity_decode(Minz_Request::paramString('js-rules'));
+			$js_rules = Minz_Request::paramString('js-rules', plaintext: true);
 			$this->saveFile(self::FILENAME, $js_rules);
 		}
 
 		$this->js_rules = '';
 		if ($this->hasFile(self::FILENAME)) {
-			$this->js_rules = htmlentities($this->getFile(self::FILENAME) ?? '');
+			$this->js_rules = htmlspecialchars($this->getFile(self::FILENAME) ?? '', ENT_NOQUOTES, 'UTF-8');
 		}
 	}
 }