carlos / todomvc-bakeoff Public

Clone
git clone https://amadan.net/carlos/todomvc-bakeoff

Plain git — no account needed to clone.

Download

Download this file

1LIVE https://todo-nim.bfj.oncarlos.com 290dbb4
2
3## Where it stands
4
5- Deployed `290dbb4` (the second review round's fix) to `todo-nim` on
6 account `bfj`, channel `edge`. `carlos deploy` confirmed it through the
7 `/api/version` build probe. Earlier deploys: `df803f7`, then `6aa1f35`.
8- Shared behaviour suite: **67 passed, 3 skipped** both locally
9 (`dist/app --listen`) and against
10 `https://todo-nim.bfj.oncarlos.com`. The skips are the suite's own: blur
11 and Escape editing with JavaScript off, and the screenshot test with
12 JavaScript off.
13- `make test`: 17 unit tests pass (store scoping, filters, toggle-all,
14 Unicode and 10,000-character titles, Unicode-whitespace trimming, migrations, cookies, 303s, 403 on
15 a foreign Origin, 404 on a foreign id, the edit view, fragments, static
16 files).
17- `scripts/boot-check.sh dist/app-linux-arm64`: `/healthz` and
18 `/api/version` answer 200 on a unix socket under qemu, and SIGTERM
19 exits 0.
20- The binary is a 1.7 MB static aarch64 ELF (zig cc, musl). SQLite 3.54.1
21 is compiled in.
22
23## Worth knowing
24
25- `src/httpserver.nim` is a copy of Nim 2.2.12's `std/asynchttpserver`
26 with a single change, marked "todo-nim": chunked bodies are limited.
27 When upgrading Nim, re-copy it and re-apply that change.
28- `asynchttpserver` has no unix-socket mode. `unixServer` in `src/app.nim`
29 binds the socket and gives it to the server through
30 `std/importutils.privateAccess`, and it has to set `maxFDs` too:
31 without it `shouldAcceptRequest` is always false and the server
32 silently never accepts. That's the one place this app reaches into the
33 standard library's internals. A `listen(domain = AF_UNIX)` option
34 upstream would remove it.
35- The README and AGENTS.md prose didn't go through a copy review. This
36 session ran unattended, and that review needs a person.
37
38## Review round 2
39
40The one finding is fixed in `290dbb4`:
41
421. **Invalid UTF-8 in titles (security).** `title=%80` made
43 `unicode.strip` index out of bounds. The `IndexDefect` isn't a
44 `CatchableError`, so it got past the handler and stopped the server.
45 Now `action` runs `unicode.validateUtf8` on the decoded title before
46 anything else touches it, and answers **400** if it isn't valid. That
47 check is bounds-safe. Browsers always send UTF-8, so a real visitor
48 never hits this. A unit test covers truncated 2-, 3- and 4-byte
49 sequences and stray bytes for both add and edit, and the list is
50 unchanged afterwards. Before the fix, `title=%80` killed the local
51 binary. After it, the binary and the live host both answer 400 and
52 keep serving.
53
54After the fix the shared suite gave 67 passed and 3 skipped both locally
55and against the live host, and the boot check passed.
56
57## Review round 1
58
59All four findings are fixed in `6aa1f35`:
60
611. **Chunked bodies (security).** The parser lives inside the standard
62 library, so the server is now vendored as `src/httpserver.nim`. Each
63 chunk's declared size is checked against what's left of the body limit
64 before `recv` allocates anything. Oversized, overflowing and
65 many-small-chunk bodies all get a 413 and the connection closes. The
66 limit is 1 MiB for both chunked and `Content-Length` bodies (it was
67 8 MiB); a 10,000-character title needs at most about 120 KB. Checked
68 with raw-socket requests against the binary: it stayed at 7 MB RSS.
692. **Unicode trim.** Add and edit now strip the whitespace JavaScript's
70 `String.prototype.trim` removes (U+00A0, U+2000–U+200A, U+3000, U+FEFF
71 and the rest). A title of only non-breaking spaces adds nothing, and
72 editing to that deletes the item. There's a unit test for both.
733. **Edits across re-renders.** `render()` keeps the item being edited
74 (text, cursor, focus) and restores it on the same item in the new
75 list, and the blur that removing the old field may fire doesn't
76 count as a save. I checked it with Playwright: toggle one item with
77 the response held for 800 ms, double-click another, type, and wait.
78 The edit is still open, focused and holding the text, and Enter saves
79 it.
804. **Zig's caches.** The Makefile exports `ZIG_GLOBAL_CACHE_DIR` and
81 `ZIG_LOCAL_CACHE_DIR` under `.cache/`, and `clean-deps` removes them.
82 After `clean-deps`, `make build-arm64` rebuilt musl and compiler-rt
83 (61 MB of cache) from scratch.
84
85After the fixes the shared suite gave 67 passed and 3 skipped both
86locally and against the live host, and the boot check passed.
87
88## Review findings not fixed
89
90None.
91