1
0
Fork 0
mirror of https://github.com/EDeev/worldlang.git synced 2026-10-07 20:49:41 +03:00

Экранирование данных из API перед вставкой в HTML

Названия и описания курсов, имена репетиторов, адреса и описания мест на карте
вставлялись в разметку как есть — запись с HTML в общем учебном API выполнилась бы
у каждого посетителя. Добавлена esc() в utils.js, через неё проходят все подстановки
данных в HTML-шаблонах app.js, account.js и map.js.
This commit is contained in:
Деев Егор Викторович 2026-10-02 21:44:31 +00:00
parent ec60a92004
commit e94e27267c
4 changed files with 47 additions and 36 deletions

View file

@ -118,13 +118,13 @@ function renderOrders() {
c => c.id === order.course_id c => c.id === order.course_id
); );
orderName = course ? course.name : orderName = course ? course.name :
`Курс #${order.course_id}`; `Курс #${esc(order.course_id)}`;
} else if (order.tutor_id && order.tutor_id > 0) { } else if (order.tutor_id && order.tutor_id > 0) {
const tutor = AccountState.tutors.find( const tutor = AccountState.tutors.find(
t => t.id === order.tutor_id t => t.id === order.tutor_id
); );
orderName = tutor ? `Репетитор: ${tutor.name}` : orderName = tutor ? `Репетитор: ${tutor.name}` :
`Репетитор #${order.tutor_id}`; `Репетитор #${esc(order.tutor_id)}`;
} }
const orderDate = formatDateTimeWithoutSeconds( const orderDate = formatDateTimeWithoutSeconds(
@ -133,22 +133,22 @@ function renderOrders() {
); );
tr.innerHTML = ` tr.innerHTML = `
<td>${orderNumber}</td> <td>${esc(orderNumber)}</td>
<td>${orderName}</td> <td>${esc(orderName)}</td>
<td>${orderDate}</td> <td>${esc(orderDate)}</td>
<td>${formatPrice(order.price)}</td> <td>${formatPrice(order.price)}</td>
<td> <td>
<div class="btn-group btn-group-sm" role="group"> <div class="btn-group btn-group-sm" role="group">
<button class="btn btn-info view-order-btn" <button class="btn btn-info view-order-btn"
data-order-id="${order.id}"> data-order-id="${esc(order.id)}">
<i class="bi bi-eye"></i> <i class="bi bi-eye"></i>
</button> </button>
<button class="btn btn-warning edit-order-btn" <button class="btn btn-warning edit-order-btn"
data-order-id="${order.id}"> data-order-id="${esc(order.id)}">
<i class="bi bi-pencil"></i> <i class="bi bi-pencil"></i>
</button> </button>
<button class="btn btn-danger delete-order-btn" <button class="btn btn-danger delete-order-btn"
data-order-id="${order.id}"> data-order-id="${esc(order.id)}">
<i class="bi bi-trash"></i> <i class="bi bi-trash"></i>
</button> </button>
</div> </div>
@ -254,7 +254,7 @@ function viewOrderDetails(orderId) {
if (course) { if (course) {
orderTitle = course.name; orderTitle = course.name;
teacherInfo = `<p><strong>Преподаватель:</strong> teacherInfo = `<p><strong>Преподаватель:</strong>
${course.teacher}</p>`; ${esc(course.teacher)}</p>`;
} }
} else if (order.tutor_id && order.tutor_id > 0) { } else if (order.tutor_id && order.tutor_id > 0) {
const tutor = AccountState.tutors.find( const tutor = AccountState.tutors.find(
@ -284,7 +284,7 @@ function viewOrderDetails(orderId) {
options.push('Доступ к интерактивной онлайн-платформе (+50%)'); options.push('Доступ к интерактивной онлайн-платформе (+50%)');
const optionsBadges = options.map(opt => const optionsBadges = options.map(opt =>
`<span class="badge bg-secondary me-1 mb-1">${opt}</span>` `<span class="badge bg-secondary me-1 mb-1">${esc(opt)}</span>`
).join(''); ).join('');
const contactData = ContactDataStorage.get(order.id); const contactData = ContactDataStorage.get(order.id);
@ -294,16 +294,16 @@ function viewOrderDetails(orderId) {
contactInfo = ` contactInfo = `
<hr> <hr>
<h6>Контактные данные:</h6> <h6>Контактные данные:</h6>
<p><strong>Имя:</strong> ${contactData.name}</p> <p><strong>Имя:</strong> ${esc(contactData.name)}</p>
<p><strong>Телефон:</strong> ${contactData.phone}</p> <p><strong>Телефон:</strong> ${esc(contactData.phone)}</p>
<p><strong>Email:</strong> ${contactData.email}</p> <p><strong>Email:</strong> ${esc(contactData.email)}</p>
${contactData.message ? ${contactData.message ?
`<p><strong>Сообщение:</strong> ${contactData.message}</p>` `<p><strong>Сообщение:</strong> ${contactData.message}</p>`
: ''} : ''}
`; `;
} }
let detailsHTML = `<h5>${orderTitle}</h5>${teacherInfo}`; let detailsHTML = `<h5>${esc(orderTitle)}</h5>${teacherInfo}`;
let priceLabel = ''; let priceLabel = '';
if (order.course_id && order.course_id > 0) { if (order.course_id && order.course_id > 0) {
@ -312,9 +312,9 @@ function viewOrderDetails(orderId) {
${formatDate(order.date_start)}</p> ${formatDate(order.date_start)}</p>
<p><strong>Время:</strong> ${formatTime(order.time_start)}</p> <p><strong>Время:</strong> ${formatTime(order.time_start)}</p>
<p><strong>Продолжительность:</strong> <p><strong>Продолжительность:</strong>
${order.duration} часов</p> ${esc(order.duration)} часов</p>
<p><strong>Количество студентов:</strong> <p><strong>Количество студентов:</strong>
${order.persons}</p> ${esc(order.persons)}</p>
${options.length > 0 ? ${options.length > 0 ?
`<p><strong>Выбранные опции:</strong><br>${optionsBadges}</p>` `<p><strong>Выбранные опции:</strong><br>${optionsBadges}</p>`
: ''} : ''}
@ -328,9 +328,9 @@ function viewOrderDetails(orderId) {
} }
detailsHTML += ` detailsHTML += `
${contactInfo} ${esc(contactInfo)}
<hr> <hr>
<h5>${priceLabel}</h5> <h5>${esc(priceLabel)}</h5>
`; `;
content.innerHTML = detailsHTML; content.innerHTML = detailsHTML;

View file

@ -163,7 +163,7 @@ function renderCourses() {
item.innerHTML = ` item.innerHTML = `
<div class="course-item-header"> <div class="course-item-header">
<h5 class="course-item-title">${course.name}</h5> <h5 class="course-item-title">${esc(course.name)}</h5>
<span class="badge level-badge <span class="badge level-badge
${getLevelBadgeClass(course.level)}"> ${getLevelBadgeClass(course.level)}">
${getLevelText(course.level)} ${getLevelText(course.level)}
@ -175,12 +175,12 @@ function renderCourses() {
<div class="course-item-details"> <div class="course-item-details">
<div> <div>
<i class="bi bi-person"></i> <i class="bi bi-person"></i>
<span>${course.teacher}</span> <span>${esc(course.teacher)}</span>
</div> </div>
<div> <div>
<i class="bi bi-clock"></i> <i class="bi bi-clock"></i>
<span>${course.total_length} недель, <span>${esc(course.total_length)} недель,
${course.week_length} ч/нед</span> ${esc(course.week_length)} ч/нед</span>
</div> </div>
<div> <div>
<i class="bi bi-cash"></i> <i class="bi bi-cash"></i>
@ -582,13 +582,13 @@ function renderTutors() {
<i class="bi bi-person-circle"></i> <i class="bi bi-person-circle"></i>
</div> </div>
</td> </td>
<td>${tutor.name}</td> <td>${esc(tutor.name)}</td>
<td><span class="badge level-badge <td><span class="badge level-badge
${getLevelBadgeClass(tutor.language_level)}"> ${getLevelBadgeClass(tutor.language_level)}">
${getLevelText(tutor.language_level)} ${getLevelText(tutor.language_level)}
</span></td> </span></td>
<td>${tutor.languages_offered.join(', ')}</td> <td>${tutor.languages_offered.join(', ')}</td>
<td>${tutor.work_experience} лет</td> <td>${esc(tutor.work_experience)} лет</td>
<td>${formatPrice(tutor.price_per_hour)}/час</td> <td>${formatPrice(tutor.price_per_hour)}/час</td>
<td> <td>
<button class="btn btn-sm btn-primary select-tutor-btn"> <button class="btn btn-sm btn-primary select-tutor-btn">

View file

@ -109,12 +109,12 @@ function initMap() {
const placemark = new ymaps.Placemark( const placemark = new ymaps.Placemark(
resource.coords, resource.coords,
{ {
balloonContentHeader: `<strong>${resource.name}</strong>`, balloonContentHeader: `<strong>${esc(resource.name)}</strong>`,
balloonContentBody: ` balloonContentBody: `
<p class="mb-1"><i class="bi bi-geo-alt"></i> ${resource.address}</p> <p class="mb-1"><i class="bi bi-geo-alt"></i> ${esc(resource.address)}</p>
<p class="mb-1"><i class="bi bi-clock"></i> ${resource.hours}</p> <p class="mb-1"><i class="bi bi-clock"></i> ${esc(resource.hours)}</p>
<p class="mb-1"><i class="bi bi-telephone"></i> ${resource.phone}</p> <p class="mb-1"><i class="bi bi-telephone"></i> ${esc(resource.phone)}</p>
<p class="mb-0"><i class="bi bi-info-circle"></i> ${resource.description}</p> <p class="mb-0"><i class="bi bi-info-circle"></i> ${esc(resource.description)}</p>
`, `,
hintContent: resource.name hintContent: resource.name
}, },
@ -130,11 +130,11 @@ function initMap() {
const item = document.createElement('div'); const item = document.createElement('div');
item.className = 'resource-item'; item.className = 'resource-item';
item.innerHTML = ` item.innerHTML = `
<h6>${resource.name}</h6> <h6>${esc(resource.name)}</h6>
<p class="mb-1"><i class="bi bi-geo-alt me-1"></i>${resource.address}</p> <p class="mb-1"><i class="bi bi-geo-alt me-1"></i>${esc(resource.address)}</p>
<p class="mb-1"><i class="bi bi-clock me-1"></i>${resource.hours}</p> <p class="mb-1"><i class="bi bi-clock me-1"></i>${esc(resource.hours)}</p>
<p class="mb-1"><i class="bi bi-telephone me-1"></i>${resource.phone}</p> <p class="mb-1"><i class="bi bi-telephone me-1"></i>${esc(resource.phone)}</p>
<p class="mb-0 text-muted small">${resource.description}</p> <p class="mb-0 text-muted small">${esc(resource.description)}</p>
`; `;
item.addEventListener('click', () => { item.addEventListener('click', () => {

View file

@ -1,3 +1,14 @@
// Экранирование данных перед вставкой в HTML (данные приходят из общего учебного API);
// utils.js подключается раньше app.js, account.js и map.js, поэтому esc() доступна во всех них
function esc(value) {
return String(value ?? '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
// Показать уведомление с автоматическим удалением // Показать уведомление с автоматическим удалением
function showNotification(message, type = 'info') { function showNotification(message, type = 'info') {
const notificationArea = document.getElementById('notificationArea'); const notificationArea = document.getElementById('notificationArea');
@ -24,8 +35,8 @@ function showNotification(message, type = 'info') {
notification.className = `alert ${alertClass} alert-dismissible notification.className = `alert ${alertClass} alert-dismissible
fade show notification`; fade show notification`;
notification.innerHTML = ` notification.innerHTML = `
<i class="bi ${iconClass} me-2"></i> <i class="bi ${esc(iconClass)} me-2"></i>
${message} ${esc(message)}
<button type="button" class="btn-close" <button type="button" class="btn-close"
data-bs-dismiss="alert"></button> data-bs-dismiss="alert"></button>
`; `;