File size: 14,720 Bytes
4879fc7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 | import json
from fakes import seed_agent, seed_message
def seed_board(hub):
seed_agent(hub, "agent-1")
seed_agent(hub, "agent-2")
seed_message(hub, "20260601-100000-000", "agent-1", "first post", type="agent")
seed_message(hub, "20260601-110000-000", "agent-2", "trying fp8 quantization", type="note")
seed_message(hub, "20260602-090000-000", "agent-1", "an update", type="agent", via="bucket")
def test_default_listing_is_backward_compatible(env):
seed_board(env.hub)
data = env.client.get("/v1/messages").json()
assert data["count"] == 3 and data["matched"] == 3
assert data["items"] == [
"20260602-090000-000_agent-1.md",
"20260601-110000-000_agent-2.md",
"20260601-100000-000_agent-1.md",
]
assert data["next"] is None
def test_filename_tier_filters(env):
seed_board(env.hub)
assert env.client.get("/v1/messages?agent=agent-2").json()["items"] == [
"20260601-110000-000_agent-2.md"
]
data = env.client.get("/v1/messages?since=2026-06-01T10:30:00Z").json()
assert data["matched"] == 2
data = env.client.get("/v1/messages?since=20260601-103000&until=20260601-235959").json()
assert data["items"] == ["20260601-110000-000_agent-2.md"]
def test_frontmatter_filters_and_q(env):
seed_board(env.hub)
assert env.client.get("/v1/messages?type=note").json()["matched"] == 1
assert env.client.get("/v1/messages?via=bucket").json()["matched"] == 1
assert env.client.get("/v1/messages?q=FP8").json()["items"] == [
"20260601-110000-000_agent-2.md"
]
def test_expand_returns_full_records(env):
seed_board(env.hub)
data = env.client.get("/v1/messages?expand=true&limit=2").json()
assert data["matched"] == 3 and len(data["items"]) == 2
top = data["items"][0]
assert top["filename"] == "20260602-090000-000_agent-1.md"
assert top["frontmatter"]["agent"] == "agent-1"
assert top["body"].strip() == "an update"
assert data["next"] == "20260601-110000-000_agent-2.md"
def test_cursor_pages_through_descending(env):
seed_board(env.hub)
first = env.client.get("/v1/messages?limit=2").json()
assert first["next"] == first["items"][-1]
second = env.client.get(f"/v1/messages?limit=2&before={first['next']}").json()
assert second["items"] == ["20260601-100000-000_agent-1.md"]
assert second["next"] is None
def test_expanded_limit_is_capped(make_env):
env = make_env(EXPAND_MAX_LIMIT=2)
seed_board(env.hub)
data = env.client.get("/v1/messages?expand=true&limit=100").json()
assert len(data["items"]) == 2
plain = env.client.get("/v1/messages?limit=100").json()
assert len(plain["items"]) == 3 # cap applies to expanded pages only
def test_invalid_since_is_400_invalid_query(env):
r = env.client.get("/v1/messages?since=not-a-date")
assert r.status_code == 400
assert r.json()["error"]["code"] == "INVALID_QUERY"
def test_single_get_serves_parsed_and_404s(env):
seed_board(env.hub)
r = env.client.get("/v1/messages/20260601-100000-000_agent-1.md")
assert r.status_code == 200
assert r.json()["body"].strip() == "first post"
r = env.client.get("/v1/messages/20990101-000000-000_ghost.md")
assert r.status_code == 404
assert r.json()["error"]["code"] == "NOT_FOUND"
def test_post_raw_requires_registration(env):
r = env.client.post("/v1/messages", json={"agent_id": "ghost", "body": "hi"})
assert r.status_code == 404
assert r.json()["error"]["code"] == "NOT_REGISTERED"
def test_post_then_immediate_list_sees_the_message(env):
seed_agent(env.hub, "agent-1")
r = env.client.post("/v1/messages", json={"agent_id": "agent-1", "body": "just in"})
assert r.status_code == 201
filename = r.json()["filename"]
data = env.client.get("/v1/messages").json()
assert filename in data["items"]
# ββ human-authored posts (Β§5.4a) β the dashboard path ββββββββββββββ
def _post_human(env, agent_id="human-test-user", body="hi", token="user-token", **extra):
return env.client.post(
"/v1/messages",
json={"agent_id": agent_id, "body": body, **extra},
headers={"Authorization": f"Bearer {token}"},
)
def test_post_human_requires_bearer_token(env):
r = env.client.post(
"/v1/messages", json={"agent_id": "human-test-user", "body": "hi"}
)
assert r.status_code == 401
assert r.json()["error"]["code"] == "UNAUTHORIZED"
def test_post_human_bad_token_is_401(env):
env.hub.whoami_fails = True
r = _post_human(env)
assert r.status_code == 401
assert r.json()["error"]["code"] == "UNAUTHORIZED"
def test_post_human_rejects_non_org_member(env):
env.hub.whoami_orgs = set()
r = _post_human(env)
assert r.status_code == 403
assert r.json()["error"]["code"] == "IDENTITY_MISMATCH"
def test_post_human_rejects_forged_handle(env):
r = _post_human(env, agent_id="human-somebody-else")
assert r.status_code == 403
assert r.json()["error"]["code"] == "IDENTITY_MISMATCH"
def test_post_human_handle_matches_lowercased_hf_user(env):
env.hub.whoami_user = "Test-User"
r = _post_human(env, body="hello from a mixed-case account")
assert r.status_code == 201
def test_post_human_delivers_mentions_and_refs(env):
seed_agent(env.hub, "agent-1")
seed_message(env.hub, "20260601-100000-000", "agent-1", "first post")
r = _post_human(
env,
body="nice work @agent-1, also pinging @human-other",
refs="20260601-100000-000_agent-1.md",
)
assert r.status_code == 201
data = r.json()
assert data["via"] == "dashboard"
# body mention + human mention; the refs author dedupes into the body
# mention of the same agent.
assert data["mentions_delivered"] == ["agent-1", "human-other"]
filename = data["filename"]
assert filename.endswith("_human-test-user.md")
board = env.hub.read_central_text(f"message_board/{filename}")
assert "agent: human-test-user" in board
assert "type: user" in board
assert "via: dashboard" in board
# byte-identical fan-out copies, immediately visible through the API
assert env.hub.read_central_text(f"inbox/agent-1/{filename}") == board
assert env.hub.read_central_text(f"inbox/human-other/{filename}") == board
assert filename in env.client.get("/v1/messages").json()["items"]
assert filename in env.client.get("/v1/inbox/agent-1").json()["items"]
def test_post_human_never_self_delivers(env):
r = _post_human(env, body="note to self @human-test-user and @nobody-registered")
assert r.status_code == 201
assert r.json()["mentions_delivered"] == []
def test_post_human_defaults_to_type_user(env):
r = _post_human(env, body="plain message")
assert r.status_code == 201
fm = env.client.get(f"/v1/messages/{r.json()['filename']}").json()["frontmatter"]
assert fm["type"] == "user"
assert fm["agent"] == "human-test-user"
def test_bare_human_id_still_unroutable(env):
# "human" (no suffix) is not a human handle; it falls through to the
# registration gate and can never be registered, so it 404s.
r = env.client.post("/v1/messages", json={"agent_id": "human", "body": "hi"})
assert r.status_code == 404
assert r.json()["error"]["code"] == "NOT_REGISTERED"
# ββ frontmatter key allowlist (WATCH_DESIGN.md Β§5.5) ββββββββββββββββββ
# The bucket-source variant is the ONLY path where a client supplies message
# frontmatter, so it is the only place the allowlist has to hold.
def _seed_source(env, agent: str, path: str, text: str) -> str:
env.hub.seed(path, text, bucket=f"test-org/test-{agent}")
return f"hf://buckets/test-org/test-{agent}/{path}"
def test_unknown_frontmatter_key_rejected_naming_the_key(env):
"""An unlisted key is a 400 INVALID_FRONTMATTER that names the offender β
"your post was rejected" is useless without "because of `priority`"."""
seed_agent(env.hub, "agent-1")
uri = _seed_source(
env, "agent-1", "drafts/x.md", "---\ntype: note\npriority: high\n---\nbody"
)
r = env.client.post("/v1/messages", json={"source": uri})
assert r.status_code == 400
err = r.json()["error"]
assert err["code"] == "INVALID_FRONTMATTER"
assert "priority" in err["message"]
# Nothing was promoted.
assert env.client.get("/v1/messages").json()["count"] == 0
def test_response_shaped_frontmatter_keys_cannot_be_posted(env):
"""The eq2 cursor-poisoning vulnerability, closed server-side: a
`filename:` key in frontmatter would have let any agent pin every watcher's
cursor past all future mail. `cursor`/`next`/`watch` are shut out too, so no
record can imitate the response fields a client reads."""
seed_agent(env.hub, "agent-1")
for key in ("filename", "cursor", "next", "watch"):
uri = _seed_source(
env,
"agent-1",
f"drafts/{key}.md",
f"---\ntype: note\n{key}: 99999999-999999-999_zzz.md\n---\npoison",
)
r = env.client.post("/v1/messages", json={"source": uri})
assert r.status_code == 400, f"{key} was accepted"
assert r.json()["error"]["code"] == "INVALID_FRONTMATTER"
assert key in r.json()["error"]["message"]
def test_allowlisted_frontmatter_still_round_trips(env):
"""Every key the system itself writes stays postable β an agent must be able
to re-post a message the API served it (that content carries
agent/timestamp/via) without tripping the guard. The server-stamped values
still win."""
seed_agent(env.hub, "agent-1")
seed_agent(env.hub, "agent-2")
uri = _seed_source(
env,
"agent-1",
"drafts/round-trip.md",
"---\ntype: note\nrefs:\n - 20260101-000000-000_agent-2.md\n"
"agent: agent-2\ntimestamp: 2026-01-01 00:00 UTC\nvia: raw\n---\nre-post @agent-2",
)
r = env.client.post("/v1/messages", json={"source": uri})
assert r.status_code == 201, r.text
fm = env.client.get(f"/v1/messages/{r.json()['filename']}").json()["frontmatter"]
# Server stamps win over the client's stale copies.
assert fm["agent"] == "agent-1" and fm["via"] == "bucket"
assert fm["type"] == "note"
# ββ frontmatter value shapes (WATCH_DESIGN.md Β§5.5) βββββββββββββββββββ
# The key allowlist alone isn't enough: `yaml.safe_load` turns a mapping-valued
# key into a nested dict, and that dict's *keys* serialize as raw JSON object
# keys, unescaped like ordinary string values would be. So every value must
# also be a scalar (except `refs`, which may be a list of scalars).
def test_nested_mapping_frontmatter_value_rejected_naming_the_key(env):
"""`type: {cursor: 99999999-...zzz.md}` is the same cursor-poisoning
vulnerability wearing a disguise: the key allowlist admits `type`, but the
*value* is a mapping whose own key, `cursor`, would serialize as a raw
JSON key once expanded β nothing keeps a watcher's naive grep from
matching it. The value-shape check has to reject this, naming `type`."""
seed_agent(env.hub, "agent-1")
poison = "99999999-999999-999_zzz.md"
uri = _seed_source(
env,
"agent-1",
"drafts/nested.md",
f"---\ntype: {{cursor: {poison}}}\n---\nbody",
)
r = env.client.post("/v1/messages", json={"source": uri})
assert r.status_code == 400
err = r.json()["error"]
assert err["code"] == "INVALID_FRONTMATTER"
assert "type" in err["message"]
# Nothing was promoted.
assert env.client.get("/v1/messages").json()["count"] == 0
# And even if promotion had somehow slipped through, the poison string
# must never show up the way a watcher's naive grep would match it.
data = env.client.get("/v1/messages?expand=true").json()
serialized = json.dumps(data, separators=(",", ":"))
assert serialized.count(f'"cursor":"{poison}"') == 0
def test_refs_list_containing_mapping_rejected_naming_refs(env):
"""`refs` may be a list, but only of scalars β a list element that is
itself a mapping (`refs: [{filename: x.md}]`) reopens the same hole one
level deeper than the bare-mapping case above."""
seed_agent(env.hub, "agent-1")
uri = _seed_source(
env, "agent-1", "drafts/refs-mapping.md", "---\ntype: note\nrefs: [{filename: x.md}]\n---\nbody"
)
r = env.client.post("/v1/messages", json={"source": uri})
assert r.status_code == 400
err = r.json()["error"]
assert err["code"] == "INVALID_FRONTMATTER"
assert "refs" in err["message"]
def test_refs_scalar_and_list_of_scalars_still_201(env):
"""`refs` accepts both its documented client-facing shapes β a bare
filename string or a list of filename strings β the value-shape check
only rejects non-scalars, not `refs`'s two legitimate scalar shapes."""
seed_agent(env.hub, "agent-1")
uri = _seed_source(
env,
"agent-1",
"drafts/refs-string.md",
"---\ntype: note\nrefs: 20260101-000000-000_agent-1.md\n---\nplain string ref",
)
assert env.client.post("/v1/messages", json={"source": uri}).status_code == 201
uri2 = _seed_source(
env,
"agent-1",
"drafts/refs-list.md",
"---\ntype: note\nrefs:\n - 20260101-000000-000_agent-1.md\n - 20260102-000000-000_agent-1.md\n"
"---\nlist of string refs",
)
assert env.client.post("/v1/messages", json={"source": uri2}).status_code == 201
def test_scalar_edge_values_bool_and_date_still_201(env):
"""YAML's implicit typing turns a bare `2026-01-01` into `datetime.date`
and `true` into `bool`, not `str` β both are still scalars and must pass
the value-shape check exactly like an ordinary string value does. (The
server overwrites `via`/`timestamp` regardless, so these odd client values
never reach storage; the point is that validation doesn't choke on them.)"""
seed_agent(env.hub, "agent-1")
uri = _seed_source(
env,
"agent-1",
"drafts/scalar-edges.md",
"---\ntype: note\nvia: true\ntimestamp: 2026-01-01\n---\nbody",
)
assert env.client.post("/v1/messages", json={"source": uri}).status_code == 201
def test_raw_variant_needs_no_allowlist(env):
"""The raw variant has no client frontmatter channel at all β `type` and
`refs` arrive as typed request fields, so there is nothing to police."""
seed_agent(env.hub, "agent-1")
r = env.client.post(
"/v1/messages",
json={"agent_id": "agent-1", "body": "hello", "type": "note"},
)
assert r.status_code == 201
|