feat(dlp): implement case management service and DLP Review integration

This commit is contained in:
igor04091968
2026-05-13 03:08:53 +03:00
parent bc44052391
commit 28a4dae67f
11 changed files with 616 additions and 36 deletions
+99
View File
@@ -958,6 +958,7 @@
'<td class="aw-ru-dlp-actions">' +
'<button type="button" data-aw-ru-save-review>Сохранить</button>' +
'<button type="button" data-aw-ru-save-rule>Правило</button>' +
'<button type="button" data-aw-ru-create-case>Кейс</button>' +
"</td>" +
"</tr>"
);
@@ -1028,6 +1029,55 @@
}, 1);
}
function getCaseApiBase() {
if (window.__awCaseApiBase && typeof window.__awCaseApiBase === "string") {
return window.__awCaseApiBase.replace(/\/+$/, "");
}
try {
const origin = window.location.origin || "";
if (/:\d+$/.test(origin)) return origin.replace(/:\d+$/, ":5602");
return origin + ":5602";
} catch (error) {
return "http://127.0.0.1:5602";
}
}
async function caseApi(path, init) {
const response = await fetch(getCaseApiBase() + path, Object.assign({ credentials: "omit" }, init || {}));
if (!response.ok) throw new Error("Case API HTTP " + response.status);
if (response.status === 204) return null;
return response.json();
}
async function createCaseFromEvent(host, event, row) {
const data = event.data || {};
const verdict = row.querySelector("[data-aw-ru-dlp-verdict]").value;
const category = row.querySelector("[data-aw-ru-dlp-category]").value.trim();
const comment = row.querySelector("[data-aw-ru-dlp-comment]").value.trim();
const incidentId = buildDlpKey(event);
const title = "DLP " + (data.signalType || "incident") + " · " + (data.username || data.owner || host || "unknown");
return caseApi("/api/0/dlp/cases", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
incident_id: incidentId,
host: host,
title: title,
severity: verdict === "incident" ? "high" : "medium",
source_bucket: getDlpBucketIdFromHash(),
source_event_ts: event.timestamp,
evidence: {
signalType: data.signalType || "",
username: data.username || data.owner || "",
documentName: data.documentName || "",
printerName: data.printerName || "",
category: category,
comment: comment
}
})
});
}
async function saveDlpRule(host, event, row) {
const bucketId = "aw-dlp-rules_" + host;
await ensureAwBucket(bucketId, "aw-dlp-rules", "aw.dlp.rule", host);
@@ -1081,9 +1131,47 @@
message.textContent = "Ошибка сохранения правила: " + error.message;
}
});
row.querySelector("[data-aw-ru-create-case]").addEventListener("click", async function () {
const message = center.querySelector("[data-aw-ru-dlp-message]");
try {
const created = await createCaseFromEvent(host, event, row);
await renderCaseManager(center, host);
message.textContent = "Кейс создан: #" + (created && created.id ? created.id : "?");
} catch (error) {
message.textContent = "Ошибка создания кейса: " + error.message;
}
});
});
}
async function renderCaseManager(center, host) {
const tbody = center.querySelector("[data-aw-ru-dlp-cases]");
if (!tbody) return;
try {
const cases = await caseApi("/api/0/dlp/cases?host=" + encodeURIComponent(host) + "&limit=100", { method: "GET" });
const rows = (cases || []).map(function (c) {
return (
"<tr>" +
"<td>" + escapeHtml(String(c.id || "")) + "</td>" +
"<td>" + escapeHtml(String(c.status || "")) + "</td>" +
"<td>" + escapeHtml(String(c.severity || "")) + "</td>" +
"<td>" + escapeHtml(String(c.title || "")) + "</td>" +
"<td>" + escapeHtml(String(c.assignee || "")) + "</td>" +
"<td>" + escapeHtml(String(c.incident_id || "")) + "</td>" +
"<td>" + escapeHtml(String(c.updated_at || c.created_at || "")) + "</td>" +
"</tr>"
);
});
tbody.innerHTML = rows.length ? rows.join("") : '<tr><td colspan="7">Кейсов нет.</td></tr>';
const status = center.querySelector("[data-aw-ru-dlp-cases-status]");
if (status) status.textContent = "Кейсов: " + (cases || []).length;
} catch (error) {
tbody.innerHTML = '<tr><td colspan="7">Ошибка загрузки кейсов: ' + escapeHtml(error.message) + '</td></tr>';
const status = center.querySelector("[data-aw-ru-dlp-cases-status]");
if (status) status.textContent = "Кейсы недоступны";
}
}
async function setDlpRuleEnabled(host, ruleEvent, enabled) {
const bucketId = "aw-dlp-rules_" + host;
await ensureAwBucket(bucketId, "aw-dlp-rules", "aw.dlp.rule", host);
@@ -1238,6 +1326,7 @@
state.reviews = [];
}
renderDlpTableRows(center, host);
await renderCaseManager(center, host);
center.querySelector("[data-aw-ru-dlp-message]").textContent = "DLP review центр обновлен.";
} catch (error) {
center.querySelector("[data-aw-ru-dlp-message]").textContent = "Ошибка загрузки DLP-событий: " + error.message;
@@ -1293,6 +1382,16 @@
'<tbody data-aw-ru-dlp-reviews><tr><td colspan="7">Загрузка...</td></tr></tbody>' +
'</table>' +
'</div>' +
'<div class="aw-ru-dlp-section">' +
'<div class="aw-ru-dlp-toolbar">' +
'<h5>Case Management</h5>' +
'<div class="aw-ru-dlp-status" data-aw-ru-dlp-cases-status>Кейсов: 0</div>' +
'</div>' +
'<table class="aw-ru-dlp-table">' +
'<thead><tr><th>ID</th><th>Статус</th><th>Severity</th><th>Заголовок</th><th>Исполнитель</th><th>Incident ID</th><th>Обновлено</th></tr></thead>' +
'<tbody data-aw-ru-dlp-cases><tr><td colspan="7">Загрузка...</td></tr></tbody>' +
'</table>' +
'</div>' +
'<div class="aw-ru-dlp-message" data-aw-ru-dlp-message></div>';
heading.parentElement.insertBefore(center, heading.nextSibling);
center.querySelector("[data-aw-ru-refresh-dlp]").addEventListener("click", function () {