security watch: remember a notice per group — a catalog pin and a DooPlex pin of one image no longer share one memory entry (old entries copied once, no re-mail); W13

This commit is contained in:
admin committed 2026-10-28 10:39:41 +01:00
1 parent 73c91eacce
commit 29e9cd36a6
3 files changed
+55 -2

No files matched your search

+15 -2
View File
@@ -630,8 +630,13 @@ def source_name(kind, arg):
# ── memory and mails (pure: the tests drive this directly) ──────────────────────────────────────────────────────────
def mem_image(it):
"""The image as the memory names it: a DooPlex pin of an image the catalog also ships is its own entry (R-1333)."""
return ("dooplex:" if it.get("group") == "dooplex" else "") + it["image"]
def adv_key(it, a):
return "%s|%s" % (it["image"], a["id"])
return "%s|%s" % (mem_image(it), a["id"])
def label(it):
@@ -649,9 +654,17 @@ def plan(items, state, today, seed=False):
open_now, checked_images = {}, set()
for it in items:
if not it.get("not_checked"):
checked_images.add(it["image"])
checked_images.add(mem_image(it))
for a in it.get("advisories", []):
open_now[adv_key(it, a)] = (it, a)
# A memory written before R-1333 keyed a DooPlex notice by the image alone: copy it to the DooPlex key, so the first
# night after the change mails nothing again; the shared key stays only while a catalog pin still has the notice.
for k in [k for k in open_now if k.startswith("dooplex:") and k not in known]:
shared = k[len("dooplex:"):]
if shared in known:
known[k] = dict(known[shared])
if shared not in open_now:
del known[shared]
# A seed that could not read some pins (no key: rotation; or a source down) leaves them PENDING: their first real
# read is recorded silently too, so the first nights after a seed are no mail storm. A pin added later is never
# pending, so its advisories mail normally.