Refuse APK entries Android can't read; ignore stale title lists

- Only stored and deflated entries are read: Python 3.9's bzip2 and lzma
  readers inflate without bound before trimming.
- loadTitles drops a response that a newer request has overtaken, so the
  install dialog's replace warning uses the fresh list.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
saphidandClaude Opus 5.5 committed 2026-09-26 22:49:18 +10:00
1 parent dfacf43e55
commit fd0a284942
3 files changed
+20 -4

No files matched your search

+6 -1
View File
@@ -189,7 +189,12 @@ def _icons(attr, res):
def _read(z, name, limit):
"""A member's bytes, inflating at most limit + 1 of them whatever its header claims
(ZipFile.read inflates everything first, then trims to the declared size)."""
size = z.getinfo(name).file_size
info = z.getinfo(name)
# Android only reads stored and deflated entries, and only those bound what
# a read inflates (Python 3.9's bzip2 and lzma readers don't).
if info.compress_type not in (zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED):
raise ApkError(f'{name} in the APK uses a compression Android does not')
size = info.file_size
if size > limit:
raise ApkError(f'{name} in the APK is {size / 1024**2:.0f} MB, more than a real one ({limit // 1024**2} MB)')
with z.open(name) as f:
+6 -3
View File
@@ -1277,10 +1277,13 @@ async function installTitle(token, choice) {
} finally { $("prog").style.display = "none"; loadTitles(); }
}
let installedTitles = null; // lower-case ids, so the install dialog can warn before replacing one; null: unknown
let titlesSeq = 0; // a slower, older request mustn't overwrite a newer answer
async function loadTitles() {
let list;
try { list = (await api("/api/titles")).titles; }
catch (e) { installedTitles = null; $("titleList").innerHTML = `<div class="sub">${esc(e.message)}</div>`; return; }
const seq = ++titlesSeq;
let list, err;
try { list = (await api("/api/titles")).titles; } catch (e) { err = e; }
if (seq !== titlesSeq) return;
if (err) { installedTitles = null; $("titleList").innerHTML = `<div class="sub">${esc(err.message)}</div>`; return; }
installedTitles = new Set(list.map(t => String(t.id).toLowerCase()));
$("titleCount").textContent = list.length ? `${list.length}` : "";
$("titleList").innerHTML = list.length ? list.map(t => `