fix(station): Zugangsdatenpruefung entpackt die Abbilder und prueft beide Rettungssaetze

Die Pruefung des Bildsatzes "ohne-wlan" suchte den Klartext der Werte aus
config.local.cmake in der Abbilddatei, wie sie auf der Platte liegt. Der Rumpf
eines Abbilds ist aber gzip (ab Byte 56, die letzten 512 Byte sind Trailer bzw.
Signatur) - im komprimierten Strom steht der Klartext nicht. Derselbe
Pruefkoerper meldete deshalb auch fuer den Satz aus build/, der die
Zugangsdaten nachweislich traegt, "sauber". Er haette nie Alarm geschlagen,
egal was im Bildsatz lag.

Jetzt wird jedes Abbild entpackt und erst dann durchsucht - im Rumpf wie in den
Rohbytes, denn Kopf, Trailer und Beidateien wie manifest.json sind
unkomprimiert. Ein Abbild, das sich nicht entpacken laesst, ist ein Fehler und
bricht den Deploy ab, statt ungeprueft durchzugehen; das gilt auch fuer einen
abgeschnittenen gzip-Strom, den decompressobj sonst stillschweigend annimmt.

Zweitens hing der ganze Block an der Existenz von migrate-nocreds/. Der
Rettungssatz rescue-nocreds/ lief ungeprueft durch, obwohl er der gefaehrlichere
von beiden ist: er geht ohne Signatur an die Lampe. Geprueft werden jetzt beide
Saetze "ohne-wlan"; migrate-wlan/ und rescue-wlan/ bleiben ungeprueft, die
sollen die Daten enthalten.

Die Meldung nennt jetzt Zahl der Abbilder und der Saetze statt der Zahl der
Suchbegriffe. Gefundene Werte werden weiterhin nie ausgegeben, nur ihr Name und
die Datei.

