fix(edi): clean file name and escape values in code list import

(cherry picked from commit 32bbf5a6c9)
This commit is contained in:
Nabin Hait
2026-10-22 09:33:24 +00:00
committed by Mergify
parent e007159c21
commit 3a84a987c4
3 changed files with 33 additions and 13 deletions
@@ -88,9 +88,14 @@ function show_column_selection_dialog(context) {
for (let column in context.filterable_columns) {
fields.push({
fieldname: `filter_${column}`,
label: __("by {}", [column]),
label: __("by {}", [frappe.utils.escape_html(column)]),
fieldtype: "Select",
options: [null].concat(context.filterable_columns[column]),
options: [null].concat(
context.filterable_columns[column].map((value) => ({
label: frappe.utils.escape_html(value),
value: value,
}))
),
});
}
@@ -176,7 +181,7 @@ function update_preview(dialog, context) {
// Add headers for filterable columns
for (let column in context.filterable_columns) {
if (dialog.get_value(`filter_${column}`)) {
html += `<th>${__(column)}</th>`;
html += `<th>${frappe.utils.escape_html(column)}</th>`;
}
}
@@ -185,23 +190,19 @@ function update_preview(dialog, context) {
for (let i = 0; i < 3; i++) {
html += "<tr>";
if (title_column) {
let title = context.example_values[title_column][i] || "";
html += `<td title="${title}">${truncate(title)}</td>`;
html += preview_cell(context.example_values[title_column][i]);
}
if (code_column) {
let code = context.example_values[code_column][i] || "";
html += `<td title="${code}">${truncate(code)}</td>`;
html += preview_cell(context.example_values[code_column][i]);
}
if (description_column) {
let description = context.example_values[description_column][i] || "";
html += `<td title="${description}">${truncate(description)}</td>`;
html += preview_cell(context.example_values[description_column][i]);
}
// Add values for filterable columns
for (let column in context.filterable_columns) {
if (dialog.get_value(`filter_${column}`)) {
let value = context.example_values[column][i] || "";
html += `<td title="${value}">${truncate(value)}</td>`;
html += preview_cell(context.example_values[column][i]);
}
}
@@ -217,3 +218,8 @@ function truncate(value, maxLength = 40) {
if (typeof value !== "string") return "";
return value.length > maxLength ? value.substring(0, maxLength - 3) + "..." : value;
}
function preview_cell(value) {
value = value || "";
return `<td title="${frappe.utils.escape_html(value)}">${frappe.utils.escape_html(truncate(value))}</td>`;
}
@@ -4,12 +4,14 @@ from urllib.parse import urlsplit
import frappe
import requests
from frappe import _
from frappe.core.doctype.file.utils import get_safe_file_name
from frappe.utils import escape_html
from frappe.utils.file_manager import save_file
from lxml import etree
GENERICODE_FETCH_TIMEOUT = 15
LOCAL_FILE_PREFIXES = ("/files/", "/private/files/")
DEFAULT_FILE_NAME = "genericode.xml"
class RemoteGenericodeUrlNotAllowedError(Exception):
@@ -55,7 +57,7 @@ def import_genericode_from_url(
):
"""Import a Code List from a trusted backend URL."""
content = fetch_genericode_from_url(url)
file_name = urlsplit(url).path.rsplit("/", 1)[-1] or "genericode.xml"
file_name = urlsplit(url).path.rsplit("/", 1)[-1] or DEFAULT_FILE_NAME
return import_genericode_content(
doctype=doctype,
@@ -134,7 +136,7 @@ def import_genericode_content(
code_list.save()
file_doc = save_file(
fname=file_name,
fname=get_safe_file_name(file_name or "").lstrip(".") or DEFAULT_FILE_NAME,
content=content,
dt=doctype,
dn=code_list.name,
@@ -119,6 +119,18 @@ class TestCodeListImport(FrappeTestCase):
file_doc = frappe.get_doc("File", import_result["file"])
self.assertEqual(code_list_import.read_file_bytes(file_doc), SAMPLE_GENERICODE)
def test_uploaded_file_is_saved_under_a_plain_name(self):
cases = (("../../sub/list.xml", "_.._sub_list.xml"), ("..", "genericode.xml"))
for index, (file_name, expected) in enumerate(cases):
frappe.db.delete("Code List", "test-code-list-v1")
content = SAMPLE_GENERICODE + f"<!-- {index} {frappe.generate_hash()} -->".encode()
with self.upload_context(content=content, file_name=file_name):
import_result = code_list_import.import_genericode()
file_doc = frappe.get_doc("File", import_result["file"])
self.assertEqual(file_doc.file_name, expected)
self.assertEqual(file_doc.file_url, f"/private/files/{expected}")
def test_process_genericode_import_reads_file_doc_content(self):
self.set_upload_context(content=SAMPLE_GENERICODE, file_name="uploaded_genericode.xml")