Ampel / ampel (push) Successful in 1m1s
Commander: "bitte baue fuer MakeMKV fallback seiten ein."
Anlass war ein echter Ausfall. Am 28.08.2026 gemessen:
https://www.makemkv.com/download/ HTTP 525 (dauerhaft)
https://makemkv.com/download/ HTTP 525
http://www.makemkv.com/download/ HTTP 403
https://forum.makemkv.com/ 200 / 522 / Zeitablauf (wechselnd)
web.archive.org/web/2025id_/...exe 200, 16.432.607 Bytes
525/522 heissen: Cloudflare erreicht den Ursprungsserver nicht. Eine Stoerung
beim Hersteller -- dieselbe, die im Projekt schon einmal jeden Worker-Build
lahmgelegt hat.
## Die Kette
Version: Hersteller-Seite (massgeblich) -> Forum-Ankuendigungen -> Archiv
Datei: eigene Quelle -> Hersteller -> Internet Archive
Der Hersteller zuerst, immer. Das Archiv ist Rueckfallebene, und Rippy nennt
hinterher die Quelle, aus der die Datei kam.
Zwei Feinheiten, beide aus Messungen statt aus dem Kopf:
* Die HOECHSTE Nummer gewinnt, nicht die erste. Antwortet das Forum nicht,
meldet das Archiv einen aelteren Stand (1.18.2 statt 1.18.4) -- "neueste
Fassung" waere dann eine falsche Aussage.
* Ein zweiter Anlauf, mit knapper Zeitgrenze. Drei Abrufe am Forum:
TimeoutError, HTTP 522, dann Erfolg. Ohne Wiederholung fiel die Quelle in
zwei von drei Faellen aus; mit 20-Sekunden-Grenzen dauerte der schlimmste
Fall 46 Sekunden, in denen das Setup eingefroren aussah. Jetzt acht
Sekunden je Versuch, schlimmstenfalls gut zwanzig.
## Warum eine fremde Quelle trotzdem sicher ist
Der naheliegende Schutz geht NICHT: MakeMKV signiert seinen Installer nicht
(Get-AuthenticodeSignature -> NotSigned, an der echten Datei gemessen). Eine
Signaturpruefung waere eine, die immer fehlschlaegt -- schlimmer als keine,
weil sie Sicherheit vortaeuscht.
Geprueft wird die Versions-Ressource, ebenfalls gemessen:
CompanyName GuinpinSoft inc
FileDescription MakeMKV installer
FileVersion v1.18.4
Das ersetzt keine Signatur, faengt aber ab, was hier wirklich droht: eine
Fehlerseite mit .exe-Namen, ein abgebrochener Download, eine falsche Fassung.
Ende zu Ende nachgewiesen, ohne etwas zu installieren:
Version laut Kette: 1.18.4
Hersteller: HTTP 525 -> uebersprungen
Internet Archive: 15,7 MB in 6,1s
Pruefung: BESTANDEN (MakeMKV v1.18.4)
Die Grenze aus KONZEPT.md 6 bleibt: Rippy liefert MakeMKV weiterhin NICHT
mit. Es holt die Datei des Herstellers -- im Rueckfall aus einem Archiv, das
genau diese Datei aufbewahrt.
Ampel lokal: 733 gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
125 lines
4.9 KiB
Python
125 lines
4.9 KiB
Python
"""Was eine Windows-Datei über sich selbst sagt — und warum das hier zählt.
|
|
|
|
## Wozu (28.08.2026)
|
|
|
|
Rippy lädt den MakeMKV-Installer notfalls aus einer Ausweichquelle, weil
|
|
makemkv.com tagelang mit HTTP 525 antwortet. Eine Datei von woanders zu
|
|
holen und ungeprüft auszuführen wäre leichtsinnig.
|
|
|
|
Der naheliegende Schutz — die digitale Signatur — geht NICHT:
|
|
|
|
Get-AuthenticodeSignature Setup_MakeMKV_v1.18.4.exe
|
|
Status: NotSigned
|
|
|
|
Am 28.08.2026 an der echten Datei gemessen: **MakeMKV signiert seinen
|
|
Installer nicht.** Eine Signaturprüfung wäre also eine Prüfung, die immer
|
|
fehlschlägt — schlimmer als keine, weil sie Sicherheit vortäuscht und dann
|
|
den richtigen Weg blockiert.
|
|
|
|
Prüfbar ist die **Versions-Ressource**. An derselben Datei gemessen:
|
|
|
|
CompanyName GuinpinSoft inc
|
|
FileDescription MakeMKV installer
|
|
FileVersion v1.18.4
|
|
ProductName MakeMKV
|
|
|
|
Das ist kein Ersatz für eine Signatur — wer die Datei fälscht, kann auch die
|
|
Ressource fälschen. Aber es fängt zuverlässig ab, was hier wirklich droht:
|
|
eine Fehlerseite, ein umbenanntes Archiv, eine falsche Version, ein
|
|
abgebrochener Download. Und es sagt dem Nutzer, WAS er da bekommen hat.
|
|
"""
|
|
|
|
import os
|
|
|
|
FELDER = ("CompanyName", "FileDescription", "FileVersion", "ProductName",
|
|
"ProductVersion")
|
|
|
|
|
|
def versionsangaben(pfad: str) -> dict:
|
|
"""Die Versions-Ressource einer Windows-Datei. Leer, wenn keine da ist.
|
|
|
|
Wirft nicht: Eine Datei ohne Ressource ist ein gültiges Ergebnis (und
|
|
genau das, was eine Fehlerseite mit `.exe`-Namen hätte).
|
|
"""
|
|
if os.name != "nt" or not os.path.isfile(pfad):
|
|
return {}
|
|
import ctypes
|
|
|
|
try:
|
|
version = ctypes.WinDLL("version")
|
|
groesse = version.GetFileVersionInfoSizeW(pfad, None)
|
|
if not groesse:
|
|
return {}
|
|
puffer = ctypes.create_string_buffer(groesse)
|
|
if not version.GetFileVersionInfoW(pfad, 0, groesse, puffer):
|
|
return {}
|
|
|
|
zeiger = ctypes.c_void_p()
|
|
laenge = ctypes.c_uint()
|
|
if not version.VerQueryValueW(puffer, r"\VarFileInfo\Translation",
|
|
ctypes.byref(zeiger), ctypes.byref(laenge)):
|
|
return {}
|
|
paar = ctypes.cast(zeiger, ctypes.POINTER(ctypes.c_uint16))
|
|
sprache = "%04x%04x" % (paar[0], paar[1])
|
|
|
|
ergebnis = {}
|
|
for feld in FELDER:
|
|
z = ctypes.c_void_p()
|
|
n = ctypes.c_uint()
|
|
pfad_im_block = "\\StringFileInfo\\%s\\%s" % (sprache, feld)
|
|
if version.VerQueryValueW(puffer, pfad_im_block,
|
|
ctypes.byref(z), ctypes.byref(n)) and n.value:
|
|
ergebnis[feld] = ctypes.wstring_at(z.value, n.value - 1).strip()
|
|
return ergebnis
|
|
except (AttributeError, OSError, ValueError):
|
|
return {}
|
|
|
|
|
|
def ist_programm(pfad: str) -> bool:
|
|
"""Fängt die Datei mit `MZ` an? (Das tut jedes Windows-Programm.)
|
|
|
|
Der billigste und wirksamste Test gegen das, was wirklich passiert: eine
|
|
HTML-Fehlerseite, die unter dem Namen `Setup_MakeMKV_v1.18.4.exe`
|
|
gespeichert wurde.
|
|
"""
|
|
try:
|
|
with open(pfad, "rb") as f:
|
|
return f.read(2) == b"MZ"
|
|
except OSError:
|
|
return False
|
|
|
|
|
|
# ── Die Prüfung als reine Funktion ──────────────────────────────────────
|
|
def passt(angaben: dict, firma: str = "", produkt: str = "",
|
|
version: str = "") -> tuple:
|
|
"""Passen die Angaben zu dem, was erwartet wird? (ja/nein, Begründung)
|
|
|
|
Reine Funktion — damit jeder Fall prüfbar ist, ohne eine Datei zu haben.
|
|
Leere Erwartungen werden nicht geprüft: Wer nichts fordert, bekommt kein
|
|
Urteil vorgesetzt.
|
|
"""
|
|
if not angaben:
|
|
return False, "Die Datei hat keine Versionsangaben — das ist kein " \
|
|
"Installationsprogramm, sondern vermutlich eine Fehlerseite."
|
|
|
|
gefunden_firma = angaben.get("CompanyName", "")
|
|
if firma and firma.lower() not in gefunden_firma.lower():
|
|
return False, ("Die Datei stammt laut ihren eigenen Angaben von "
|
|
"%r, erwartet war %r." % (gefunden_firma or "niemandem", firma))
|
|
|
|
text = " ".join(angaben.get(f, "") for f in
|
|
("ProductName", "FileDescription")).lower()
|
|
if produkt and produkt.lower() not in text:
|
|
return False, ("Die Datei bezeichnet sich als %r, erwartet war %r."
|
|
% (text.strip() or "nichts", produkt))
|
|
|
|
if version:
|
|
gefunden = (angaben.get("FileVersion") or
|
|
angaben.get("ProductVersion") or "").lstrip("vV")
|
|
if gefunden and not gefunden.startswith(version):
|
|
return False, ("Die Datei ist Fassung %s, erwartet war %s."
|
|
% (gefunden, version))
|
|
|
|
return True, "%s %s" % (angaben.get("ProductName", "?"),
|
|
angaben.get("FileVersion", "?"))
|