diff --git a/REPORT-security-app-updates-2026-10-27.md b/REPORT-security-app-updates-2026-10-27.md new file mode 100644 index 00000000..fcbc64a0 --- /dev/null +++ b/REPORT-security-app-updates-2026-10-27.md @@ -0,0 +1,53 @@ +# REPORT — security app updates, 2026-10-27 (day, attended) + +> A sibling of `REPORT.md` (the release session of the same day owns that file). + +## In plain words + +- **Real notices:** the security watch flagged 38 pins. Read by hand: 33 real, in 32 apps plus 3 box programs; 5 apps + were false alarms (`audits/security-app-updates-2026-10-27/TRIAGE.md`). +- **Out tonight by the quick lane:** 14 apps, in three batches, each on your yes — Rallly, RomM, code-server, Outline, + Vikunja; n8n, PrivateBin, Docmost, Grafana; Homebox, Mealie, Audiobookshelf, BentoPDF, Uptime Kuma. Wishlist and + Bookstack had no notice id and went as normal steps. Immich (the lowest item) was not done. +- **Big steps:** Jellyfin 10.11 → 12.4 is done (bench, box, and the undo proven). Ten are left, a row each. +- **No-fix picks:** four questions for you (STATUS D56–D59); none changed a template. +- **Box programs released:** yes — controller 0.346.1 (Traefik v3.7.16, file manager 1.5.8-stable, cloudflared + 2026.10.4), global floor 15:50:15Z on your yes. + +## Catalog commits (app-catalog-felhom.eu) and CI by commit + +| Commit | What | CI job | +|---|---|---| +| `06e603e` | batch 1 + wishlist; Rallly/Jellyfin fixtures; the privileges gate skips `steps/*.felhom.yml` | 2422 success | +| `7b0071e` | batch 2 + bookstack | 2425 success | +| `65a6b11` | batch 3; Homebox/Mealie fixtures | 2432 (see the final report for the read-back) | +| `540f185` | Jellyfin 10.11.11 → 12.4 | (see the final report) | + +## Controller + +`v0.346.1` = the release commit `b1b9be3` (v0.346.0) + one commit `5ba1b86` (tag `v0.346.1`; the same commit is on +`main`). CI 2427/2428 success. Image `gitea.dooplex.hu/admin/felhom-controller:0.346.1` +`sha256:b6b1e5488d31cb0b6596228fd6bf50900692ab0798d5da858159db67e8440190`, built from `ed3927f` (= `5ba1b86` + the +release session's REPORT.md only). MinAgent 0.158.1. + +## Proofs + +- The lane: a negative control first (busybox as TO → `failed`, undone). 14 walks `proven`, 12 with the seed read back. +- Part 5: 9202 (rc.1) and Tester 1, demo-hp, demo-felhom (per-box floors 15:41Z): all 29 public names redirect to + https; the file manager signs in (Tester 1, 9202: listing 200, 401 without a cookie); an app container cannot reach + it on any box (R-1281 probe with a control); the tunnel gap 4–5 s per box, the Traefik restart 7–20 s; a 30-minute + watch after the global floor was clean; the per-box floors cleared. +- Part 3: Jellyfin's undo — 12.4 migrated the database, failed on purpose, the box undid it, 10.11 signed the user in. + +## Register + +Rows at the task's start 221; now 224. This task opened 15 (R-1310, R-1311, R-1313–R-1325, R-1314 closed the same day) +and closed 3 (R-1304, R-1306, R-1314); R-1305 NARROWED. The release session closed ten and opened one in the same time. + +## Teardown + +Machine: 9202 back on the live catalog, controller 0.346.1, its saved config copy removed (identical); the walks +removed every app they installed; the drill mirror in 9202's volume stays (harmless, as after B23). Bench 9401 +stopped (as found). Host: nothing. Hub: per-box floors cleared; the global floor 0.346.1 stays (intended). + +Evidence: `documentation/audits/security-app-updates-2026-10-27/`. diff --git a/STATUS.md b/STATUS.md index b47a858a..a3dfd1da 100644 --- a/STATUS.md +++ b/STATUS.md @@ -2,7 +2,7 @@ **Ready for Tester 2? Yes — tell the friend go (hand-over list: `documentation/pilot/TESTER2-handover.md`).** -**Updated 2026-10-27 (afternoon).** Released on your yes: hub 0.173.0, agent 0.167.0 (not vouched yet), controller 0.346.0 (floor 15:37 CET). The line test on a seeding box failed first (qBittorrent keeps sending 14 s after the hold) and works since the same-day fix: 94.5 Mbit/s, the limit put back exactly. demo-hp's clean-up window removed 223 old copies, no alarm. +**Updated 2026-10-27 (evening).** On your yes: 14 apps got a tested security step, and Wishlist, Bookstack and Jellyfin a normal step; boxes take them tonight. Every box runs controller 0.346.1 with the new Traefik, file manager and cloudflared. Ten apps need a bigger step, a row each (R-1311, R-1313, R-1315–R-1322); no household runs them. ## What works - **The Plex attack (10-17 → 10-23) did not reach Felhom's secrets** (R-1241). @@ -32,6 +32,10 @@ Newest first. CC's pick is in the „Pick" column. Each row also has its full te | # | Row | Question | Pick (why) | The other option costs | If you do nothing | Blocked | |---|---|---|---|---|---|---| +| D59 | R-1313 | Hide Wanderer from new installs until its big step is done? Any server on the internet can overwrite its records, with no login. | **Yes, hide it** (no box runs it; a new install is open from day one) | leave it offered: a new install is open to that attack | it stays offered and open | — | +| D58 | R-1311 | Termix: wait for its big step, or strip the forged-address header now? A stranger can guess passwords faster than the limit allows. | **Wait** (stripping it lets a stranger lock the family out, R-775) | strip now: wrong guesses can lock the family out | we wait | — | +| D57 | R-1315 | Gitea: keep repository import on? A signed-in family member can make the box fetch a home-network address; no fix exists. | **Keep it on** (sign-up is closed; judge again in the big step) | turn it off: no import from GitHub | it stays on | — | +| D56 | R-785 | SparkyFitness: leave it offered while two flaws have no fix? Both need a family account or a leaked photo link. | **Leave it offered** (take the fix with its big step) | hide it: it leaves the catalog for an unknown time | it stays offered | — | | D38 | R-1235 | Put the newest golden (637 MB) into the nightly release copy on ep0 too? | **Yes** (a new install after a DooPlex loss needs it; it fits under the 3 GB cap) | leave it out: after a loss, no new install until a golden is rebuilt and vouched | the same | — | | D37 | R-1236 | Raise Gitea's memory limit from 1 GiB to 2 GiB? It was killed for memory 4 times in 17 days, once during today's release. | **Yes** (DooPlex has the RAM; a DooPlex change, so yours) | keep 1 GiB: a restart in release hours fails box updates and CI for a minute | the same | — | | D9 | R-1062 | Split the N100's USB drive in two, for multi-drive tests? | **No — keep one drive** (the lived-in box keeps its nightly whole-box backup) | Split: the box loses its local whole-box backup, or you buy a second drive first | it stays one drive; multi-drive tests stay on Tester 1 | — | diff --git a/documentation/audits/security-app-updates-2026-10-27/PICKS.md b/documentation/audits/security-app-updates-2026-10-27/PICKS.md index dad0f4b6..5dda0f56 100644 --- a/documentation/audits/security-app-updates-2026-10-27/PICKS.md +++ b/documentation/audits/security-app-updates-2026-10-27/PICKS.md @@ -4,7 +4,7 @@ No template was changed for any of these. Each pick: the question, CC's pick fir happens if nobody decides. Fleet fact for all of them (hub 2026-10-27): **no box runs any of these apps**; the risk is a NEW install. -## 1. SparkyFitness — two notices, no fix released (GHSA-p8cf-g8rv-gvrq, GHSA-cmmf-53wf-r5gv) +## 1. SparkyFitness (STATUS D56) — two notices, no fix released (GHSA-p8cf-g8rv-gvrq, GHSA-cmmf-53wf-r5gv) What a stranger can do with our template: nothing without an account or a leaked photo URL. p8cf needs a household account (it overwrites another member's Withings link); cmmf needs the exact URL of an owner-only photo. A third notice @@ -16,7 +16,7 @@ account (it overwrites another member's Withings link); cmmf needs the exact URL the catalog for an unknown time. - **If you do nothing:** A. -## 2. Gitea — GHSA-rqhx-647v-wx32: SSRF through a redirect in repository migration (the upstream patch was reverted) +## 2. Gitea (STATUS D57) — GHSA-rqhx-647v-wx32: SSRF through a redirect in repository migration (the upstream patch was reverted) What a stranger can do: nothing without an account (sign-up is closed twice in our template). A household account can make the box fetch an internal address through „New migration". @@ -27,7 +27,7 @@ make the box fetch an internal address through „New migration". repository from GitHub any more. - **If you do nothing:** A. -## 3. Termix — GHSA-7j7m-85p4-2cpm: a stranger skips the login rate limit with a forged X-Forwarded-For (R-1311) +## 3. Termix (STATUS D58) — GHSA-7j7m-85p4-2cpm: a stranger skips the login rate limit with a forged X-Forwarded-For (R-1311) The fix is Termix's big step (2.8 → 2.9.2 → 26.10.5, two hops). @@ -37,9 +37,9 @@ The fix is Termix's big step (2.8 → 2.9.2 → 26.10.5, two hops). R-775). - **If you do nothing:** A. -## 4. The big steps a stranger can use before they are done (Wanderer, Jellyfin, Gitea, Termix) +## 4. The big steps (STATUS D59) a stranger can use before they are done (Wanderer, Jellyfin, Gitea, Termix) -Wanderer's critical notice (GHSA-fprh-3pp9-68m9: any remote ActivityPub server overwrites records, no login, federation +(Jellyfin's big step went LIVE the same day, catalog `540f185`, so this pick is now about Wanderer alone.) Wanderer's critical notice (GHSA-fprh-3pp9-68m9: any remote ActivityPub server overwrites records, no login, federation has no off switch) is the worst open item. Jellyfin (GHSA-hw73, a file read with no login), Gitea (two notices with no login) and Termix (above) follow. diff --git a/documentation/audits/security-app-updates-2026-10-27/TRIAGE.md b/documentation/audits/security-app-updates-2026-10-27/TRIAGE.md index d29a6917..61125f85 100644 --- a/documentation/audits/security-app-updates-2026-10-27/TRIAGE.md +++ b/documentation/audits/security-app-updates-2026-10-27/TRIAGE.md @@ -18,44 +18,44 @@ lane only when the notes between pin and target show no file conversion, no new | # | App | Pin → TARGET | Real / total | Worst real (login?) | Reachable on a box | Id(s) the step carries | Fixture | State | |---|---|---|---|---|---|---|---|---| -| 1 | rallly | 4.15.3 → 4.16.3 | 1+3 rel / 1+4 | critical RCE (none) | yes, after setup | GHSA-gmq4-f8j5-8w8q | yes | pending | -| 2 | romm | 5.3.1 → 5.4.1 | 6 / 6 | high: unauth WebSocket scan deletes media (none) | yes | GHSA-722m-w9p2-xv2j, -q7fg-…, -7jw3-…, -9q83-… | yes | pending | -| 3 | code-server | 4.129.0 → 4.143.1 (lscr.io) | 1 / 1 | high: Node Brotli before sign-in (none, inferred) | yes, no gate | CVE-2026-111497 (Node Oct 2026) | no (seed none) | pending | -| 4 | outline | 1.10.1 → 1.12.0 | 8 / 13 | high: unpublished doc keeps its share (none) | yes | GHSA-rq53-m97j-f2pg, -97vv-…, -w8vg-… | yes | pending | -| 5 | vikunja | 2.6.0 → 2.7.1 | 42 / 45 | high (any user; m687 needs the victim's browser) | own login (setup+signup gates) | GHSA-m687-p538-r5hp, -hq66-…, -5q33-… | yes | pending | -| 6 | homebox | 0.26.2 → 0.27.1 | 14 / 15 | high: group takeover (any user) | own login; unauth register notices blocked by signup_block | GHSA-jm28-2xm3-m6m5 … | yes | pending | -| 7 | n8n | 2.42.1 → 2.44.7 | 12 / 13 | high (a workflow builder) | own login | GHSA-m7gv-…, -mp7q-…, -wvjj-… | yes | pending | -| 8 | privatebin | 2.0.6 → 2.1.0 | 2 / 2 | medium: fake pop-up (none) | yes, public by design | GHSA-w873-3ghv-hmh6 | yes | pending | -| 9 | docmost | 0.96.0 → 0.97.0 | 1 rel / 1 | unsure: Node 26 + fastify/socket.io (pre-auth surfaces) | yes, after setup | CVE-2026-111497 (Node Oct 2026) | yes | pending | -| 10 | grafana | 13.2.3 → 13.2.4 | 1 rel / 5 | medium: CVE-2026-15577 (read-only user) | own login | CVE-2026-15577 | yes | pending | -| 11 | mealie | v3.28.0 → v3.29.0 | 1 / 1 | medium (any user, 2+ groups) | own login | GHSA-99wf-32rc-2767 | yes | pending | -| 12 | audiobookshelf | 2.37.1 → 2.37.2 | 1 rel / 1 | Node runtime; low in practice | own login | CVE-2026-111497 (Node Oct 2026) | yes | pending | -| 13 | bentopdf | v2.8.6 → v2.8.10 | 1+2 rel / 3+2 | low known (cx8x, a crafted workflow file); v2.8.10's two GHSAs still private | yes, no login by design | GHSA-cx8x-7rrr-r9x8, -v77x-…, -qch9-… | no (seed none) | pending | -| 14 | uptime-kuma | 2.5.5 → 2.5.8 | 1 rel / 2+3 | admin-only code execution (GHSA-v2p3, unpublished) | admin login | GHSA-v2p3-6f5h-6hhf | yes | pending — CAVEAT: 2.5.8 drops TLS on the TCP-port monitor | -| 15 | immich | v3.2.4 → v3.3.5 (server + ml) | 0 GHSA + 1 rel | Node + CA bundle runtime only | own login | CVE-2026-111497 (Node Oct 2026) | yes | optional — lowest | +| 1 | rallly | 4.15.3 → 4.16.3 | 1+3 rel / 1+4 | critical RCE (none) | yes, after setup | GHSA-gmq4-f8j5-8w8q | yes | LIVE `06e603e` (fixture fixed: 4.15 code-only sign-in) | +| 2 | romm | 5.3.1 → 5.4.1 | 6 / 6 | high: unauth WebSocket scan deletes media (none) | yes | GHSA-722m-w9p2-xv2j, -q7fg-…, -7jw3-…, -9q83-… | yes | LIVE `06e603e` | +| 3 | code-server | 4.129.0 → 4.143.1 (lscr.io) | 1 / 1 | high: Node Brotli before sign-in (none, inferred) | yes, no gate | CVE-2026-111497 (Node Oct 2026) | no (seed none) | LIVE `06e603e` (seed: none) | +| 4 | outline | 1.10.1 → 1.12.0 | 8 / 13 | high: unpublished doc keeps its share (none) | yes | GHSA-rq53-m97j-f2pg, -97vv-…, -w8vg-… | yes | LIVE `06e603e` | +| 5 | vikunja | 2.6.0 → 2.7.1 | 42 / 45 | high (any user; m687 needs the victim's browser) | own login (setup+signup gates) | GHSA-m687-p538-r5hp, -hq66-…, -5q33-… | yes | LIVE `06e603e` | +| 6 | homebox | 0.26.2 → 0.27.1 | 14 / 15 | high: group takeover (any user) | own login; unauth register notices blocked by signup_block | GHSA-jm28-2xm3-m6m5 … | yes | LIVE `65a6b11` (fixture fixed: 0.26 entities API) | +| 7 | n8n | 2.42.1 → 2.44.7 | 12 / 13 | high (a workflow builder) | own login | GHSA-m7gv-…, -mp7q-…, -wvjj-… | yes | LIVE `7b0071e` | +| 8 | privatebin | 2.0.6 → 2.1.0 | 2 / 2 | medium: fake pop-up (none) | yes, public by design | GHSA-w873-3ghv-hmh6 | yes | LIVE `7b0071e` | +| 9 | docmost | 0.96.0 → 0.97.0 | 1 rel / 1 | unsure: Node 26 + fastify/socket.io (pre-auth surfaces) | yes, after setup | CVE-2026-111497 (Node Oct 2026) | yes | LIVE `7b0071e` | +| 10 | grafana | 13.2.3 → 13.2.4 | 1 rel / 5 | medium: CVE-2026-15577 (read-only user) | own login | CVE-2026-15577 | yes | LIVE `7b0071e` | +| 11 | mealie | v3.28.0 → v3.29.0 | 1 / 1 | medium (any user, 2+ groups) | own login | GHSA-99wf-32rc-2767 | yes | LIVE `65a6b11` (fixture fixed: generated admin password) | +| 12 | audiobookshelf | 2.37.1 → 2.37.2 | 1 rel / 1 | Node runtime; low in practice | own login | CVE-2026-111497 (Node Oct 2026) | yes | LIVE `65a6b11` | +| 13 | bentopdf | v2.8.6 → v2.8.10 | 1+2 rel / 3+2 | low known (cx8x, a crafted workflow file); v2.8.10's two GHSAs still private | yes, no login by design | GHSA-cx8x-7rrr-r9x8, -v77x-…, -qch9-… | no (seed none) | LIVE `65a6b11` (seed: none — no data) | +| 14 | uptime-kuma | 2.5.5 → 2.5.7 (not 2.5.8: it drops TLS on the TCP-port monitor) | 1 rel / 2+3 | admin-only code execution (GHSA-v2p3, unpublished) | admin login | GHSA-v2p3-6f5h-6hhf | yes | LIVE `65a6b11` (2.5.7) | +| 15 | immich | v3.2.4 → v3.3.5 (server + ml) | 0 GHSA + 1 rel | Node + CA bundle runtime only | own login | CVE-2026-111497 (Node Oct 2026) | yes | NOT DONE — R-1322 | ## Lane A′ — same main version but NOT the lane | App | Pin → TARGET | Why not the lane | Path | |---|---|---|---| -| wishlist | v0.67.1 → v0.69.0 | **critical, no login**: `/forgot-password` returns the reset token in its own response, SMTP or not (PR #818) — but **no GHSA/CVE is published**, and the lane's authority is a public notice id | a NORMAL proven step: bench (fixture exists) + box walk, `--write-ladder` | -| bookstack | 26.09.1 → 26.09.2 (lscr.io) | a real security release (two authorisation leaks between signed-in users) with **no CVE/GHSA id** | a normal proven step (fixture exists), after the lane batch | +| wishlist | v0.67.1 → v0.69.0 | **critical, no login**: `/forgot-password` returns the reset token in its own response, SMTP or not (PR #818) — but **no GHSA/CVE is published**, and the lane's authority is a public notice id | LIVE `06e603e` — a NORMAL step: bench 9401 + box 9202 proven | +| bookstack | 26.09.1 → 26.09.2 (lscr.io) | a real security release (two authorisation leaks between signed-in users) with **no CVE/GHSA id** | LIVE `7b0071e` — a NORMAL step: bench 9401 + box 9202 proven | ## Lane B — a new main version, or a breaking change inside it (Part 3) -| App | Pin → TARGET | Worst real (login?) | Why big | Runs on a box today | -|---|---|---|---|---| -| wanderer | v0.20.0 → v0.21.2 (web + db) | **critical: any remote ActivityPub server overwrites records (none)**; federation has no off switch | new REQUIRED `POCKETBASE_PROXY_SECRET` on both services; 0.21.1 migration shortens rich text, no downgrade | no | -| jellyfin | 10.11.11 → 12.4 | critical 6828 (admin after setup); hw73 file read with **no login** (medium) | 10 → 12; DB conversion with no rollback; `/emby` + legacy auth removed | no | -| gokapi | v1.9.6 → v2.2.4 | high 3c22 is self-XSS for the only account | 1 → 2; user system, no downgrade; our first-boot config seed must be re-captured | no | -| seerr | jellyseerr 2.7.3 → `ghcr.io/seerr-team/seerr:v3.5.1` | high rc4w (stranger registers, Plex setups only), mc6w path traversal → RCE (needs a hostile media server) | 2 → 3; new image name; runs as UID 1000 → `/app/config` chown; config migration | no | -| karakeep | 0.33.2 + chrome 151 → 0.34.1 + chrome 154.0.8037.57-r1 | high: two CISA-KEV V8 bugs in the crawler's Chromium (`--no-sandbox`), via content the household saves | auth library swap (everyone signed out, e-mails lowercased); chrome rebuilt on full Chrome — memory limit and healthcheck to re-measure | no | -| gitea | 1.27.3 → 28.2.0 | critical 7cq7 / 6pv5 (any user); c3cr, hf55 need no login | 1.28 renamed 28.0.0 — no 1.27.4 exists; 28.0 routes git traffic through a new proxy with egress settings | no | -| paperless-ngx | 2.20.15 → 3.3.4 | low (2jhj, needs a setting we do not set) — **not reachable** | 2 → 3: encryption + API v1 removed, search index rebuilt | YES (4 test boxes) — not urgent | -| homepage | v1.13.2 → v2.7.0 | medium; family_gate, only with a hand-written credentialed widget | 1 → 2: auth rework | no | -| termix | 2.8.0 → 2.9.2 → 26.10.5 | high; **7j7m: the login rate limit is bypassed by a spoofed X-Forwarded-For (no login)** | two hops (2.9 must start once); calendar versions; guacd dropped | no | -| tandoor | 2.6.15 → 2.7.2 | high: 5f74 XXE file read, 69q3 (any user) | 2.7.0 removes settings and an endpoint and rewrites the login pages (our probe path, unsure); 2.7.0/2.7.1 break invite links with sign-up off → straight to 2.7.2 | no | -| sparkyfitness | v0.17.3 (both images) | medium p8cf (any user), cmmf (a leaked photo URL) | **no fix named in any release**; 0.17 → 1.x is R-785's big step anyway | no | +| App | Pin → TARGET | Worst real (login?) | Why big | Runs on a box today | State | +|---|---|---|---|---|---| +| wanderer | v0.20.0 → v0.21.2 (web + db) | **critical: any remote ActivityPub server overwrites records (none)**; federation has no off switch | new REQUIRED `POCKETBASE_PROXY_SECRET` on both services; 0.21.1 migration shortens rich text, no downgrade | no | OPEN — R-1313 (+ pick 4) | +| jellyfin | 10.11.11 → 12.4 | critical 6828 (admin after setup); hw73 file read with **no login** (medium) | 10 → 12; DB conversion with no rollback; `/emby` + legacy auth removed | no | LIVE `540f185` — bench + box + the undo proven | +| gokapi | v1.9.6 → v2.2.4 | high 3c22 is self-XSS for the only account | 1 → 2; user system, no downgrade; our first-boot config seed must be re-captured | no | OPEN — R-1316 | +| seerr | jellyseerr 2.7.3 → `ghcr.io/seerr-team/seerr:v3.5.1` | high rc4w (stranger registers, Plex setups only), mc6w path traversal → RCE (needs a hostile media server) | 2 → 3; new image name; runs as UID 1000 → `/app/config` chown; config migration | no | OPEN — R-1317 | +| karakeep | 0.33.2 + chrome 151 → 0.34.1 + chrome 154.0.8037.57-r1 | high: two CISA-KEV V8 bugs in the crawler's Chromium (`--no-sandbox`), via content the household saves | auth library swap (everyone signed out, e-mails lowercased); chrome rebuilt on full Chrome — memory limit and healthcheck to re-measure | no | OPEN — R-1318 | +| gitea | 1.27.3 → 28.2.0 | critical 7cq7 / 6pv5 (any user); c3cr, hf55 need no login | 1.28 renamed 28.0.0 — no 1.27.4 exists; 28.0 routes git traffic through a new proxy with egress settings | no | OPEN — R-1315 | +| paperless-ngx | 2.20.15 → 3.3.4 | low (2jhj, needs a setting we do not set) — **not reachable** | 2 → 3: encryption + API v1 removed, search index rebuilt | YES (4 test boxes) — not urgent | OPEN — R-1319 | +| homepage | v1.13.2 → v2.7.0 | medium; family_gate, only with a hand-written credentialed widget | 1 → 2: auth rework | no | OPEN — R-1320 | +| termix | 2.8.0 → 2.9.2 → 26.10.5 | high; **7j7m: the login rate limit is bypassed by a spoofed X-Forwarded-For (no login)** | two hops (2.9 must start once); calendar versions; guacd dropped | no | OPEN — R-1311 | +| tandoor | 2.6.15 → 2.7.2 | high: 5f74 XXE file read, 69q3 (any user) | 2.7.0 removes settings and an endpoint and rewrites the login pages (our probe path, unsure); 2.7.0/2.7.1 break invite links with sign-up off → straight to 2.7.2 | no | OPEN — R-1321 | +| sparkyfitness | v0.17.3 (both images) | medium p8cf (any user), cmmf (a leaked photo URL) | **no fix named in any release**; 0.17 → 1.x is R-785's big step anyway | no | OPEN — R-785 (no fix; pick 1) | ## No fix exists (Part 4) @@ -89,6 +89,6 @@ lane only when the notes between pin and target show no file conversion, no new | Program | Pin → TARGET | Real | Breaking (notes read) | |---|---|---|---| -| traefik | v3.7.13 → v3.7.16 | 12 notices; none reaches our config (docker + file providers, no FastProxy, no NTLM backend, no k8s) — R-1304 | migration guide v3.7.14–16: k8s providers' names, OTel histograms, **CONNECT rejected with 501**; none touches us | -| file manager | 1.5.6-stable → **1.5.8-stable** | GHSA-qx86-4v5r-26g5 (TOTP re-enrol), GHSA-3846-gh75-gp3m (upload share) — R-1305 | 1.5.7-stable is superseded: it broke existing TOTP logins; 1.5.8-stable supersedes it | -| cloudflared | 2026.10.3 → 2026.10.4 | VULN-176088 „Allowlist debug endpoints on the metrics server"; our metrics port is not reachable from outside — low | none | +| traefik | v3.7.13 → v3.7.16 (LIVE: controller v0.346.1) | 12 notices; none reaches our config (docker + file providers, no FastProxy, no NTLM backend, no k8s) — R-1304 | migration guide v3.7.14–16: k8s providers' names, OTel histograms, **CONNECT rejected with 501**; none touches us | +| file manager | 1.5.6-stable → **1.5.8-stable** (LIVE: controller v0.346.1, global floor 15:50Z) | GHSA-qx86-4v5r-26g5 (TOTP re-enrol), GHSA-3846-gh75-gp3m (upload share) — R-1305 | 1.5.7-stable is superseded: it broke existing TOTP logins; 1.5.8-stable supersedes it | +| cloudflared | 2026.10.3 → 2026.10.4 (LIVE: controller v0.346.1) | VULN-176088 „Allowlist debug endpoints on the metrics server"; our metrics port is not reachable from outside — low | none | diff --git a/documentation/audits/security-app-updates-2026-10-27/part3/tools/undo_proof.py b/documentation/audits/security-app-updates-2026-10-27/part3/tools/undo_proof.py new file mode 100644 index 00000000..ff98f44c --- /dev/null +++ b/documentation/audits/security-app-updates-2026-10-27/part3/tools/undo_proof.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python3 +"""undo_proof.py — Part 3: the big step's UNDO, proven on scratch 9202 through the product's own guarded Update. + +Jellyfin 10.11.11 -> 12.4 converts its database with no way back (the bench's abort put the old image back on the +converted data: „the app started but the seeded data was gone"). So the box's undo must bring back the DATA, not only +the old image. The proof: + 1. the drill's jellyfin at FROM (a temporary drill commit), install, seed the first user, read it back (C1); + 2. a temporary drill commit moves the image to 12.4 AND makes its checks fail (compose healthcheck `false`, the + .felhom.yml probe on a path that does not exist) — so 12.4 starts, converts the database, and the Update fails + in its check; + 3. press the guarded Update: the final phase must be `undone`; while it verifies, 12.4's own log is saved (the + conversion is seen, not assumed); + 4. after the undo: the running image is 10.11.11 and the seeded user signs in (a wrong password refused); + 5. the two temporary drill commits are reverted, the app removed. +usage: SC= TARGET=9202 EV= python3 undo_proof.py +""" +import json, os, re, subprocess, sys, threading, time +sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts") +import box_walk as w +import upgrade_fixtures_box28 as f28 + +APP, SUB = "jellyfin", "jellyfin" +FROM, TO = "jellyfin/jellyfin:10.11.11", "jellyfin/jellyfin:12.4" +D = w.DRILL +EV = os.environ["EV"] +os.makedirs(EV, exist_ok=True) +rec = {"app": APP, "from": FROM, "to": TO, "started": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())} + + +def git(*a): + return subprocess.run(["git", "-C", D] + list(a), capture_output=True, text=True, timeout=120) + + +def drill_commit(msg, edit): + git("pull", "-q", "--rebase", "origin", "main") + comp, fy = f"{D}/templates/{APP}/docker-compose.yml", f"{D}/templates/{APP}/.felhom.yml" + c, y = open(comp).read(), open(fy).read() + c, y = edit(c, y) + open(comp, "w").write(c) + open(fy, "w").write(y) + git("add", f"templates/{APP}") + git("commit", "-q", "-m", msg) + r = git("push", "-q", "origin", "main") + h = git("rev-parse", "--short=12", "HEAD").stdout.strip() + w.say(f" drill commit {h}: {msg} (push rc={r.returncode})") + w.refresh_box_mirror() + return h + + +def finish(verdict, why): + rec["verdict"], rec["why"] = verdict, why + rec["log"] = w.LOG + json.dump(rec, open(os.path.join(EV, "undo-proof-jellyfin.json"), "w"), indent=2) + w.say(f"RESULT {verdict} — {why}") + + +w.login() +head0 = git("rev-parse", "HEAD").stdout.strip() +rec["drill_head_before"] = head0 +fx = f28.FIXTURES28[APP] +commits = [] +try: + def to_from(c, y): + return c.replace(f"image: {TO}", f"image: {FROM}"), y + commits.append(drill_commit("UNDO-PROOF setup: jellyfin at FROM (temporary, reverted at the end)", to_from)) + if w.stack(APP).get("deployed"): + w.remove(APP) + w.sync_rescan(APP, FROM, tries=40, delay=10) + if not w.deploy(APP, SUB): + finish("inconclusive", "the install at FROM did not complete"); raise SystemExit(1) + w.wait_app(SUB, "/System/Info/Public", want=("200",), tries=60) + tok = fx.seed(w, SUB, w.say) + if not tok or not fx.verify(w, SUB, tok, w.say): + finish("inconclusive", "C1: the seed did not read back before the update"); raise SystemExit(1) + + def failing_to(c, y): + c = c.replace(f"image: {FROM}", f"image: {TO}") + c = c.replace('test: ["CMD", "curl", "-f", "http://127.0.0.1:8096/health"]', 'test: ["CMD", "false"]') + y = y.replace('path: "/health"', 'path: "/undo-proof-no-such-path"', 1) + return c, y + commits.append(drill_commit("UNDO-PROOF: jellyfin 12.4 whose checks fail (temporary, reverted at the end)", failing_to)) + w.sync_rescan(APP, TO, tries=40, delay=10) + + seen = {"lines": []} + stop = threading.Event() + + def watch(): + while not stop.is_set(): + out = w.guest("docker inspect -f '{{.Config.Image}}' jellyfin 2>/dev/null; " + "docker logs --tail 400 jellyfin 2>&1 | grep -iE 'migrat|Jellyfin version|database' | tail -40") + if "12.4" in out.split("\n")[0]: + seen["lines"] = out.strip().split("\n") + time.sleep(5) + th = threading.Thread(target=watch, daemon=True) + th.start() + rec["update"] = u = w.press_update(APP, poll=1.0, cap_s=1800) + stop.set() + th.join(timeout=30) + rec["to_log_while_verifying"] = seen["lines"][-40:] + time.sleep(15) + rec["observables_after"] = obs = w.observables(APP) + running = w.guest("docker inspect -f '{{.Config.Image}}' jellyfin 2>/dev/null").strip() + rec["running_image_after"] = running + if u.get("final_phase") != "undone": + finish("failed", f"the update ended {u.get('final_phase')!r}, not 'undone'"); raise SystemExit(1) + if running != FROM: + finish("failed", f"after the undo the running image is {running!r}, not {FROM}"); raise SystemExit(1) + if not any(re.search(r"migrat", l, re.I) for l in seen["lines"]): + w.say(" note: no migration line was captured from 12.4 while it verified") + rec["seed_after_undo"] = ok = fx.verify(w, SUB, tok, w.say) + if not ok: + finish("failed", "the undo put the old image back but the seeded user does not sign in"); raise SystemExit(1) + finish("proven", "12.4 started on the data and failed its check; the box undid it: 10.11.11 runs again and the " + "seeded user signs in (a wrong password refused)") +finally: + if w.stack(APP).get("deployed"): + w.remove(APP) + for h in reversed(commits): + git("pull", "-q", "--rebase", "origin", "main") + r = git("revert", "--no-edit", h) + w.say(f" drill revert {h}: rc={r.returncode}") + if commits: + git("push", "-q", "origin", "main") + w.refresh_box_mirror() + rec["drill_head_after"] = git("rev-parse", "HEAD").stdout.strip() + rec["drill_tree_equal_to_before"] = git("diff", "--quiet", head0, "HEAD", "--", f"templates/{APP}").returncode == 0 + w.say(f" drill templates/{APP} equal to before: {rec['drill_tree_equal_to_before']}") + json.dump({k: v for k, v in rec.items() if k != "log"} | {"log": w.LOG}, + open(os.path.join(EV, "undo-proof-jellyfin.json"), "w"), indent=2) diff --git a/documentation/audits/security-app-updates-2026-10-27/part3/undo-proof-jellyfin.json b/documentation/audits/security-app-updates-2026-10-27/part3/undo-proof-jellyfin.json new file mode 100644 index 00000000..d6bc58dc --- /dev/null +++ b/documentation/audits/security-app-updates-2026-10-27/part3/undo-proof-jellyfin.json @@ -0,0 +1,178 @@ +{ + "app": "jellyfin", + "from": "jellyfin/jellyfin:10.11.11", + "to": "jellyfin/jellyfin:12.4", + "started": "2026-10-27T16:05:59Z", + "drill_head_before": "a241806d00ecf4020741594fd22792b4d87c9069", + "update": { + "accepted": true, + "http": "202", + "phases": [ + { + "t": 0.0, + "phase": "backing-up", + "label": "Biztons\u00e1gi ment\u00e9s k\u00e9sz\u00fcl a friss\u00edt\u00e9s el\u0151tt\u2026", + "updating": true, + "error": null, + "hold": null + }, + { + "t": 3.1, + "phase": "safety-dump", + "label": "Adatb\u00e1zis pillanatk\u00e9p\u2026", + "updating": true, + "error": null, + "hold": null + }, + { + "t": 4.1, + "phase": "pulling", + "label": "\u00daj verzi\u00f3 let\u00f6lt\u00e9se\u2026", + "updating": true, + "error": null, + "hold": null + }, + { + "t": 36.9, + "phase": "copying", + "label": "Az adatok m\u00e1sol\u00e1sa a friss\u00edt\u00e9s el\u0151tt\u2026", + "updating": true, + "error": null, + "hold": null + }, + { + "t": 40.0, + "phase": "starting", + "label": "Ind\u00edt\u00e1s az \u00faj verzi\u00f3val\u2026", + "updating": true, + "error": null, + "hold": null + }, + { + "t": 41.0, + "phase": "verifying", + "label": "M\u0171k\u00f6d\u00e9s ellen\u0151rz\u00e9se\u2026", + "updating": true, + "error": null, + "hold": null + }, + { + "t": 343.5, + "phase": "undoing", + "label": "Vissza\u00e1ll\u00edt\u00e1s az el\u0151z\u0151 v\u00e1ltozatra\u2026", + "updating": true, + "error": null, + "hold": null + }, + { + "t": 362.1, + "phase": "undone", + "label": "Vissza\u00e1ll\u00edtva az el\u0151z\u0151 v\u00e1ltozatra", + "updating": false, + "error": null, + "hold": null + } + ], + "duration_s": 362.1, + "final_phase": "undone", + "update_error": null, + "hold_reason": null, + "state": "running" + }, + "to_log_while_verifying": [ + "[17:15:41] [INF] [9] Jellyfin.Server.Migrations.Routines.CleanupOrphanedExternalData: Skipping chapter image cleanup; root /config/metadata/library does not exist", + "[17:15:41] [INF] [9] InternalCodeMigration: Migration 20260525010000_CleanupOrphanedExternalData was successfully applied", + "[17:15:41] [INF] [9] InternalCodeMigration: Perform migration 20260610120000_RefreshCleanNamesAndValues", + "[17:15:41] [INF] [9] Jellyfin.Server.Migrations.Routines.RefreshCleanNamesAndValues: Refreshing CleanName for 4 library items", + "[17:15:41] [INF] [9] Jellyfin.Server.Migrations.Routines.RefreshCleanNamesAndValues: Processed: 0/4 - Updated: 0 - Time: 00:00:00.0216493", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.RefreshCleanNamesAndValues: Refreshed CleanName for 1 out of 4 items in 00:00:00.3289760", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.RefreshCleanNamesAndValues: Refreshing CleanValue for 0 item values", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.RefreshCleanNamesAndValues: Processed: 0/0 - Updated: 0 - Time: 00:00:00.0111801", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.RefreshCleanNamesAndValues: Refreshed CleanValue for 0 out of 0 item values in 00:00:00.0189360", + "[17:15:42] [INF] [9] InternalCodeMigration: Migration 20260610120000_RefreshCleanNamesAndValues was successfully applied", + "[17:15:42] [INF] [9] InternalCodeMigration: Perform migration 20260722120000_RefreshForcedSortNames", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.RefreshForcedSortNames: Refreshing SortName for 0 library items with a forced sort name", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.RefreshForcedSortNames: Processed: 0/0 - Updated: 0 - Time: 00:00:00.0047795", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.RefreshForcedSortNames: Refreshed SortName for 0 out of 0 items in 00:00:00.0221081", + "[17:15:42] [INF] [9] InternalCodeMigration: Migration 20260722120000_RefreshForcedSortNames was successfully applied", + "[17:15:42] [INF] [9] InternalCodeMigration: Perform migration 20260729120000_RestorePlaylistChildrenFromMetadata", + "[17:15:42] [INF] [9] InternalCodeMigration: Migration 20260729120000_RestorePlaylistChildrenFromMetadata was successfully applied", + "[17:15:42] [INF] [9] InternalCodeMigration: Perform migration 20260821120000_RecomputeSeriesPresentationKey", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.RecomputeSeriesPresentationKey: Recomputing presentation unique key for 0 series", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.RecomputeSeriesPresentationKey: Recomputed presentation unique key for 0 of 0 series and 0 seasons in 00:00:00.0060964", + "[17:15:42] [INF] [9] InternalCodeMigration: Migration 20260821120000_RecomputeSeriesPresentationKey was successfully applied", + "[17:15:42] [INF] [9] InternalCodeMigration: Perform migration 20260825200000_ConsolidateLocalizedUserViews", + "[17:15:42] [INF] [9] InternalCodeMigration: Migration 20260825200000_ConsolidateLocalizedUserViews was successfully applied", + "[17:15:42] [INF] [9] InternalCodeMigration: Perform migration 20260831100000_EnableLocalSimilarityProviders", + "[17:15:42] [INF] [9] InternalCodeMigration: Migration 20260831100000_EnableLocalSimilarityProviders was successfully applied", + "[17:15:42] [INF] [9] InternalCodeMigration: Perform migration 20260908120000_RepairAlternateVersionLinks", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.RepairAlternateVersionLinks: No alternate version links found, nothing to repair.", + "[17:15:42] [INF] [9] InternalCodeMigration: Migration 20260908120000_RepairAlternateVersionLinks was successfully applied", + "[17:15:42] [INF] [9] InternalCodeMigration: Perform migration 20260911120000_StripEmbeddedLinkedChildren", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.StripEmbeddedLinkedChildren: Dropped dead keys from the serialized data of 2 items", + "[17:15:42] [INF] [9] InternalCodeMigration: Migration 20260911120000_StripEmbeddedLinkedChildren was successfully applied", + "[17:15:42] [INF] [9] InternalCodeMigration: Perform migration 20260912120000_HarmonizeConflictingUserData", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.HarmonizeConflictingUserData: No conflicting user data found.", + "[17:15:42] [INF] [9] InternalCodeMigration: Migration 20260912120000_HarmonizeConflictingUserData was successfully applied", + "[17:15:42] [INF] [9] InternalCodeMigration: Perform migration 20260915120000_MigrateRatingLevels", + "[17:15:42] [INF] [9] Jellyfin.Server.Migrations.Routines.MigrateRatingLevels: Recalculating parental rating levels based on rating string.", + "[17:15:42] [INF] [9] InternalCodeMigration: Migration 20260915120000_MigrateRatingLevels was successfully applied", + "[17:15:42] [INF] [9] Main: Migrations have been applied, optimizing the database... This might take a while", + "[17:15:42] [INF] [9] Jellyfin.Database.Providers.Sqlite.SqliteDatabaseProvider: Not analyzing jellyfin.db, the library holds no items yet", + "[17:15:42] [INF] [9] Jellyfin.Database.Providers.Sqlite.SqliteDatabaseProvider: jellyfin.db optimized successfully!" + ], + "observables_after": { + "pinned_images": { + "jellyfin": "jellyfin/jellyfin:10.11.11" + }, + "installed_images": { + "jellyfin": "jellyfin/jellyfin:10.11.11" + }, + "catalog_images": { + "jellyfin": "jellyfin/jellyfin:12.4" + }, + "live_compose_image_lines": [ + "image: jellyfin/jellyfin:10.11.11" + ], + "docker_inspect": [ + "jellyfin jellyfin/jellyfin:10.11.11 running=true restarts=0" + ] + }, + "running_image_after": "jellyfin/jellyfin:10.11.11", + "seed_after_undo": true, + "verdict": "proven", + "why": "12.4 started on the data and failed its check; the box undid it: 10.11.11 runs again and the seeded user signs in (a wrong password refused)", + "drill_head_after": "6d5751dbdce22938c6c4af7f1bba66c7fd6958bf", + "drill_tree_equal_to_before": true, + "log": [ + "17:06:00 drill commit c99cdeca5363: UNDO-PROOF setup: jellyfin at FROM (temporary, reverted at the end) (push rc=0)", + "17:06:09 [5] the box's drill mirror now at c99cdeca", + "17:13:43 [sync] the badge NEVER caught up to jellyfin/jellyfin:10.11.11 in 454.1s \u2014 catalog_images = None", + "17:13:43 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH']", + "17:13:43 [1] deploy -> 202 {'ok': True, 'message': 'Telep\u00edt\u00e9s elind\u00edtva \u2013 az \u00e1llapot a k\u00e1rty\u00e1n k\u00f6vethet\u0151'}", + "17:14:38 [1] deployed, controller state=running, pinned={'jellyfin': 'jellyfin/jellyfin:10.11.11'}", + "17:14:39 gate: jellyfin is gated \u2014 passed as the household (cookie set)", + "17:14:39 jellyfin: seeded first user drilld1744440", + "17:14:41 jellyfin: sign-in as the seeded user http=200 token=True (wrong password refused: http 401)", + "17:14:42 drill commit 3dcb73dc7679: UNDO-PROOF: jellyfin 12.4 whose checks fail (temporary, reverted at the end) (push rc=0)", + "17:14:50 [5] the box's drill mirror now at 3dcb73dc", + "17:14:55 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Friss\u00edt\u00e9s elindult \u2013 az \u00e1llapot a k\u00e1rty\u00e1n k\u00f6vethet\u0151'}", + "17:14:55 + 0.0s phase=backing-up label=Biztons\u00e1gi ment\u00e9s k\u00e9sz\u00fcl a friss\u00edt\u00e9s el\u0151tt\u2026 err=None hold=None", + "17:14:58 + 3.1s phase=safety-dump label=Adatb\u00e1zis pillanatk\u00e9p\u2026 err=None hold=None", + "17:14:59 + 4.1s phase=pulling label=\u00daj verzi\u00f3 let\u00f6lt\u00e9se\u2026 err=None hold=None", + "17:15:32 + 36.9s phase=copying label=Az adatok m\u00e1sol\u00e1sa a friss\u00edt\u00e9s el\u0151tt\u2026 err=None hold=None", + "17:15:35 + 40.0s phase=starting label=Ind\u00edt\u00e1s az \u00faj verzi\u00f3val\u2026 err=None hold=None", + "17:15:36 + 41.0s phase=verifying label=M\u0171k\u00f6d\u00e9s ellen\u0151rz\u00e9se\u2026 err=None hold=None", + "17:20:39 + 343.5s phase=undoing label=Vissza\u00e1ll\u00edt\u00e1s az el\u0151z\u0151 v\u00e1ltozatra\u2026 err=None hold=None", + "17:20:57 + 362.1s phase=undone label=Vissza\u00e1ll\u00edtva az el\u0151z\u0151 v\u00e1ltozatra err=None hold=None", + "17:21:20 jellyfin: sign-in as the seeded user http=200 token=True (wrong password refused: http 401)", + "17:21:20 RESULT proven \u2014 12.4 started on the data and failed its check; the box undid it: 10.11.11 runs again and the seeded user signs in (a wrong password refused)", + "17:21:21 [X] stop -> 200 {'ok': True, 'message': 'Stack jellyfin stop completed'}", + "17:21:53 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'jellyfin', 'volumes_removed': ['jellyfin_jellyfin_cache', 'jellyfin_jellyfin_config'], 'hdd_paths_removed': [], 'hdd_paths_pre", + "17:22:01 [X] after remove: deployed=False leftovers=''", + "17:22:02 drill revert 3dcb73dc7679: rc=0", + "17:22:02 drill revert c99cdeca5363: rc=0", + "17:22:11 [5] the box's drill mirror now at 6d5751db", + "17:22:11 drill templates/jellyfin equal to before: True" + ] +} \ No newline at end of file diff --git a/documentation/audits/security-app-updates-2026-10-27/part3/undo-proof-jellyfin.txt b/documentation/audits/security-app-updates-2026-10-27/part3/undo-proof-jellyfin.txt new file mode 100644 index 00000000..1a5c0711 --- /dev/null +++ b/documentation/audits/security-app-updates-2026-10-27/part3/undo-proof-jellyfin.txt @@ -0,0 +1,29 @@ +17:06:00 drill commit c99cdeca5363: UNDO-PROOF setup: jellyfin at FROM (temporary, reverted at the end) (push rc=0) +17:06:09 [5] the box's drill mirror now at c99cdeca +17:13:43 [sync] the badge NEVER caught up to jellyfin/jellyfin:10.11.11 in 454.1s — catalog_images = None +17:13:43 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH'] +17:13:43 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'} +17:14:38 [1] deployed, controller state=running, pinned={'jellyfin': 'jellyfin/jellyfin:10.11.11'} +17:14:39 gate: jellyfin is gated — passed as the household (cookie set) +17:14:39 jellyfin: seeded first user drilld1744440 +17:14:41 jellyfin: sign-in as the seeded user http=200 token=True (wrong password refused: http 401) +17:14:42 drill commit 3dcb73dc7679: UNDO-PROOF: jellyfin 12.4 whose checks fail (temporary, reverted at the end) (push rc=0) +17:14:50 [5] the box's drill mirror now at 3dcb73dc +17:14:55 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'} +17:14:55 + 0.0s phase=backing-up label=Biztonsági mentés készül a frissítés előtt… err=None hold=None +17:14:58 + 3.1s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None +17:14:59 + 4.1s phase=pulling label=Új verzió letöltése… err=None hold=None +17:15:32 + 36.9s phase=copying label=Az adatok másolása a frissítés előtt… err=None hold=None +17:15:35 + 40.0s phase=starting label=Indítás az új verzióval… err=None hold=None +17:15:36 + 41.0s phase=verifying label=Működés ellenőrzése… err=None hold=None +17:20:39 + 343.5s phase=undoing label=Visszaállítás az előző változatra… err=None hold=None +17:20:57 + 362.1s phase=undone label=Visszaállítva az előző változatra err=None hold=None +17:21:20 jellyfin: sign-in as the seeded user http=200 token=True (wrong password refused: http 401) +17:21:20 RESULT proven — 12.4 started on the data and failed its check; the box undid it: 10.11.11 runs again and the seeded user signs in (a wrong password refused) +17:21:21 [X] stop -> 200 {'ok': True, 'message': 'Stack jellyfin stop completed'} +17:21:53 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'jellyfin', 'volumes_removed': ['jellyfin_jellyfin_cache', 'jellyfin_jellyfin_config'], 'hdd_paths_removed': [], 'hdd_paths_pre +17:22:01 [X] after remove: deployed=False leftovers='' +17:22:02 drill revert 3dcb73dc7679: rc=0 +17:22:02 drill revert c99cdeca5363: rc=0 +17:22:11 [5] the box's drill mirror now at 6d5751db +17:22:11 drill templates/jellyfin equal to before: True diff --git a/documentation/audits/security-app-updates-2026-10-27/part5/perbox-floors-cleared.txt b/documentation/audits/security-app-updates-2026-10-27/part5/perbox-floors-cleared.txt new file mode 100644 index 00000000..5dbd2cb7 --- /dev/null +++ b/documentation/audits/security-app-updates-2026-10-27/part5/perbox-floors-cleared.txt @@ -0,0 +1,14 @@ +== clear tester-1 2026-10-27T16:25:19Z +HTTP/1.1 303 See Other +Location: /customers/tester-1?flash=floor_set +== clear demo-hp 2026-10-27T16:25:19Z +HTTP/1.1 303 See Other +Location: /customers/demo-hp?flash=floor_set +== clear demo-felhom 2026-10-27T16:25:19Z +HTTP/1.1 303 See Other +Location: /customers/demo-felhom?flash=floor_set +2026/10/27 17:25:19 [INFO] Customer tester-1 controller-version floor override set to "" (declared MinAgent "") +2026/10/27 17:25:19 [INFO] Customer demo-hp controller-version floor override set to "" (declared MinAgent "") +2026/10/27 17:25:20 [INFO] Customer demo-felhom controller-version floor override set to "" (declared MinAgent "") +override marks left: 0 +Tester-2 Tester 2 sajatfelhom.hu DOWN 0.292.0 v0.346.1 ● MANAGED Tester4 Tester4 tester4.test DOWN 0.345.1 v0.346.1 ● MANAGED demo-felhom Demo Ügyfél demo-felhom.eu OK 0.346.1 v0.346.1 MANAGED demo-hp Demo HP enkisfelhom.hu OK 0.346.1 v0.346.1 MANAGED tester-1 Tester 1 enkicsifelhom.hu OK 0.346.1 v0.346.1 MANAGED Felhom Hub 0.17 diff --git a/documentation/audits/security-app-updates-2026-10-27/teardown-9202.txt b/documentation/audits/security-app-updates-2026-10-27/teardown-9202.txt new file mode 100644 index 00000000..775f71c1 --- /dev/null +++ b/documentation/audits/security-app-updates-2026-10-27/teardown-9202.txt @@ -0,0 +1,21 @@ +# teardown 2026-10-27T18:20:43Z +27: sync_interval: 15m +-rw------- 1 root root 1994 Oct 22 14:47 /var/lib/docker/volumes/felhom-controller-data/_data/controller.yaml.pre-seclane1023 +mirror main 6d5751db Revert "UNDO-PROOF setup: jellyfin at FROM (temporary, reverted at the end)" +cache HEAD 540f185 Jellyfin big step 10.11.11 -> 12.4 (felhom.eu R-1314): a normal ladder step, bench + box + the undo proven +https://gitea.dooplex.hu/admin/app-catalog-felhom.eu.git + +# 9202 controller: rc.1 -> 0.346.1 2026-10-27T18:21:18Z +saved-copy-removed-identical +gitea.dooplex.hu/admin/felhom-controller:0.346.1 +felhom-controller gitea.dooplex.hu/admin/felhom-controller:0.346.1 Up 25 seconds (healthy) +filebrowser gtstef/filebrowser:1.5.8-stable Up 3 hours (healthy) +traefik traefik:v3.7.16 Up 3 hours +paperless-webserver ghcr.io/paperless-ngx/paperless-ngx:2.20.15 Up 17 hours (healthy) +paperless-postgres c293117fcecd Up 17 hours (healthy) +paperless-redis f84b0c467801 Up 17 hours (healthy) +immich-server ghcr.io/immich-app/immich-server:v3.2.4 Up 17 hours (healthy) +immich-postgres ghcr.io/immich-app/postgres:16-vectorchord0.4.3-pgvectors0.2.0 Up 17 hours (healthy) +immich-machine-learning ghcr.io/immich-app/immich-machine-learning:v3.2.4 Up 17 hours (healthy) +immich-redis redis:7-alpine Up 17 hours (healthy) +status: stopped diff --git a/documentation/backlog/CLOSED-ITEMS.md b/documentation/backlog/CLOSED-ITEMS.md index 267fb925..353b048f 100644 --- a/documentation/backlog/CLOSED-ITEMS.md +++ b/documentation/backlog/CLOSED-ITEMS.md @@ -26,12 +26,13 @@ --- -## 2026-10-27 (day, attended) — security app updates: two rows closed (full text of the rows: `git show 708a3451:documentation/backlog/OPEN-ITEMS.md`) +## 2026-10-27 (day, attended) — security app updates: three rows closed; R-1314 was filed and closed the same day (full text of the rows: `git show 708a3451:documentation/backlog/OPEN-ITEMS.md`) | Row | What | Closed | Evidence | |---|---|---|---| +| **R-1314** | **Jellyfin 10.11.11: six notices, one a file read with NO login through the legacy HLS routes (GHSA-hw73); the fix is 12.x, a big step with a database conversion that has no way back.** (P2) | CLOSED 2026-10-27 — RELEASED as a NORMAL ladder step 10.11.11 → 12.4 in catalog `540f185` on the operator's yes at the Part 3 STOP: bench 9401 `proven` (its abort put 10.11 on the converted data: the data was gone — the reason the undo must restore the copy); box 9202 `proven` (done in 53 s, the user signs in after, soak clean); **the undo proven on 9202**: 12.4 ran its migrations, failed its check on purpose, the box undid it in 18 s and 10.11.11 signed the seeded user in again. Two stale fixture steps fixed on the way (10.11's wizard needs a GET first; `/Users/Public` lists nobody — the read-back is a sign-in). No box ran Jellyfin. | `audits/security-app-updates-2026-10-27/part3/`, `walks/jellyfin/`, `bench/jellyfin/` | | **R-1304** | **traefik v3.7.13 (our pin) is inside 12 advisories of October 2026, fixed in v3.7.14–v3.7.16** (GHSA-fh26-gfpp-7xxx snicheck, GHSA-hrpp-p4fc-wwf8 ForwardAuth pool, GHSA-53qr-784g-cj35 FastProxy, and nine on the Kubernetes ingress-nginx provider or NTLM/Negotiate backends). Triage from our config: docker + file providers only, no FastProxy, no per-router TLS options, no NTLM backend; ForwardAuth goes to the controller — none seem to apply. Found by the security-watch spike (R-1303). `audits/security-watch-2026-10-26/raw/repoadv-traefik_traefik.txt` (P3) | CLOSED 2026-10-27 — RELEASED: controller v0.346.1 (= v0.346.0 `b1b9be3` + one commit `5ba1b86`, tag `v0.346.1`) pins `traefik:v3.7.16`; test `TestEnsureTraefik_ANewImageRecreatesTraefik` (red first); global floor 15:50:15Z (MinAgent 0.158.1) on the operator's yes; live on Tester 1, demo-hp, demo-felhom (per-box floors, 15:41Z) and 9202: all 29 public names redirect to https; the Traefik restart 7–20 s, the tunnel gap 4–5 s per box. | `audits/security-app-updates-2026-10-27/part5/` | -| **R-1306** | **The first security-watch run: 27 catalog pins carry published notices that affect the version we pin** (89 notices; the list with our version and the fixed versions: `audits/security-watch-2026-10-26/ADVISORIES-2026-10-26.md`). Several fix only in a NEW major (Jellyfin 10.11.11 → 12.0; Gokapi 1.9.6 → 2.x; Seerr 2.7.3 → 3.x); some name no fix yet (SparkyFitness); some are release notes naming a CVE (Grafana, BookStack, PrivateBin …). The seed mailed none of them (R-1303). (P2) | CLOSED 2026-10-27 — WORKED THROUGH: 14 security-fix steps (rallly, romm, code-server, outline, vikunja; n8n, privatebin, docmost, grafana; homebox, mealie, audiobookshelf, bentopdf, uptime-kuma) + two normal steps (wishlist, bookstack) LIVE in catalog `06e603e`, `7b0071e`, `65a6b11`, each on the operator's yes at a batch STOP; 5 NOT-A-FINDING; what is left has its own row: R-1313 wanderer, R-1314 jellyfin, R-1315 gitea, R-1316 gokapi, R-1317 seerr, R-1318 karakeep, R-1319 paperless-ngx, R-1320 homepage, R-1321 tandoor, R-1322 immich, R-1311 termix, R-785 sparkyfitness. | `audits/security-app-updates-2026-10-27/TRIAGE.md` | +| **R-1306** | **The first security-watch run: 27 catalog pins carry published notices that affect the version we pin** (89 notices; the list with our version and the fixed versions: `audits/security-watch-2026-10-26/ADVISORIES-2026-10-26.md`). Several fix only in a NEW major (Jellyfin 10.11.11 → 12.0; Gokapi 1.9.6 → 2.x; Seerr 2.7.3 → 3.x); some name no fix yet (SparkyFitness); some are release notes naming a CVE (Grafana, BookStack, PrivateBin …). The seed mailed none of them (R-1303). (P2) | CLOSED 2026-10-27 — WORKED THROUGH: 14 security-fix steps (rallly, romm, code-server, outline, vikunja; n8n, privatebin, docmost, grafana; homebox, mealie, audiobookshelf, bentopdf, uptime-kuma) + two normal steps (wishlist, bookstack) LIVE in catalog `06e603e`, `7b0071e`, `65a6b11`, each on the operator's yes at a batch STOP; 5 NOT-A-FINDING; Jellyfin's big step done the same day (R-1314, below); what is left has its own row: R-1313 wanderer, R-1315 gitea, R-1316 gokapi, R-1317 seerr, R-1318 karakeep, R-1319 paperless-ngx, R-1320 homepage, R-1321 tandoor, R-1322 immich, R-1311 termix, R-785 sparkyfitness. | `audits/security-app-updates-2026-10-27/TRIAGE.md` | ## 2026-10-27 (attended release block) — ten rows released and closed (full text of the rows: `git show 708a3451:documentation/backlog/OPEN-ITEMS.md`) diff --git a/documentation/backlog/STATUS-HISTORY.md b/documentation/backlog/STATUS-HISTORY.md index 2719be1f..8a54a543 100644 --- a/documentation/backlog/STATUS-HISTORY.md +++ b/documentation/backlog/STATUS-HISTORY.md @@ -5,6 +5,10 @@ Moved here byte for byte on 2026-10-14 (night session), newest first, so that `S **Updated 2026-10-13 (night): demo-hp, demo-felhom and Tester 1 run agent 0.155.0; demo-felhom runs controller 0.310.0, the other two 0.309.0 until the floor is raised. The open-items list is at 127. Reports: `REPORT-poweroff.md`, `REPORT-plugmeter.md`, `REPORT-usenet-2026-10-12.md`, `REPORT-newapps-2026-10-12.md`, `REPORT-disk-evening-2026-10-12.md`, `REPORT-answers-2026-10-12.md`, `REPORT-m3-2026-10-11.md`, `REPORT-redesign-2026-10-10.md`, `REPORT-testers-2026-10-10.md`, `REPORT-register-shrink-2026-10-10.md`, `REPORT-new-apps-2026-10-10.md`, `REPORT-release-2026-10-10.md`, `REPORT-break-the-circle-2026-10-09.md`, `REPORT-dooplex-survival-2026-10-09.md`, `REPORT-day4-2026-10-09.md`.** +## Moved from STATUS on 2026-10-27 (evening) + +**Updated 2026-10-27 (afternoon).** Released on your yes: hub 0.173.0, agent 0.167.0 (not vouched yet), controller 0.346.0 (floor 15:37 CET). The line test on a seeding box failed first (qBittorrent keeps sending 14 s after the hold) and works since the same-day fix: 94.5 Mbit/s, the limit put back exactly. demo-hp's clean-up window removed 223 old copies, no alarm. + ## Moved from STATUS on 2026-10-27 (afternoon) **Updated 2026-10-26 (night).** Saved and proven tonight, not released: kept data never offers a live app's folder; a reinstalled box backs up off-site at once; app-file import keeps links and owners and works again for MariaDB apps; Go 1.27.3. D55's window runs tonight.