Webapp redesign: fixes from the code review
Blocker - /reports/export returned 500: REPORT_EXPORT_RANGES and _months_ago were deleted with the old reports route. Restored. Major - A session without stage_index/stage_total took down /, /imaging and /imaging/tiles (int filter on Undefined). The templates default both; saving a LAPS password for a session that no longer exists is refused (it created a bare session). - /reports/todo parsed every report four times (23 s at 700 reports). blancco_report.parse() now caches by path, mtime and size: 0.09 s. - Stored XSS through the session serial in the Clear confirm (the browser decodes ' before running an inline handler). The message moves to data-confirm, and update_session() only accepts serials of letters, digits, '.', '_' and '-' (400 from /imaging/status otherwise). - Reports without a document id and with odd characters in the file name got a key the mark routes rejected, so they could never be cleared. report_key() now always produces a valid key. Minor - failure_note() handles a list-valued erasure target (500 on the report and to-do pages). - _recently_marked() tolerates naive, 'Z' and non-dict state entries. - Key regexes use fullmatch (a trailing newline was accepted). - Unreadable reports are no longer counted as to tick / to destroy; the reports page already flags them as Unreadable. - Rollback review recounts restorable files after reading the backups. - Enrollment: the startnet fallback package (no or empty ppkg.conf) and a quoted SOURCE_PPKG are protected too. - Backup upload overwrite check is case-insensitive (SMB share) and replaces the existing file rather than adding a case twin. - Overview polling keeps the last good rows on an error and pauses while the tab is hidden. A shown or typed LAPS password survives the floor's refresh. - hardware_dismiss only redirects to same-site paths; tiles ?limit is clamped; the Updates overview survives an unreadable Dell scan or iPXE state; describe() uses aversion(); a client cannot label its session 'inferred'.
This commit is contained in:
@@ -51,8 +51,8 @@
|
||||
{% macro floor_row(s) %}
|
||||
{% set is_inferred = (s.source == 'inferred') %}
|
||||
{% set is_blancco = (s.source == 'blancco' or s.pctype == 'blancco') %}
|
||||
{% set stage_idx = s.stage_index | int(0) %}
|
||||
{% set stage_total = s.stage_total | int(0) %}
|
||||
{% set stage_idx = (s.stage_index or 0) | int(0) %}
|
||||
{% set stage_total = (s.stage_total or 0) | int(0) %}
|
||||
{% set state = s.floor_state or 'running' %}
|
||||
{% set drive_pct = None %}
|
||||
{% if is_blancco and stage_idx == 4 and s.current_stage and '%]' in s.current_stage %}
|
||||
@@ -178,7 +178,8 @@
|
||||
{% if is_blancco %}<a href="{{ url_for('blancco_reports') }}">Erasure reports</a>{% endif %}
|
||||
<div class="sep"></div>
|
||||
<form method="post" action="{{ url_for('imaging_delete_session', serial=s.serial) }}"
|
||||
onsubmit="return confirm('Clear the session for {{ s.serial }}? Its status and log files are deleted.');">
|
||||
data-confirm="Clear the session for {{ s.serial }}? Its status and log files are deleted."
|
||||
onsubmit="return confirm(this.dataset.confirm);">
|
||||
<input type="hidden" name="_csrf_token" value="{{ csrf_token() }}">
|
||||
<button type="submit" class="del">Clear session</button>
|
||||
</form>
|
||||
|
||||
@@ -208,8 +208,11 @@
|
||||
(function () {
|
||||
var url = "{{ url_for('imaging_tiles_partial', limit=6) }}";
|
||||
setInterval(function () {
|
||||
if (document.querySelector('#imaging-tiles details[open], #imaging-tiles details.more[open]')) return;
|
||||
fetch(url, {credentials: 'same-origin'}).then(function (r) { return r.text(); }).then(function (html) {
|
||||
if (document.hidden || document.querySelector('#imaging-tiles details[open], #imaging-tiles details.more[open]')) return;
|
||||
fetch(url, {credentials: 'same-origin'}).then(function (r) {
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status); // keep the last good rows
|
||||
return r.text();
|
||||
}).then(function (html) {
|
||||
var box = document.getElementById('imaging-tiles');
|
||||
if (box) { box.innerHTML = html; if (window.renderAllQRs) window.renderAllQRs(); }
|
||||
}).catch(function () {});
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
{% if packages %}
|
||||
<div class="sgrid colhead" aria-hidden="true"><span>File</span><span class="num">Size</span><span class="when">Modified</span></div>
|
||||
{% for p in packages %}
|
||||
{% set is_active = active_ppkg and active_ppkg.SOURCE_PPKG and p.filename|lower == active_ppkg.SOURCE_PPKG|lower %}
|
||||
{% set is_active = p.filename|lower == effective_ppkg|lower %}
|
||||
<div class="ui-row">
|
||||
<div class="sgrid">
|
||||
<span class="s-name">
|
||||
|
||||
@@ -105,6 +105,11 @@ Content-Type: application/json
|
||||
// copy. The next refresh after they hit Make-QR will catch up.
|
||||
var active = document.activeElement;
|
||||
if (active && active.classList && active.classList.contains('laps-input')) return true;
|
||||
// Also while a password is shown, or typed but not yet made into a QR.
|
||||
var inputs = document.querySelectorAll('.laps-input');
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
if (inputs[i].type === 'text' || inputs[i].value !== inputs[i].defaultValue) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
{% set st = session.status or 'in_progress' %}
|
||||
{% set st_chip = {'succeeded': ('Succeeded', 'ok'), 'failed': ('Failed', 'fail')}.get(st, ('In progress', 'run')) %}
|
||||
{% set st_kind = {'succeeded': 'ok', 'failed': 'fail'}.get(st, 'run') %}
|
||||
{% set stage_idx = session.stage_index | int(0) %}
|
||||
{% set stage_total = session.stage_total | int(0) %}
|
||||
{% set stage_idx = (session.stage_index or 0) | int(0) %}
|
||||
{% set stage_total = (session.stage_total or 0) | int(0) %}
|
||||
{% set history = session.stage_history or [] %}
|
||||
|
||||
{# Support summary: built here so the copy button carries it (static/app.js
|
||||
|
||||
Reference in New Issue
Block a user