Refactor web_scan.py for improved readability and maintainability; add cron job script for npm log backups; update start.ps1 for consistent formatting.
This commit is contained in:
1 parent
0d1cd21468
commit
c2eba88ac8
113 files changed
+772606
-772579
No files matched your search
+185
-185
@@ -1,185 +1,185 @@
|
||||
{% extends "base.html" %} {% block title %}Scan — Intrusion Detection{% endblock
|
||||
%} {% block content %}
|
||||
<h1>Run a scan</h1>
|
||||
<p class="muted">
|
||||
Scans run in the background; only one at a time. You can leave this page and
|
||||
come back.
|
||||
</p>
|
||||
|
||||
<div class="panel">
|
||||
<form id="scan-form" class="form-grid">
|
||||
<label
|
||||
>Type
|
||||
<select id="kind">
|
||||
<option value="consolidated">Consolidated (copyparty + npm)</option>
|
||||
<option value="copyparty">copyparty only</option>
|
||||
<option value="npm">npm only</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>Top IPs <input id="top" type="number" value="30" min="1" /></label>
|
||||
<label
|
||||
>Min denied (copyparty)
|
||||
<input id="min_denied" type="number" value="1" min="0"
|
||||
/></label>
|
||||
<label
|
||||
>Min errors (npm) <input id="min_errors" type="number" value="5" min="0"
|
||||
/></label>
|
||||
<label
|
||||
>Error ratio (npm)
|
||||
<input
|
||||
id="error_ratio"
|
||||
type="number"
|
||||
step="0.05"
|
||||
value="0.5"
|
||||
min="0"
|
||||
max="1"
|
||||
/></label>
|
||||
<label class="check"
|
||||
><input id="update_firewall" type="checkbox" /> Update firewall after
|
||||
scan</label
|
||||
>
|
||||
<label class="check"
|
||||
><input id="dry_run" type="checkbox" /> Dry-run (preview, no
|
||||
changes)</label
|
||||
>
|
||||
<label class="check"
|
||||
><input id="full" type="checkbox" /> Full scan (ignore incremental
|
||||
state)</label
|
||||
>
|
||||
</form>
|
||||
<div class="panel-actions">
|
||||
<button class="btn primary" id="run-btn">Run scan</button>
|
||||
<span class="muted" id="busy-hint"></span>
|
||||
</div>
|
||||
<div id="job-status" class="job-status"></div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head"><h2>Recent jobs</h2></div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Job</th>
|
||||
<th>Kind</th>
|
||||
<th>State</th>
|
||||
<th>Started</th>
|
||||
<th>Finished</th>
|
||||
<th class="num">Blocked</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="jobs"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script>
|
||||
let pollTimer = null;
|
||||
|
||||
const form = document.getElementById("scan-form");
|
||||
const runBtn = document.getElementById("run-btn");
|
||||
const hint = document.getElementById("busy-hint");
|
||||
|
||||
async function runScan() {
|
||||
const kind = document.getElementById("kind").value;
|
||||
const params = {
|
||||
top: parseInt(document.getElementById("top").value, 10) || 30,
|
||||
min_denied:
|
||||
parseInt(document.getElementById("min_denied").value, 10) || 1,
|
||||
min_errors:
|
||||
parseInt(document.getElementById("min_errors").value, 10) || 5,
|
||||
error_ratio:
|
||||
parseFloat(document.getElementById("error_ratio").value) || 0.5,
|
||||
update_firewall: document.getElementById("update_firewall").checked,
|
||||
dry_run: document.getElementById("dry_run").checked,
|
||||
full: document.getElementById("full").checked,
|
||||
quiet: true,
|
||||
};
|
||||
runBtn.disabled = true;
|
||||
try {
|
||||
const job = await startScan(kind, params);
|
||||
setStatus(`Job ${job.job_id} started (${job.state}).`, "ok");
|
||||
poll(job.job_id);
|
||||
} catch (e) {
|
||||
setStatus(e.message, "error");
|
||||
runBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function setStatus(msg, type) {
|
||||
const box = document.getElementById("job-status");
|
||||
box.textContent = msg;
|
||||
box.style.color =
|
||||
type === "error"
|
||||
? "var(--danger)"
|
||||
: type === "ok"
|
||||
? "var(--ok)"
|
||||
: "inherit";
|
||||
}
|
||||
|
||||
async function poll(jobId) {
|
||||
clearTimeout(pollTimer);
|
||||
let job;
|
||||
try {
|
||||
job = await api(`/api/scan/${jobId}`);
|
||||
} catch (e) {
|
||||
setStatus(e.message, "error");
|
||||
runBtn.disabled = false;
|
||||
return;
|
||||
}
|
||||
const r = job.result || {};
|
||||
setStatus(
|
||||
`[${job.state}] ${job.kind} — started ${fmtTime(job.started)}` +
|
||||
(job.finished ? `, finished ${fmtTime(job.finished)}` : "") +
|
||||
(r.blocked_targets != null
|
||||
? `, blocked targets: ${r.blocked_targets}`
|
||||
: "") +
|
||||
(job.error ? `, error: ${job.error}` : ""),
|
||||
job.state === "error" ? "error" : job.state === "done" ? "ok" : "",
|
||||
);
|
||||
if (job.state === "running" || job.state === "queued") {
|
||||
pollTimer = setTimeout(() => poll(jobId), 2000);
|
||||
} else {
|
||||
runBtn.disabled = false;
|
||||
refreshJobs();
|
||||
const running = (await api("/api/scan/status")).running;
|
||||
hint.textContent = running ? "" : "";
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshJobs() {
|
||||
try {
|
||||
const s = await api("/api/scan/status");
|
||||
hint.textContent = s.running ? "A scan is currently running…" : "";
|
||||
const body = document.getElementById("jobs");
|
||||
body.innerHTML = "";
|
||||
const jobs = s.latest_scans || [];
|
||||
if (!jobs.length) {
|
||||
body.innerHTML =
|
||||
'<tr><td colspan="6" class="muted">No jobs yet.</td></tr>';
|
||||
return;
|
||||
}
|
||||
for (const j of jobs) {
|
||||
const r = j.result || {};
|
||||
body.append(
|
||||
el(
|
||||
"tr",
|
||||
el("td", { class: "mono", text: j.job_id }),
|
||||
el("td", { text: j.kind }),
|
||||
el("td", {}, badge(j.state)),
|
||||
el("td", { text: fmtTime(j.started) }),
|
||||
el("td", { text: fmtTime(j.finished) }),
|
||||
el("td", { class: "num", text: r.blocked_targets ?? "—" }),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
toast(e.message, "error");
|
||||
}
|
||||
}
|
||||
|
||||
runBtn.addEventListener("click", runScan);
|
||||
refreshJobs();
|
||||
// /api/scan/status is only polled every 15 s; the active job's own
|
||||
// 2 s poll keeps live progress without hammering the status endpoint.
|
||||
setInterval(refreshJobs, 15000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% extends "base.html" %} {% block title %}Scan — Intrusion Detection{% endblock
|
||||
%} {% block content %}
|
||||
<h1>Run a scan</h1>
|
||||
<p class="muted">
|
||||
Scans run in the background; only one at a time. You can leave this page and
|
||||
come back.
|
||||
</p>
|
||||
|
||||
<div class="panel">
|
||||
<form id="scan-form" class="form-grid">
|
||||
<label
|
||||
>Type
|
||||
<select id="kind">
|
||||
<option value="consolidated">Consolidated (copyparty + npm)</option>
|
||||
<option value="copyparty">copyparty only</option>
|
||||
<option value="npm">npm only</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>Top IPs <input id="top" type="number" value="30" min="1" /></label>
|
||||
<label
|
||||
>Min denied (copyparty)
|
||||
<input id="min_denied" type="number" value="1" min="0"
|
||||
/></label>
|
||||
<label
|
||||
>Min errors (npm) <input id="min_errors" type="number" value="5" min="0"
|
||||
/></label>
|
||||
<label
|
||||
>Error ratio (npm)
|
||||
<input
|
||||
id="error_ratio"
|
||||
type="number"
|
||||
step="0.05"
|
||||
value="0.5"
|
||||
min="0"
|
||||
max="1"
|
||||
/></label>
|
||||
<label class="check"
|
||||
><input id="update_firewall" type="checkbox" /> Update firewall after
|
||||
scan</label
|
||||
>
|
||||
<label class="check"
|
||||
><input id="dry_run" type="checkbox" /> Dry-run (preview, no
|
||||
changes)</label
|
||||
>
|
||||
<label class="check"
|
||||
><input id="full" type="checkbox" /> Full scan (ignore incremental
|
||||
state)</label
|
||||
>
|
||||
</form>
|
||||
<div class="panel-actions">
|
||||
<button class="btn primary" id="run-btn">Run scan</button>
|
||||
<span class="muted" id="busy-hint"></span>
|
||||
</div>
|
||||
<div id="job-status" class="job-status"></div>
|
||||
</div>
|
||||
|
||||
<div class="panel">
|
||||
<div class="panel-head"><h2>Recent jobs</h2></div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Job</th>
|
||||
<th>Kind</th>
|
||||
<th>State</th>
|
||||
<th>Started</th>
|
||||
<th>Finished</th>
|
||||
<th class="num">Blocked</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="jobs"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %} {% block scripts %}
|
||||
<script>
|
||||
let pollTimer = null;
|
||||
|
||||
const form = document.getElementById("scan-form");
|
||||
const runBtn = document.getElementById("run-btn");
|
||||
const hint = document.getElementById("busy-hint");
|
||||
|
||||
async function runScan() {
|
||||
const kind = document.getElementById("kind").value;
|
||||
const params = {
|
||||
top: parseInt(document.getElementById("top").value, 10) || 30,
|
||||
min_denied:
|
||||
parseInt(document.getElementById("min_denied").value, 10) || 1,
|
||||
min_errors:
|
||||
parseInt(document.getElementById("min_errors").value, 10) || 5,
|
||||
error_ratio:
|
||||
parseFloat(document.getElementById("error_ratio").value) || 0.5,
|
||||
update_firewall: document.getElementById("update_firewall").checked,
|
||||
dry_run: document.getElementById("dry_run").checked,
|
||||
full: document.getElementById("full").checked,
|
||||
quiet: true,
|
||||
};
|
||||
runBtn.disabled = true;
|
||||
try {
|
||||
const job = await startScan(kind, params);
|
||||
setStatus(`Job ${job.job_id} started (${job.state}).`, "ok");
|
||||
poll(job.job_id);
|
||||
} catch (e) {
|
||||
setStatus(e.message, "error");
|
||||
runBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function setStatus(msg, type) {
|
||||
const box = document.getElementById("job-status");
|
||||
box.textContent = msg;
|
||||
box.style.color =
|
||||
type === "error"
|
||||
? "var(--danger)"
|
||||
: type === "ok"
|
||||
? "var(--ok)"
|
||||
: "inherit";
|
||||
}
|
||||
|
||||
async function poll(jobId) {
|
||||
clearTimeout(pollTimer);
|
||||
let job;
|
||||
try {
|
||||
job = await api(`/api/scan/${jobId}`);
|
||||
} catch (e) {
|
||||
setStatus(e.message, "error");
|
||||
runBtn.disabled = false;
|
||||
return;
|
||||
}
|
||||
const r = job.result || {};
|
||||
setStatus(
|
||||
`[${job.state}] ${job.kind} — started ${fmtTime(job.started)}` +
|
||||
(job.finished ? `, finished ${fmtTime(job.finished)}` : "") +
|
||||
(r.blocked_targets != null
|
||||
? `, blocked targets: ${r.blocked_targets}`
|
||||
: "") +
|
||||
(job.error ? `, error: ${job.error}` : ""),
|
||||
job.state === "error" ? "error" : job.state === "done" ? "ok" : "",
|
||||
);
|
||||
if (job.state === "running" || job.state === "queued") {
|
||||
pollTimer = setTimeout(() => poll(jobId), 2000);
|
||||
} else {
|
||||
runBtn.disabled = false;
|
||||
refreshJobs();
|
||||
const running = (await api("/api/scan/status")).running;
|
||||
hint.textContent = running ? "" : "";
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshJobs() {
|
||||
try {
|
||||
const s = await api("/api/scan/status");
|
||||
hint.textContent = s.running ? "A scan is currently running…" : "";
|
||||
const body = document.getElementById("jobs");
|
||||
body.innerHTML = "";
|
||||
const jobs = s.latest_scans || [];
|
||||
if (!jobs.length) {
|
||||
body.innerHTML =
|
||||
'<tr><td colspan="6" class="muted">No jobs yet.</td></tr>';
|
||||
return;
|
||||
}
|
||||
for (const j of jobs) {
|
||||
const r = j.result || {};
|
||||
body.append(
|
||||
el(
|
||||
"tr",
|
||||
el("td", { class: "mono", text: j.job_id }),
|
||||
el("td", { text: j.kind }),
|
||||
el("td", {}, badge(j.state)),
|
||||
el("td", { text: fmtTime(j.started) }),
|
||||
el("td", { text: fmtTime(j.finished) }),
|
||||
el("td", { class: "num", text: r.blocked_targets ?? "—" }),
|
||||
),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
toast(e.message, "error");
|
||||
}
|
||||
}
|
||||
|
||||
runBtn.addEventListener("click", runScan);
|
||||
refreshJobs();
|
||||
// /api/scan/status is only polled every 15 s; the active job's own
|
||||
// 2 s poll keeps live progress without hammering the status endpoint.
|
||||
setInterval(refreshJobs, 15000);
|
||||
</script>
|
||||
{% endblock %}
|
||||
Reference in new issue
Block a user