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

View file

@ -163,7 +163,7 @@ function renderCourses() {
item.innerHTML = `
<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
${getLevelBadgeClass(course.level)}">
${getLevelText(course.level)}
@ -175,12 +175,12 @@ function renderCourses() {
<div class="course-item-details">
<div>
<i class="bi bi-person"></i>
<span>${course.teacher}</span>
<span>${esc(course.teacher)}</span>
</div>
<div>
<i class="bi bi-clock"></i>
<span>${course.total_length} недель,
${course.week_length} ч/нед</span>
<span>${esc(course.total_length)} недель,
${esc(course.week_length)} ч/нед</span>
</div>
<div>
<i class="bi bi-cash"></i>
@ -582,13 +582,13 @@ function renderTutors() {
<i class="bi bi-person-circle"></i>
</div>
</td>
<td>${tutor.name}</td>
<td>${esc(tutor.name)}</td>
<td><span class="badge level-badge
${getLevelBadgeClass(tutor.language_level)}">
${getLevelText(tutor.language_level)}
</span></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>
<button class="btn btn-sm btn-primary select-tutor-btn">

View file

@ -109,12 +109,12 @@ function initMap() {
const placemark = new ymaps.Placemark(
resource.coords,
{
balloonContentHeader: `<strong>${resource.name}</strong>`,
balloonContentHeader: `<strong>${esc(resource.name)}</strong>`,
balloonContentBody: `
<p class="mb-1"><i class="bi bi-geo-alt"></i> ${resource.address}</p>
<p class="mb-1"><i class="bi bi-clock"></i> ${resource.hours}</p>
<p class="mb-1"><i class="bi bi-telephone"></i> ${resource.phone}</p>
<p class="mb-0"><i class="bi bi-info-circle"></i> ${resource.description}</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> ${esc(resource.hours)}</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> ${esc(resource.description)}</p>
`,
hintContent: resource.name
},
@ -130,11 +130,11 @@ function initMap() {
const item = document.createElement('div');
item.className = 'resource-item';
item.innerHTML = `
<h6>${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-clock me-1"></i>${resource.hours}</p>
<p class="mb-1"><i class="bi bi-telephone me-1"></i>${resource.phone}</p>
<p class="mb-0 text-muted small">${resource.description}</p>
<h6>${esc(resource.name)}</h6>
<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>${esc(resource.hours)}</p>
<p class="mb-1"><i class="bi bi-telephone me-1"></i>${esc(resource.phone)}</p>
<p class="mb-0 text-muted small">${esc(resource.description)}</p>
`;
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') {
const notificationArea = document.getElementById('notificationArea');
@ -24,8 +35,8 @@ function showNotification(message, type = 'info') {
notification.className = `alert ${alertClass} alert-dismissible
fade show notification`;
notification.innerHTML = `
<i class="bi ${iconClass} me-2"></i>
${message}
<i class="bi ${esc(iconClass)} me-2"></i>
${esc(message)}
<button type="button" class="btn-close"
data-bs-dismiss="alert"></button>
`;