Browser feedback uses the signed public endpoint; add the config page
Connor pointed out he may use the site from a laptop away from home. The gated /ui/feedback/ path was worse than just inconvenient: fetch() follows redirects, so an expired Authelia session would go 302 -> login page -> HTTP 200, and the UI would mark the card labelled having recorded nothing. Silent data loss on the one signal the system learns from. Both surfaces now post to the same HMAC-signed public endpoint, with `redirect: 'error'` so a redirect fails loudly instead of looking like success. The gated route is gone -- one mechanism, not two. /config shows what every block actually matches right now, and a box to try a filter before saving it: describe what you want caught, see what it catches and what sits just below the line, move the threshold, then save it as a veto or a demote. That boundary view is the point -- a crypto filter at 0.55 catches 131 clusters including "Security updates for Monday", and at 0.62 catches 4, which you can only know by looking. Saves use tomlkit rather than tomllib plus a dump, because profile.toml carries the reasoning behind every constant and a comment-stripping round-trip would quietly destroy the record of why each number is what it is. Verified: 97 top-level comment lines in, 97 out. The config mount had to stop being :ro, and ~/config/news2 is now owned 10001:1000 group-writable so the container can save while hand-editing still works. `:U` would have fixed the container and locked the user out.
This commit is contained in:
1 parent
42c38d759d
commit
5f4bc8d890
8 files changed
+1481
-26
No files matched your search
+29
-1
@@ -105,9 +105,12 @@ def client(tmp_path, monkeypatch):
|
||||
published_at,first_seen_at,content_hash) VALUES
|
||||
('i1','s1','Council votes on transit','','http://b/1','http://b/1',?,?,'h')""",
|
||||
(now.isoformat(), now.isoformat()))
|
||||
# 384 dims: the triage page scores against the real profile probes, so a
|
||||
# short centroid blows up in the matmul.
|
||||
import numpy as np
|
||||
con.execute("""INSERT INTO clusters (id,centroid,size,first_seen_at,last_seen_at,lead_item_id)
|
||||
VALUES ('c1',?,1,?,?,'i1')""",
|
||||
(store.to_blob(__import__("numpy").zeros(3, dtype="float32")),
|
||||
(store.to_blob(np.zeros(384, dtype="float32")),
|
||||
now.isoformat(), now.isoformat()))
|
||||
con.execute("INSERT INTO cluster_members VALUES ('c1','i1',?)", (now.isoformat(),))
|
||||
con.execute("""INSERT INTO notification_log (cluster_id,lane,score,created_at)
|
||||
@@ -116,6 +119,7 @@ def client(tmp_path, monkeypatch):
|
||||
VALUES (?,'The council met.','m',?)""",
|
||||
(now.date().isoformat(), now.isoformat()))
|
||||
from news import web
|
||||
web._VECTORS = None # module-level cache must not leak across tests
|
||||
return TestClient(web.app), now
|
||||
|
||||
|
||||
@@ -204,3 +208,27 @@ def test_feedback_survives_a_concurrent_writer(tmp_path, monkeypatch):
|
||||
finally:
|
||||
stop.set()
|
||||
t.join(timeout=5)
|
||||
|
||||
|
||||
def test_browser_labels_use_the_same_signed_public_endpoint(client):
|
||||
"""The browser must not post to an Authelia-gated path.
|
||||
|
||||
fetch() follows redirects, so with an expired session a gated POST would go
|
||||
302 -> login page -> HTTP 200, and the UI would report success having
|
||||
recorded nothing. Both surfaces use the signed public endpoint instead.
|
||||
"""
|
||||
c, _ = client
|
||||
html = c.get("/").text
|
||||
assert "/api/feedback/" in html
|
||||
assert "/ui/feedback/" not in html, "the gated browser route must be gone"
|
||||
assert "redirect: 'error'" in html, "a redirect must fail loudly, not look like success"
|
||||
assert html.count("data-yes=") > 0
|
||||
|
||||
|
||||
def test_labelling_is_disabled_when_no_secret_is_configured(tmp_path, monkeypatch):
|
||||
monkeypatch.setenv("NEWS_DB", str(tmp_path / "n.db"))
|
||||
monkeypatch.delenv("NEWS_FEEDBACK_SECRET", raising=False)
|
||||
store.connect(tmp_path / "n.db")
|
||||
from news import web
|
||||
web._VECTORS = None
|
||||
assert "labelling is disabled" in TestClient(web.app).get("/").text
|
||||
Reference in new issue
Block a user