Mobile: every page earns its phone layout, and --lan serves the network
Verified page by page in a 390px viewport against live data. The rail collapses to a brand line plus one horizontally scrollable nav row (was: two wrapped rows). The Titles table becomes stacked line-cards — title, chips, match, photos, finger-sized actions — with empty cells dropped (the templates now emit them tight so :empty applies); the editor stacks full-width inputs. Queue/library ledgers scroll inside their card and photo lists wrap at commas instead of clipping. The review done-card stats wrap, keyboard hints hide on touch widths, reshoot stencils go horizontal, buttons get touch-sized padding. Also: post-rename copy on the remove button. bggpipe web --lan binds 0.0.0.0 and extends the mutation guard's host allowlist with this machine's names and addresses (never a wildcard — DNS-rebinding arrives under the attacker's hostname, which an allowlist rejects), prints every reachable URL, and warns loudly that the app has no login. Default stays localhost-only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
co-authored by
Claude Fable 5
parent
429c519237
commit
2936d21f0a
@@ -869,3 +869,34 @@ def test_confirm_marks_shaky_read_verified_without_requeue(tmp_path):
|
||||
).status_code
|
||||
== 400
|
||||
)
|
||||
|
||||
|
||||
def test_lan_allowed_hosts_admit_network_but_not_strangers(tmp_path):
|
||||
cfg = make_cfg(tmp_path)
|
||||
app = create_app(
|
||||
cfg,
|
||||
client=unauthorized_client(tmp_path),
|
||||
allowed_hosts={"192.168.1.5", "erics-mac.local"},
|
||||
)
|
||||
body = {"title_raw": "Citadels", "source_photos": "shelf.jpg", "confirm": True}
|
||||
lan = TestClient(app, base_url="http://192.168.1.5")
|
||||
assert lan.post("/api/edit-title", json=body).status_code == 200
|
||||
# a hostname NOT on the allowlist (DNS rebinding shape) is still refused
|
||||
stranger = TestClient(app, base_url="http://attacker.example")
|
||||
assert stranger.post("/api/edit-title", json=body).status_code == 403
|
||||
# and without the opt-in, the LAN host is refused too
|
||||
plain = create_app(cfg, client=unauthorized_client(tmp_path))
|
||||
assert (
|
||||
TestClient(plain, base_url="http://192.168.1.5")
|
||||
.post("/api/edit-title", json=body)
|
||||
.status_code
|
||||
== 403
|
||||
)
|
||||
|
||||
|
||||
def test_lan_hosts_reports_this_machine(tmp_path):
|
||||
from bggpipe.webreview import lan_hosts
|
||||
|
||||
hosts = lan_hosts()
|
||||
assert hosts # at least the hostname
|
||||
assert all(h == h.lower() or "." in h for h in hosts)
|
||||
|
||||
Reference in New Issue
Block a user