fix(u6): fold the cold bug-hunt panel — a div in a span, a symlink split, and an append outside its lock
/heid-bug-hunt panel 01M35CRRK2RTVWWF1BN09AFQG3, diff-scoped against 91fd8bc.
The most severe of the three rounds, and three of its four convergent findings
were already closed by our own adversarial pass before the reply landed. Three
were not.
- The benches panel was nested inside the booth header's <span class="sub">.
The insertion had matched the first `{% if board %}` in the template rather
than the block-level one. A div inside a span is invalid HTML: the parser
closes the span implicitly and hoists the div out, orphaning the rest of the
sub-line. Nothing 500s, which is precisely why no test in this suite could
see it. Moved to block level, pinned by an offset assertion, and verified
with a real HTML parser.
- _booth_exists used a bare is_dir() while resolve_booth resolves and requires
the parent to BE the data root. They disagreed on a symlink: the marker
called a booth pointing outside the root alive while the page 404s it, so the
row rendered healthy and the link was dead. Same containment now, and
ValueError joins OSError in the guard -- one bad row must never cost the
other 220.
- The board append opened its fd OUTSIDE the lock. `flock LOCK printf ... >>
board` reads as locked and is not: the shell opens the append fd while
parsing, before flock acquires. A concurrent unlink replaces the inode via
os.replace, the old fd still points at the unlinked one, and the append
succeeds, reports success, and vanishes. Pre-existing rather than this
unit's, but it is silent data loss in the file this unit lives in. Proved by
holding the lock and asserting nothing is written.
- The atomic write used a predictable .tmp.<pid> name; a pre-planted symlink
there redirects the write straight through the replace. mkstemp with O_EXCL
in the same directory, and an fsync before the replace -- os.replace orders
the rename, not the data behind it.
Declined and recorded: on a host where booth.links cannot be imported, `booth
link` now refuses every URL rather than only booth ones. True, and kept. A
guard that fails open is not a guard, and that state is a broken install in
which most of the CLI is equally broken.
The sharpest line in the reply is one three arms found independently: this repo
had ALREADY paid for the RecursionError class in marks.py, and the new module
re-introduced the unguarded parse. Reading the new module in isolation would
never have surfaced that.
604 -> 607 tests.
This commit is contained in:
@@ -840,3 +840,50 @@ def test_name_and_owner_ARE_clipped_at_the_read(tmp_path):
|
||||
benches, err = read_benches(tmp_path)
|
||||
assert err is None
|
||||
assert len(benches[0].name) == NAME_MAX and len(benches[0].owner) == OWNER_MAX
|
||||
|
||||
|
||||
def test_the_benches_panel_is_not_nested_inside_a_span(tmp_path):
|
||||
"""Cold bug-hunt panel, 3-of-4, seat-confirmed by byte offset in the live
|
||||
document: the panel `<div>` had landed INSIDE the booth header's
|
||||
`<span class="sub">`, because the insertion matched the first
|
||||
`{% if board %}` in the template rather than the block-level one.
|
||||
|
||||
A `<div>` inside a `<span>` is invalid HTML — the parser closes the span
|
||||
implicitly and hoists the div out, orphaning the rest of the sub-line. It
|
||||
renders "fine" in the sense that nothing 500s, which is exactly why no
|
||||
other test in this file could see it.
|
||||
|
||||
Checked the way the seat checked it: by offset. Defeating change: moving
|
||||
the panel back above the sub-span's close."""
|
||||
_board(tmp_path, ROW_REF)
|
||||
upsert_bench(tmp_path, "https://talk.test/", "talk", "o")
|
||||
body = _client(tmp_path).get("/b/links/").text
|
||||
sub_open = body.index('<span class="sub">')
|
||||
sub_close = body.index("</span>", body.index("· ", sub_open))
|
||||
panel = body.index('<div class="benches">')
|
||||
assert not (sub_open < panel < sub_close), (
|
||||
f"the benches div (offset {panel}) sits inside the sub span "
|
||||
f"({sub_open}..{sub_close})")
|
||||
|
||||
|
||||
def test_a_symlinked_booth_is_dead_to_the_marker_as_it_is_to_the_page(tmp_path):
|
||||
"""Cold bug-hunt panel, 3-of-4: `_booth_exists` used a bare `is_dir()`
|
||||
while `resolve_booth` resolves and requires the parent to BE the data root.
|
||||
They disagreed on a symlink — the marker called a booth pointing outside
|
||||
the root alive while the page 404s it, so the row rendered healthy and the
|
||||
link was dead. The worst of both, and invisible.
|
||||
|
||||
Defeating change: dropping the containment check from `_booth_exists`."""
|
||||
outside = tmp_path.parent / f"outside-{tmp_path.name}"
|
||||
outside.mkdir()
|
||||
try:
|
||||
(tmp_path / "escapee").symlink_to(outside, target_is_directory=True)
|
||||
except OSError:
|
||||
pytest.skip("no symlink support here")
|
||||
_board(tmp_path, "- [x](http://h:8090/b/escapee/) <sub>· a · 2026-09-01 00:00</sub>\n")
|
||||
c = _client(tmp_path)
|
||||
body = c.get("/b/links/")
|
||||
assert body.status_code == 200
|
||||
# the page's own verdict on that name, which the marker must agree with
|
||||
assert c.get("/b/escapee/").status_code == 404
|
||||
assert _dead_rows(body.text), "the marker called a booth alive that the page 404s"
|
||||
|
||||
@@ -625,3 +625,37 @@ def test_a_credential_never_reaches_the_board(booth):
|
||||
assert r.returncode != OK
|
||||
assert "credentials" in r.stderr
|
||||
assert not (data / "links").exists(), "a credentialed URL created the board"
|
||||
|
||||
|
||||
def test_the_append_happens_INSIDE_the_lock(booth):
|
||||
"""Cold bug-hunt panel, hulda solo. `flock LOCK printf ... >> board` reads
|
||||
as locked and is not: the SHELL opens the append fd while parsing, before
|
||||
flock acquires. A concurrent `unlink` rewriting the board in that window
|
||||
replaces the inode, the old fd keeps pointing at the unlinked one, and the
|
||||
append succeeds, reports success, and vanishes.
|
||||
|
||||
Proved by holding the lock: if the open were outside it, `booth link` would
|
||||
write and exit while blocked. Defeating change: reverting to the bare
|
||||
`flock LOCK printf ... >>` form, under which this test writes the row.
|
||||
"""
|
||||
import fcntl
|
||||
data, _ = booth
|
||||
board = data / "links"
|
||||
board.mkdir(parents=True)
|
||||
(board / "links.md").write_text("")
|
||||
lock = board / ".links.lock"
|
||||
lock.touch()
|
||||
with lock.open("r+") as lf:
|
||||
fcntl.flock(lf, fcntl.LOCK_EX)
|
||||
try:
|
||||
# subprocess.run directly: `run()` pins timeout=30 itself.
|
||||
with pytest.raises(subprocess.TimeoutExpired):
|
||||
subprocess.run(
|
||||
[str(SCRIPT), "link", "https://ok.test/x", "blocked"],
|
||||
capture_output=True, text=True, timeout=5,
|
||||
env={**os.environ, "BOOTH_DATA_DIR": str(data),
|
||||
"BOOTH_URL": "http://booth.invalid"})
|
||||
finally:
|
||||
fcntl.flock(lf, fcntl.LOCK_UN)
|
||||
assert (board / "links.md").read_text() == "", \
|
||||
"the row was appended while another writer held the lock"
|
||||
|
||||
Reference in New Issue
Block a user