Record the smoketest auth gap and the browser-verification gap

Two things surfaced while building the User Directory that are worth a decision
rather than a mention in a handover.

3. server/smoketest.py has no login step, so auth_gate 401s every check after
   /api/health. DEPLOYMENT.md presents it as the way to prove the stack works
   end-to-end, including a docker compose exec invocation, so the documented
   verification path reports failure on a healthy system -- the failure mode most
   likely to be believed. Rated Medium for that reason. Predates the login
   portal; confirmed unrelated to this branch by stashing it and re-running. The
   Admin Console's in-browser smoke test is the working equivalent today.

4. users.js and wp-sidenav.js have never been executed -- no JS engine on the
   machine they were written on. Logged as a verification gap, not a defect, with
   what WAS checked (server tests, delimiter balance, handler resolution, id
   targets) and what only a browser can settle (layout, transitions, focus trap).
   Includes the five-step manual pass that closes it, and the hard-reload note,
   since sw.js bumped to wp-suite-shell-v6 and a soft reload serves the old
   shell.

Both entries follow the file's existing shape: what is wrong, what it costs, why
it is still open, what closing it takes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-05 18:06:47 -07:00
parent 4ace2afb1c
commit c99ef08cf1

View File

@@ -12,6 +12,8 @@ Close an entry by deleting it in the same commit that fixes it.
|---|-------|----------|--------|--------|
| 1 | XSS via SOP discipline names in the WP creator | Medium (internal), High if externally reachable | 2026-08-05 | Open |
| 2 | Archived projects: the two big apps don't grey out their own controls | Low | 2026-08-05 | Open |
| 3 | `server/smoketest.py` cannot authenticate — every run fails with 401 | Medium | 2026-08-05 | Open |
| 4 | User Directory and nav drawer have not been run in a browser | Low (verification gap, not a known defect) | 2026-08-05 | Open |
---
@@ -161,3 +163,130 @@ save/issue controls, or add a boot check in each app that disables them and show
read-only notice inline. Decide separately how the embedded creator
(`wp-creation-index.html`) surfaces it, since it runs in an iframe where the shared
app bar — and therefore the banner — is deliberately skipped.
---
## 3. `server/smoketest.py` cannot authenticate — every run fails with 401
**Files:** `server/smoketest.py` · documented in `DEPLOYMENT.md` §"Automated smoke
test" (line ~147)
**Predates:** the login portal. The script was written against an open API and was
never updated when authentication landed.
### What is wrong
The script has no login step — no call to `/api/auth/login`, no cookie jar, no
credential arguments. `auth_gate` (`server/app.py`) refuses every `/api/` route
without a session cookie, so every check after the first fails:
```
- create project (status=401)
- fetch project by id (status=401)
...
Result: FAIL
```
Only `/api/health` passes, because it is on `auth._EXEMPT_EXACT`.
### What it costs
**The documented end-to-end verification path does not work, and hasn't for some
time.** `DEPLOYMENT.md` presents this as the way to prove "NGINX → FastAPI →
PostgreSQL all work", including a `docker compose exec` invocation for use inside the
api container. Anyone following it after a deploy gets a wall of 401s and has to work
out for themselves whether the stack is broken or the script is.
The stack itself is fine, and there is a working equivalent: the **Admin Console →
End-to-end smoke test** card runs the same sequence from the browser, where the
session cookie already exists. `html/admin.html` describes it as mirroring
`smoketest.py`, which is now the only place that sequence actually runs.
Rated Medium rather than Low because it is a verification tool that reports failure
on a healthy system — the failure mode most likely to be believed and acted on.
### Why it is still open
It was found while testing unrelated work (the User Directory) and is not a defect in
the product. Fixing it means choosing how the script gets credentials, which is a
small design decision about a deploy-time tool rather than a code fix, and it should
not ride along inside a feature branch.
### What closing it takes
Small — under an hour, stdlib only, matching the script's existing constraint.
1. Add `--user` / `--password` arguments, defaulting to `WP_SMOKE_USER` /
`WP_SMOKE_PASSWORD` from the environment so the runbook does not put a password on
a command line.
2. Install an `http.cookiejar.CookieJar` on the opener in `call()`, then POST
`/api/auth/login` before the first check and assert it returned 200. There is a
working reference for both steps in the throwaway harness written for this branch
(`Client` in the scratch `http_check.py`), or in `html/admin.js`'s in-browser
version of the same flow.
3. Note in `DEPLOYMENT.md` that the account needs access to the project the script
creates — simplest is an admin account, since a `project_user` cannot create a
project.
4. Decide whether a missing credential is a hard failure or a skip with a clear
message. A silent 401 wall is what caused this entry.
---
## 4. User Directory and nav drawer have not been run in a browser
**Files:** `html/users.html`, `html/users.js`, `html/wp-sidenav.js`,
`html/wp-sidenav.css`, `html/console.css`
**Raised:** 2026-08-05, with the Project Super User work.
### What is wrong
This is a **gap in what has been verified, not a known defect.** The machine the code
was written on has no JavaScript engine available (no node, deno, or npx), so these
files have never been executed. What *has* been checked:
- The server side they talk to: 93 scope/permission tests and 29 live HTTP tests
through the real dependency stack, all passing.
- Static analysis of the browser code: delimiter balance across all scripts (with a
tokenizer that handles nested template interpolation), every inline handler
resolving to a defined function, every `getElementById` target existing in its page,
and no leftover references to the functions that moved out of `admin.js`.
What that cannot cover is anything only a browser decides: CSS layout, the drawer's
open/close transitions and focus trap, event wiring, and how the three role
renderings actually look with real data.
### What it costs
Unknown by definition, bounded by blast radius. A syntax or boot error in `users.js`
means a blank User Directory — obvious the moment anyone opens it, and it takes
nothing else down: `users.html` is a new page, and the drawer is additive to pages
that already worked without it. The server-side role and its scope rules are
independently tested and unaffected either way.
The one thing to watch is `console.css`, which was extracted from `admin.html`'s
inline styles and is now shared. A rule lost in that move would show up as the Admin
Console losing its dense-table styling — the same symptom the deploy runbook already
tells you to look for ("one line per user", not three).
### Why it is still open
It closes with one manual page load, which needs a browser signed in to a real
deployment — not something to fake from a test harness.
### What closing it takes
Minutes. Hard-reload first (Ctrl+Shift+R) — the service worker caches the app shell
and `sw.js` bumped to `wp-suite-shell-v6`, so a normal reload can serve the old file
list and make a good deploy look broken.
1. Open **users.html** as an admin: the table renders, rows are one line tall, and
the Permissions and Project role dropdowns are populated.
2. Open it as a **Project Super User**: the blue scope banner names their project(s),
the create form demands at least one project, and any account also on a job they
don't administer shows as `read-only` with a reason on hover.
3. Open it as a **project user**: six columns, no controls, no create form.
4. Open **field.html** on a phone or a narrow window: the ☰ opens the drawer, Escape
and the scrim close it, and Admin Console appears only for admins.
5. Open **admin.html** and confirm the tables still look dense and correct — that is
the `console.css` extraction check.
Delete this entry once those five are done.