Die Testattrappen haben jetzt den echten Aufbau (56 Byte Kopf, gzip-Rumpf,
512 Byte Trailer) und tragen die Zugangsdaten im komprimierten Rumpf; die
Gegenproben-Hilfe tree_contains entpackt mit. Neu sind Szenarien fuer
Zugangsdaten im Rettungssatz, die Abgrenzung zu rescue-wlan, ein nicht
entpackbares Abbild und den Meldungstext. Szenario 33, 39, 41 und 42 sind gegen
den alten Pruefkoerper rot.
This commit is contained in:
Michael Sauer
2026-09-23 23:55:23 +02:00
parent bd467b5e52
commit d7810c6cff
2 changed files with 312 additions and 41 deletions
+81 -12
View File
@@ -329,46 +329,115 @@ fi
# Bedieners unwiderruflich in fremde Lampen wandert - der teure, nicht rueckholbare
# Fehler. Lieber ein abgebrochener Deploy.
#
# Geprueft wird VOR dem Uebertragen, es ist also noch nichts beim Pi angekommen. Nur
# dieser eine Satz: migrate-wlan/ SOLL die Zugangsdaten enthalten, das ist sein Zweck.
# Der Pruefkoerper ist bis auf die Meldung derselbe wie in tools/release.sh.
if [ -d "$STAGE/migrate-nocreds" ]; then
echo "== Bildsatz 'ohne-wlan' auf Zugangsdaten pruefen =="
# Geprueft wird VOR dem Uebertragen, es ist also noch nichts beim Pi angekommen.
# Geprueft werden BEIDE Saetze "ohne-wlan" - der Migrationssatz migrate-nocreds/ UND der
# Rettungssatz rescue-nocreds/. Der Rettungssatz ist dabei der gefaehrlichere von beiden:
# er geht ohne Signatur an die Lampe. Frueher hing dieser ganze Block allein an
# migrate-nocreds/, der Rettungssatz lief ungeprueft durch.
# migrate-wlan/ und rescue-wlan/ bleiben ungeprueft: die SOLLEN die Zugangsdaten
# enthalten, das ist ihr Zweck.
#
# Gesucht wird im ENTPACKTEN Rumpf, nicht in der Datei, wie sie auf der Platte liegt:
# jedes Abbild (_ota.img wie die signierten _prod.img/_dev.img) ist 56 Byte Kopf, dann
# ein gzip-Strom, dann 512 Byte Trailer bzw. Signatur. Im komprimierten Strom steht der
# Klartext des Passworts nicht - eine Suche im Rohbyteinhalt meldete deshalb IMMER
# "sauber", auch fuer den Satz aus build/, der die Zugangsdaten nachweislich traegt. Die
# Pruefung hat bis hierhin also nie irgendetwas geprueft.
#
# Ein Abbild, das sich nicht entpacken laesst, ist ein FEHLER und bricht ab - nicht
# "dann eben ungeprueft durchlassen". Genau diese Nachsicht waere der Weg, auf dem das
# Passwort am Ende doch hinausgeht.
#
# Der Pruefkoerper in tools/release.sh ist verwandt, aber nicht mehr identisch: dort
# wird ein fertiges Archiv aus lauter verschiedenen Dateiarten geprueft, hier ein
# Bildsatz aus Firmware-Abbildern.
# Als if-Zweige und nicht als "[ -d … ] && CHECK_SETS+=(…)": unter "set -e" waere eine
# solche Zeile, wenn das Verzeichnis fehlt, ein fehlgeschlagenes letztes Kommando und
# risse den Deploy ab.
declare -a CHECK_SETS=()
if [ -d "$STAGE/migrate-nocreds" ]; then CHECK_SETS+=(migrate-nocreds); fi
if [ -d "$STAGE/rescue-nocreds" ]; then CHECK_SETS+=(rescue-nocreds); fi
if [ ${#CHECK_SETS[@]} -gt 0 ]; then
echo "== Bildsaetze 'ohne-wlan' auf Zugangsdaten pruefen =="
LOCAL="$ROOT/config.local.cmake"
if [ -f "$LOCAL" ]; then
python3 - "$STAGE/migrate-nocreds" "$LOCAL" <<'CHECKPY' || exit 1
python3 - "$LOCAL" "$STAGE" "${CHECK_SETS[@]}" <<'CHECKPY' || exit 1
import os
import re
import sys
import zlib
stage, local = sys.argv[1], sys.argv[2]
local, stage, sets = sys.argv[1], sys.argv[2], sys.argv[3:]
text = open(local, encoding="utf-8", errors="replace").read()
# Nur die drei Werte, die wirklich geheim sind. NINLIGHT_AP_PASSWORD und der
# Vorgabetoken stehen oeffentlich im Repo (README, Bruno) - sie hier mitzupruefen
# haette nur falsche Alarme erzeugt.
SECRETS = ("NINLIGHT_WIFI_SSID", "NINLIGHT_WIFI_PASSWORD", "NINLIGHT_TOKEN")
PUBLIC = {"", "ninlight", "aabbccddeeff0011223344", "ninlight-recovery", "change-me-please"}
# Aufbau eines Abbilds: 56 Byte Kopf (vom wm_tool erzeugt), dann der gzip-Strom mit der
# eigentlichen Firmware, dann 512 Byte ninlight-Trailer bzw. RSA-Signatur.
HEADER_LEN = 56
TRAILER_LEN = 512
needles = []
for name in SECRETS:
m = re.search(r'^\s*set\s*\(\s*%s\s+"([^"]*)"' % name, text, re.M)
if m and m.group(1) not in PUBLIC:
needles.append((name, m.group(1).encode()))
def unpack(raw):
"""Entpackt den gzip-Rumpf eines Abbilds. Wirft bei allem, was nicht passt."""
if len(raw) <= HEADER_LEN + TRAILER_LEN:
raise ValueError("Datei ist kuerzer als Kopf + Trailer")
dec = zlib.decompressobj(zlib.MAX_WBITS | 16)
body = dec.decompress(raw[HEADER_LEN:-TRAILER_LEN])
# Ohne diese Abfrage waere ein ABGESCHNITTENER gzip-Strom kein Fehler: decompressobj
# liefert stillschweigend so viel, wie es lesen konnte. Der ungelesene Rest bliebe
# ungeprueft - und genau dort koennte das Passwort stehen.
if not dec.eof:
raise ValueError("gzip-Strom ist unvollstaendig")
return body
hits = []
for root, _dirs, files in os.walk(stage):
broken = []
images = 0
for image_set in sets:
root_dir = os.path.join(stage, image_set)
for root, _dirs, files in os.walk(root_dir):
for fn in sorted(files):
path = os.path.join(root, fn)
rel = os.path.relpath(path, stage)
with open(path, "rb") as fh:
data = fh.read()
raw = fh.read()
# Durchsucht werden beide Sichten: der entpackte Rumpf (dort steht die
# Firmware) UND die Rohbytes (Kopf und Trailer werden nicht komprimiert,
# und Beidateien wie manifest.json gibt es nur roh).
views = [raw]
if fn.endswith(".img"):
images += 1
try:
views.append(unpack(raw))
except Exception as exc:
broken.append("%s (%s)" % (rel, exc))
continue
for name, needle in needles:
if needle in data:
hits.append("%s in %s" % (name, os.path.relpath(path, stage)))
if any(needle in view for view in views):
# NUR der Name des Wertes und die Datei - niemals der Wert selbst.
hits.append("%s in %s" % (name, rel))
if broken:
sys.stderr.write(
"Fehler: Abbild im Bildsatz 'ohne-wlan' laesst sich nicht entpacken, also auch "
"nicht auf Zugangsdaten pruefen - nichts uebertragen:\n "
+ "\n ".join(broken) + "\n")
sys.exit(1)
if hits:
sys.stderr.write(
"Fehler: Zugangsdaten aus config.local.cmake im Bildsatz 'ohne-wlan' gefunden, "
"nichts uebertragen:\n " + "\n ".join(hits) + "\n")
sys.exit(1)
print(" sauber (%d geprueft)" % len(needles))
print(" sauber (%d Abbilder in %d Saetzen geprueft: %s)"
% (images, len(sets), ", ".join(sets)))
CHECKPY
else
echo " uebersprungen - $LOCAL nicht vorhanden" >&2
+227 -25
View File
@@ -217,18 +217,63 @@ if with_dev:
PY
}
# Erzeugt eine gueltige rohe _gz.img-Attrappe: >= 1024 Byte, durch 4 teilbar, ohne
# NINL-Trailer - genau das, was nl_sign.py als Eingabe akzeptiert.
make_gz_file() {
local path=$1 fill=${2:-A}
python3 -c "
# Baut eine Attrappe mit dem AUFBAU EINES ECHTEN ABBILDS: 56 Byte Kopf, dann ein
# gzip-Strom mit dem Rumpf, dann ggf. 512 Byte Trailer. Genau diesen Aufbau setzt die
# Zugangsdatenpruefung in deploy.sh voraus - eine unkomprimierte Attrappe wuerde die
# Pruefung an einem Abbild vorfuehren, das es so nie gibt, und damit nichts belegen.
# $1 = Pfad, $2 = Fuellzeichen (auch die 56 Kopfbytes, damit Szenarien den Quellbaum
# am Dateianfang wiedererkennen), $3 = "creds", wenn die Zugangsdaten in den
# KOMPRIMIERTEN Rumpf eingebaut werden sollen, $4 = "ota" fuer den NINL-Trailer.
# Ergebnis ohne Trailer ist eine gueltige Eingabe fuer nl_sign.py: >= 1024 Byte, durch 4
# teilbar, kein NINL im Signaturblock.
make_image_file() {
local path=$1 fill=${2:-A} with_creds=${3:-} kind=${4:-gz}
SSID="$SECRET_SSID" PASS="$SECRET_PASS" TOKEN="$SECRET_TOKEN" \
python3 - "$path" "$fill" "$with_creds" "$kind" <<'PY'
import gzip
import os
import sys
path, fill = sys.argv[1], sys.argv[2].encode()
data = bytearray((fill * 2048)[:2048])
data[-512:-508] = b'\\x00\\x00\\x00\\x00' # nie zufaellig wie 'NINL' aussehen
with open(path, 'wb') as fh:
path, fill, with_creds, kind = sys.argv[1], sys.argv[2].encode(), sys.argv[3], sys.argv[4]
body = bytearray((fill * 4096)[:4096])
if with_creds == "creds":
creds = (os.environ["SSID"] + "\0" + os.environ["PASS"] + "\0"
+ os.environ["TOKEN"] + "\0").encode()
body[64:64 + len(creds)] = creds
# mtime=0: sonst haengt die Dateigroesse an der Uhrzeit und Szenarien, die Groessen
# vergleichen, waeren launisch.
packed = gzip.compress(bytes(body), mtime=0)
data = bytearray((fill * 56)[:56] + packed)
# Auffuellen auf die Mindestlaenge und auf ein Vielfaches von 4 - der Rumpf eines echten
# Abbilds ist deutlich groesser als der komprimierte Testrumpf. Die Nullen hinter dem
# gzip-Strom stoeren das Entpacken nicht.
while len(data) < 1024 or len(data) % 4 != 0:
data.append(0)
if kind == "ota":
trailer = bytearray(b"\xff" * 512)
trailer[0:4] = b"NINL"
trailer[4] = 1
data += trailer
with open(path, "wb") as fh:
fh.write(bytes(data))
" "$path" "$fill"
PY
}
# Erzeugt eine gueltige rohe _gz.img-Attrappe (ohne Trailer, ohne Zugangsdaten).
make_gz_file() {
make_image_file "$1" "${2:-A}" "" gz
}
# Erzeugt eine _ota.img-Attrappe im echten Aufbau, ohne Zugangsdaten.
make_ota_image() {
make_image_file "$1" "${2:-A}" "" ota
}
# Dasselbe, aber mit den Zugangsdaten im komprimierten Rumpf.
make_ota_image_with_creds() {
make_image_file "$1" "${2:-A}" creds ota
}
# Erzeugt eine _ota.img-Attrappe mit ANDEREM Inhalt als das zugehoerige _gz.img und
@@ -270,33 +315,36 @@ EOF
# test_release.sh: nur weil er die Werte tatsaechlich einbaut, sagt ein bestandener
# Pruefdurchlauf ueberhaupt etwas.
make_gz_file_with_creds() {
local path=$1 fill=${2:-A}
SSID="$SECRET_SSID" PASS="$SECRET_PASS" TOKEN="$SECRET_TOKEN" python3 -c "
import os, sys
path, fill = sys.argv[1], sys.argv[2].encode()
creds = (os.environ['SSID'] + '\\0' + os.environ['PASS'] + '\\0'
+ os.environ['TOKEN'] + '\\0').encode()
data = bytearray((fill * 2048)[:2048])
data[64:64 + len(creds)] = creds
data[-512:-508] = b'\\x00\\x00\\x00\\x00' # nie zufaellig wie 'NINL' aussehen
with open(path, 'wb') as fh:
fh.write(bytes(data))
" "$path" "$fill"
make_image_file "$1" "${2:-A}" creds gz
}
# Sucht eine Zeichenkette BINAER in jeder Datei unterhalb eines Verzeichnisses.
# Rueckgabe 0 = gefunden. Wie contains_secrets() in test_release.sh.
# Sucht eine Zeichenkette in jeder Datei unterhalb eines Verzeichnisses - roh UND im
# ENTPACKTEN Rumpf jedes .img (56 Byte Kopf, gzip, 512 Byte Trailer/Signatur).
# Rueckgabe 0 = gefunden. Wie contains_secrets() in test_release.sh, nur dass die
# Gegenproben seit den echt aufgebauten Attrappen sonst nichts mehr faenden: im
# komprimierten Strom steht der Klartext nicht. Genau das war der Fehler in deploy.sh.
tree_contains() {
local dir=$1 needle=$2
NEEDLE="$needle" python3 - "$dir" <<'PY'
import os
import sys
import zlib
needle = os.environ["NEEDLE"].encode()
for root, _dirs, files in os.walk(sys.argv[1]):
for fn in files:
with open(os.path.join(root, fn), "rb") as fh:
if needle in fh.read():
raw = fh.read()
if needle in raw:
sys.exit(0)
if not fn.endswith(".img") or len(raw) <= 56 + 512:
continue
for body in (raw[56:-512], raw[56:]):
try:
clear = zlib.decompressobj(zlib.MAX_WBITS | 16).decompress(body)
except Exception:
continue
if needle in clear:
sys.exit(0)
sys.exit(1)
PY
@@ -1559,6 +1607,160 @@ else
fail "das neue Rettungsimage fehlt: $OUT"
fi
echo
echo "=== Szenario 39: Zugangsdaten im RETTUNGSSATZ 'ohne-wlan' brechen den Deploy ab ==="
# Der Rettungssatz ist der gefaehrlichere der beiden Saetze "ohne-wlan": er geht ohne
# Signatur an die Lampe. Trotzdem hing die Zugangsdatenpruefung frueher allein an
# migrate-nocreds/ - rescue-nocreds/ lief ungeprueft durch. Hier ohne Schluessel, damit
# es NUR um den Rettungssatz geht: es entsteht gar kein migrate-nocreds/.
SANDBOX="$TMPROOT/s39"; STUBBIN="$TMPROOT/bin39"; LOG="$TMPROOT/s39.log"; SANDBOX_STATE="$TMPROOT/s39-state"
setup_sandbox "$SANDBOX"
setup_stubbin "$STUBBIN" "$LOG"
write_local_cmake "$SANDBOX"
mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo"
make_ota_image "$SANDBOX/build/foo/ninlight_foo_ota.img" W
# Der undichte Rettungsabzug: die Zugangsdaten stecken im KOMPRIMIERTEN Rumpf.
make_ota_image_with_creds "$SANDBOX/build-nocreds/foo/ninlight_foo_ota.img" N
# GEGENPROBE: ohne sie waere dieses Szenario wertlos - der "Compiler" dieser Suite muss
# die Zugangsdaten wirklich einbauen.
if tree_contains "$SANDBOX/build-nocreds" "$SECRET_PASS"; then
pass "Gegenprobe: der Rettungsabzug enthaelt das WLAN-Passwort wirklich (im gzip-Rumpf)"
else
fail "Gegenprobe fehlgeschlagen - der Rettungsabzug enthaelt gar keine Zugangsdaten, Szenario 39 waere wertlos"
fi
# Und die Gegenprobe zur Gegenprobe: ROH ist nichts davon zu finden. Genau deshalb hat
# die alte Pruefung nie etwas gemeldet.
if grep -q "$SECRET_PASS" "$SANDBOX/build-nocreds/foo/ninlight_foo_ota.img" 2>/dev/null; then
fail "das Passwort steht im Klartext in der Datei - die Attrappe ist nicht komprimiert und belegt nichts"
else
pass "Gegenprobe: roh ist im Abbild nichts zu finden - nur entpackt"
fi
mkdir -p "$SANDBOX_STATE/opt/ninlight-station/images/rescue-nocreds"
echo "alter-sauberer-abzug" >"$SANDBOX_STATE/opt/ninlight-station/images/rescue-nocreds/ninlight_foo_ota.img"
: >"$LOG"
set +e
OUT=$(run_deploy "$SANDBOX" pi-test foo 2>&1)
RC=$?
set -e
[ "$RC" -ne 0 ] && pass "Exit-Code != 0 ($RC)" || fail "Exit-Code war 0 - der undichte Rettungssatz waere ausgeliefert worden: $OUT"
echo "$OUT" | grep -q "NINLIGHT_WIFI_PASSWORD" \
&& pass "Fehlermeldung nennt den betroffenen Schluessel" \
|| fail "Fehlermeldung nennt den Schluessel nicht: $OUT"
echo "$OUT" | grep -q "ninlight_foo_ota.img" \
&& pass "Fehlermeldung nennt die betroffene Datei" \
|| fail "Fehlermeldung nennt die Datei nicht: $OUT"
if echo "$OUT" | grep -q "$SECRET_PASS"; then
fail "die Meldung gibt den WERT aus - sie darf nur den Namen nennen"
else
pass "die Meldung nennt nur den Namen, nie den Wert"
fi
if ! grep -q "STUB ssh\|STUB rsync\|STUB scp" "$LOG"; then
pass "kein ssh/rsync/scp aufgerufen - nichts wurde uebertragen"
else
fail "es wurde trotz gefundener Zugangsdaten uebertragen: $(cat "$LOG")"
fi
[ "$(cat "$SANDBOX_STATE/opt/ninlight-station/images/rescue-nocreds/ninlight_foo_ota.img")" = "alter-sauberer-abzug" ] \
&& pass "der Bestand auf dem Pi blieb unangetastet" \
|| fail "der Bestand auf dem Pi wurde veraendert"
if ! tree_contains "$SANDBOX_STATE" "$SECRET_PASS"; then
pass "kein Zugangsdatum hat es in die Sandbox-Zielablage geschafft"
else
fail "Zugangsdaten sind trotz Abbruch beim Ziel gelandet"
fi
echo
echo "=== Szenario 40 (Abgrenzung zu 39): rescue-wlan DARF die Zugangsdaten tragen ==="
# Dieselbe Abgrenzung wie Szenario 34 fuer die Migration: der Satz 'mit-wlan' SOLL die
# Zugangsdaten enthalten. Eine Pruefung, die auch ihn abweist, waere unbenutzbar.
SANDBOX="$TMPROOT/s40"; STUBBIN="$TMPROOT/bin40"; LOG="$TMPROOT/s40.log"; SANDBOX_STATE="$TMPROOT/s40-state"
setup_sandbox "$SANDBOX"
setup_stubbin "$STUBBIN" "$LOG"
write_local_cmake "$SANDBOX"
mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo"
make_ota_image_with_creds "$SANDBOX/build/foo/ninlight_foo_ota.img" W
make_ota_image "$SANDBOX/build-nocreds/foo/ninlight_foo_ota.img" N
: >"$LOG"
set +e
OUT=$(run_deploy "$SANDBOX" pi-test foo 2>&1)
RC=$?
set -e
[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC - die Pruefung weist einen sauberen Rettungssatz ab: $OUT"
[ -f "$SANDBOX_STATE/opt/ninlight-station/images/rescue-wlan/ninlight_foo_ota.img" ] \
&& pass "rescue-wlan kam trotz einkompilierter Zugangsdaten an" \
|| fail "rescue-wlan wurde faelschlich mit abgewiesen: $OUT"
[ -f "$SANDBOX_STATE/opt/ninlight-station/images/rescue-nocreds/ninlight_foo_ota.img" ] \
&& pass "der saubere Rettungssatz 'ohne-wlan' kam an" \
|| fail "der saubere Rettungssatz 'ohne-wlan' fehlt: $OUT"
if tree_contains "$SANDBOX_STATE/opt/ninlight-station/images/rescue-wlan" "$SECRET_PASS"; then
pass "Gegenprobe: rescue-wlan traegt die Zugangsdaten wirklich - die Pruefung haette anschlagen KOENNEN"
else
fail "Gegenprobe fehlgeschlagen - rescue-wlan traegt gar keine Zugangsdaten, die Abgrenzung waere wertlos"
fi
if ! tree_contains "$SANDBOX_STATE/opt/ninlight-station/images/rescue-nocreds" "$SECRET_PASS"; then
pass "und der Rettungssatz 'ohne-wlan' auf dem Pi ist frei davon"
else
fail "der Rettungssatz 'ohne-wlan' auf dem Pi enthaelt Zugangsdaten"
fi
echo
echo "=== Szenario 41: ein Abbild, das sich nicht entpacken laesst, bricht den Deploy ab ==="
# Nachsicht an dieser Stelle ("dann eben ungeprueft durchlassen") ist der Weg, auf dem
# das Passwort am Ende doch hinausgeht: ein Abbild in einem anderen Format ist
# genau das, was niemand geprueft hat.
SANDBOX="$TMPROOT/s41"; STUBBIN="$TMPROOT/bin41"; LOG="$TMPROOT/s41.log"; SANDBOX_STATE="$TMPROOT/s41-state"
setup_sandbox "$SANDBOX"
setup_stubbin "$STUBBIN" "$LOG"
write_local_cmake "$SANDBOX"
mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo"
make_ota_image "$SANDBOX/build/foo/ninlight_foo_ota.img" W
# Kein gzip-Rumpf: aufgebaut wie ein Abbild (56 Byte Kopf, 512 Byte Trailer), aber der
# Rumpf dazwischen ist unkomprimiert - die alte, unkomprimierte Attrappe dieser Suite.
make_ota_file "$SANDBOX/build-nocreds/foo/ninlight_foo_ota.img"
: >"$LOG"
set +e
OUT=$(run_deploy "$SANDBOX" pi-test foo 2>&1)
RC=$?
set -e
[ "$RC" -ne 0 ] && pass "Exit-Code != 0 ($RC)" || fail "Exit-Code war 0 - ein ungepruefter Rettungssatz waere ausgeliefert worden: $OUT"
echo "$OUT" | grep -q "nicht entpacken" \
&& pass "Fehlermeldung nennt den Grund" \
|| fail "keine Fehlermeldung zum Entpacken: $OUT"
echo "$OUT" | grep -q "rescue-nocreds/ninlight_foo_ota.img" \
&& pass "Fehlermeldung nennt die betroffene Datei" \
|| fail "Fehlermeldung nennt die Datei nicht: $OUT"
if ! grep -q "STUB ssh\|STUB rsync\|STUB scp" "$LOG"; then
pass "kein ssh/rsync/scp aufgerufen - nichts wurde uebertragen"
else
fail "es wurde trotz ungepruefbarem Abbild uebertragen: $(cat "$LOG")"
fi
echo
echo "=== Szenario 42: die Meldung sagt, wie viele Abbilder in wie vielen Saetzen geprueft wurden ==="
# Die alte Meldung "sauber (2 geprueft)" zaehlte die SUCHBEGRIFFE und las sich wie zwei
# Dateien - sie haette genauso gelautet, wenn kein einziges Abbild angefasst worden waere.
SANDBOX="$TMPROOT/s42"; STUBBIN="$TMPROOT/bin42"; LOG="$TMPROOT/s42.log"; SANDBOX_STATE="$TMPROOT/s42-state"
setup_sandbox "$SANDBOX"
setup_stubbin "$STUBBIN" "$LOG"
write_local_cmake "$SANDBOX"
mkdir -p "$SANDBOX/build/foo" "$SANDBOX/build-nocreds/foo"
make_gz_file "$SANDBOX/build/foo/ninlight_foo_gz.img" W
make_ota_image "$SANDBOX/build/foo/ninlight_foo_ota.img" W
make_gz_file "$SANDBOX/build-nocreds/foo/ninlight_foo_gz.img" N
make_ota_image "$SANDBOX/build-nocreds/foo/ninlight_foo_ota.img" N
: >"$LOG"
set +e
OUT=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1)
RC=$?
set -e
[ "$RC" -eq 0 ] && pass "Exit-Code 0" || fail "Exit-Code $RC: $OUT"
# migrate-nocreds/: foo_prod.img + foo_dev.img, rescue-nocreds/: ninlight_foo_ota.img.
echo "$OUT" | grep -q "sauber (3 Abbilder in 2 Saetzen geprueft" \
&& pass "die Meldung nennt Zahl der Abbilder UND der Saetze" \
|| fail "die Meldung sagt nicht, was geprueft wurde: $OUT"
echo "$OUT" | grep -q "migrate-nocreds, rescue-nocreds" \
&& pass "die Meldung nennt beide geprueften Saetze beim Namen" \
|| fail "die Meldung nennt die Saetze nicht: $OUT"
echo
if [ "$FAILED" -eq 0 ]; then
echo "Alle Tests bestanden."