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:
@@ -18,6 +18,7 @@ Returned shape (best-effort; missing keys are OK in templates):
|
||||
}
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import os
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
@@ -52,7 +53,28 @@ def _text(parent, path, default=""):
|
||||
return el.text.strip()
|
||||
|
||||
|
||||
# Parsed reports by path, reused while the file's size and mtime are
|
||||
# unchanged. The reports list, the to-do (which walks a year of reports
|
||||
# several times per request), exports and evidence packs all parse the same
|
||||
# files; a report on the share is never edited in place. Callers must not
|
||||
# mutate the returned dict.
|
||||
_parse_cache: dict = {}
|
||||
|
||||
|
||||
def parse(path: str) -> dict:
|
||||
st = os.stat(path)
|
||||
sig = (st.st_mtime_ns, st.st_size)
|
||||
hit = _parse_cache.get(path)
|
||||
if hit and hit[0] == sig:
|
||||
return hit[1]
|
||||
data = _parse(path)
|
||||
if len(_parse_cache) > 5000: # far above a site's report count; just a bound
|
||||
_parse_cache.clear()
|
||||
_parse_cache[path] = (sig, data)
|
||||
return data
|
||||
|
||||
|
||||
def _parse(path: str) -> dict:
|
||||
tree = ET.parse(path)
|
||||
root = tree.getroot()
|
||||
rep = root.find(".//report/blancco_data") or root
|
||||
|
||||
Reference in New Issue
Block a user