v0.207.0 — R-249: the retrieval passphrase leaves the page body; R-252/R-253: two refusals learn to say what to do
gates / gates (push) Successful in 18s
gates / gates (push) Successful in 18s
R-249. settings_security.html rendered the passphrase into a display:none span behind a Megjelenit button. That toggle stops a browser DRAWING the value and nothing else — the plaintext was in the response body of every render, so a curl of the page returned it. Found by exactly that: it landed in a session transcript while driving the documented rebuild path. The codebase already stated this rule for the recovery code and this page did not follow it (escrow_handlers.go: 'reveal (claim XHR only — R is NEVER templated server-side into HTML)'). The page now carries only HasRetrievalPassword; the value comes from POST /settings/retrieval-password/reveal — CSRF-covered because POST, no-store, and LOGGED as an act, which reading it off the markup never was. The tests assert the RAW RESPONSE BODY. Every test that asked what the customer sees passed while the bytes carried the secret; that is why this survived. Census: the render-then-hide pattern appears twice more — app_info.html (a real per-install app password in a hidden span) and deploy.html. Filed as R-254, NOT fixed here. R-252. A rebuilt box keeps its drives but loses their REGISTRATION. The restore page now states that before the customer presses anything, says the backups and drives are both still there, and links to Tarhely > Meghajtok. Page and resolver ask ONE question — HasRestoreDestination() reads the same GetSchedulableStoragePaths() the scratch resolver reads. R-253. The list promised 'a visszaallitas elobb ujratelepiti' three lines above a refusal that fired BECAUSE the app was not installed. The promise was the wrong half: reconstitution writes to the app's own GetStackHDDPath, which exists only once the CUSTOMER has chosen a drive at deploy time. Auto-reinstalling would mean the product making that choice for them. Copy now says to install first and routes to /stacks/<app>/deploy. Both notices are conditional — a healthy box renders as before, pinned by a test that fails if either becomes unconditional.
This commit is contained in:
@@ -1,3 +1,61 @@
|
||||
## v0.207.0 — a password stops living in the page source, and two refusals learn to say what to do (2026-08-08, R-249/R-252/R-253) — MinAgent 0.127.0
|
||||
|
||||
Three items the fifth walk exposed by passing. None of them touches the recovery path it proved; all
|
||||
three are about what the product *says*.
|
||||
|
||||
### 1. The retrieval passphrase leaves the page body (R-249)
|
||||
|
||||
`settings_security.html` rendered the passphrase into a `display:none` span behind a „Megjelenít"
|
||||
button. **That toggle stops a browser DRAWING the value and nothing else** — the plaintext was in the
|
||||
response body of every render, so a `curl` of the page returned it. It was found by doing exactly
|
||||
that: it landed in a session transcript on 2026-08-07 while driving the documented rebuild path.
|
||||
|
||||
**The product already had this rule and this page did not follow it.** `escrow_handlers.go` states it
|
||||
for the recovery code — *"reveal (claim XHR only — R is NEVER templated server-side into HTML)"*. The
|
||||
passphrase now follows the same shape: the page carries only `HasRetrievalPassword`, and the value
|
||||
comes from **`POST /settings/retrieval-password/reveal`**, behind the same RequireAuth + CsrfProtect
|
||||
every other POST sits behind, `Cache-Control: no-store`, **and logged as an act** — reading it off the
|
||||
markup left no trace anywhere, where the hub's equivalent break-glass reveal has always emitted an
|
||||
event.
|
||||
|
||||
**POST for a read, deliberately:** a GET would be re-fetchable from history, pre-fetchable, cacheable,
|
||||
and — since CsrfProtect only covers unsafe methods — uncovered by CSRF.
|
||||
|
||||
**The test asserts the raw response body, not a rendered view**, because that is precisely why this
|
||||
survived: every test that asked what the customer *sees* passed while the bytes carried the secret.
|
||||
|
||||
**Census (§7.1), reported not fixed:** the render-then-hide pattern appears **twice more** —
|
||||
`deploy.html` (an auto-generated app secret in a `type="password"` input's `value=`; unavoidable on
|
||||
the pre-deploy form, which must post it, but not on an already-deployed app's page) and
|
||||
**`app_info.html`, which puts a per-install generated app password inside a `hidden` span** — the same
|
||||
shape with a real secret. Filed as **R-254**.
|
||||
|
||||
### 2. „nincs elérhető adatmeghajtó" now names the reason and the route (R-252)
|
||||
|
||||
A rebuilt box's drives survive; their **registration** does not. Every restore then refused with a
|
||||
sentence that named no next step and read like data loss. The restore page now states the precondition
|
||||
**before** the customer presses anything, says the backups and the drives are both still there, and
|
||||
links to Tárhely → Meghajtók. The refusal string says the same.
|
||||
|
||||
The page asks the question through the backup manager's own `HasRestoreDestination()`, which reads the
|
||||
**same** `GetSchedulableStoragePaths()` the resolver reads — a second copy of that predicate is exactly
|
||||
how a page ends up promising what the handler refuses, which is the next item.
|
||||
|
||||
### 3. The page no longer promises a reinstall the restore cannot do (R-253)
|
||||
|
||||
The restore list said **„Nincs telepítve — a visszaállítás előbb újratelepíti."** Three lines later the
|
||||
restore refused *because* the app was not installed. Two shipped sentences, in the customer's own
|
||||
language, contradicting each other at the last step of a recovery.
|
||||
|
||||
**The promise was the wrong half, and this is why:** reconstitution writes to the app's own data path
|
||||
(`GetStackHDDPath`), which exists only once the customer has chosen a drive at deploy time. An
|
||||
automatic reinstall would mean the product picking that drive for them — the one decision this whole
|
||||
recovery path exists to leave with the customer. So the copy now says to install it first and routes
|
||||
to `/stacks/<app>/deploy`; the refusal was reworded to match.
|
||||
|
||||
**A healthy box renders exactly as before** — both notices are conditional, and a test fails if either
|
||||
becomes unconditional.
|
||||
|
||||
## v0.206.0 — the box does not mint a key over a sealed package, and abandoning ends the question (2026-08-07, R-241) — MinAgent 0.127.0
|
||||
|
||||
**R-241 was ruled a MINTING defect, not a screen-predicate defect** (`SPIKE-r241-recovery-offer-2026-08-07.md`),
|
||||
|
||||
Reference in New Issue
Block a user