Răsfoiți Sursa

fix: escape labels during JSON import and render tag dropdown as text (#9504)

- Normalize label names parsed from Google Reader JSON categories during import, using the same escaping helper as the other imported string fields.
- Decode stored label names before returning them to the dynamic tag dropdown.
- Build dynamic tag dropdown entries with DOM text nodes instead of HTML string concatenation.
Pythame 2 zile în urmă
părinte
comite
bc962e9aad

+ 1 - 1
app/Controllers/importExportController.php

@@ -544,7 +544,7 @@ class FreshRSS_importExport_Controller extends FreshRSS_ActionController {
 					} elseif (preg_match('%^user/[A-Za-z0-9_-]+/state/com.google/unread$%', $tag)) {
 						$is_read = false;
 					} elseif (preg_match('%^user/[A-Za-z0-9_-]+/label/\s*(?P<tag>.+?)\s*$%', $tag, $matches)) {
-						$labels[] = $matches['tag'];
+						$labels[] = Minz_Helper::htmlspecialchars_utf8($matches['tag']);
 					}
 					unset($tags[$i]);
 				}

+ 5 - 1
app/Controllers/tagController.php

@@ -153,7 +153,11 @@ class FreshRSS_tag_Controller extends FreshRSS_ActionController {
 		header('Cache-Control: private, no-cache, no-store, must-revalidate');
 		$id_entry = Minz_Request::paramString('id_entry');
 		$tagDAO = FreshRSS_Factory::createTagDao();
-		$this->view->tagsForEntry = $tagDAO->getTagsForEntry($id_entry);
+		$tagsForEntry = $tagDAO->getTagsForEntry($id_entry);
+		foreach ($tagsForEntry as $key => $tag) {
+			$tagsForEntry[$key]['name'] = htmlspecialchars_decode($tag['name'], ENT_QUOTES);
+		}
+		$this->view->tagsForEntry = $tagsForEntry;
 	}
 
 	public function addAction(): void {

+ 31 - 13
p/scripts/main.js

@@ -1807,8 +1807,10 @@ async function loadDynamicTags(div) {
 		div.querySelector('.dropdown-menu-scrollable').appendChild(li_item0);
 	}
 
-	let html = '';
-	let datalist = '';
+	const dropdown = div.querySelector('.dropdown-menu-scrollable');
+	const datalistLabels = document.getElementById('datalist-labels');
+	datalistLabels.innerHTML = ''; // clear before add the (updated) labels list
+
 	if (json && json.length) {
 		let nbLabelsChecked = 0;
 		for (let i = 0; i < json.length; i++) {
@@ -1820,21 +1822,37 @@ async function loadDynamicTags(div) {
 			if (tag.checked) {
 				nbLabelsChecked++;
 			}
-			html += '<li class="item"><label><input ' +
-				(context.anonymous ? '' : 'class="checkboxTag" ') +
-				'name="t_' + tag.id + '"type="checkbox" ' +
-				(context.anonymous ? 'disabled="disabled" ' : '') +
-				(tag.checked ? 'checked="checked" ' : '') + '/>' + tag.name + '</label></li>';
-			datalist += '<option value="' + tag.name + '"></option>';
+
+			const liItem = document.createElement('li');
+			liItem.className = 'item';
+			const labelItem = document.createElement('label');
+			const inputCheckbox = document.createElement('input');
+			if (!context.anonymous) {
+				inputCheckbox.className = 'checkboxTag';
+			}
+			inputCheckbox.name = 't_' + tag.id;
+			inputCheckbox.type = 'checkbox';
+			inputCheckbox.disabled = context.anonymous;
+			inputCheckbox.checked = !!tag.checked;
+			labelItem.appendChild(inputCheckbox);
+			labelItem.appendChild(document.createTextNode(tag.name));
+			liItem.appendChild(labelItem);
+			dropdown.appendChild(liItem);
+
+			const optionLabel = document.createElement('option');
+			optionLabel.value = tag.name;
+			datalistLabels.appendChild(optionLabel);
 		}
 		if (context.anonymous && nbLabelsChecked === 0) {
-			html += '<li class="item"><span class="emptyLabels">' + context.i18n.labels_empty + '</span></li>';
+			const emptyItem = document.createElement('li');
+			emptyItem.className = 'item';
+			const spanEmpty = document.createElement('span');
+			spanEmpty.className = 'emptyLabels';
+			spanEmpty.textContent = context.i18n.labels_empty;
+			emptyItem.appendChild(spanEmpty);
+			dropdown.appendChild(emptyItem);
 		}
 	}
-	div.querySelector('.dropdown-menu-scrollable').insertAdjacentHTML('beforeend', html);
-	const datalistLabels = document.getElementById('datalist-labels');
-	datalistLabels.innerHTML = ''; // clear before add the (updated) labels list
-	datalistLabels.insertAdjacentHTML('beforeend', datalist);
 }
 
 // <actualize>