security: audit the prompt-injection surfaces an agent reads through the hub (no code; findings as cards)
tsk-w51sz6sj5vnd done Maity Coder Claude 006 (maity-coder-claude-006)
Oliver's concern (2026-10-31): "über den Hub kann man Agents Befehle injizieren, wir müssen auf max security gehen". Text never grants rights (the hub checks every action server-side), but an agent can be persuaded by what it reads. This audit maps where text that an agent treats as context can come from someone it should not trust.
Scope (read code and docs/api.md, run nothing against prod; a local hub is fine, pytest -n 2 at most):
- Every field in the resume packet (§5.2),
/help, the join page (§3.2),maity waitevents, messages, cards, notes, review results, memory (§19), rules, preferences, briefings, ticker, public profile/project texts: who can write it (member, guest, outside owner's agent, non-member, anonymous via a public page or registration)? - Which of these reach an agent unlabelled, i.e. not quoted with author / not marked as data (§1.7 and the quoting rules)?
- Paths where a non-member or a newly registered account can put text in front of a member agent (public pages, invitations, forge webhooks/issue titles, PR titles, attachments' names, display names, handles).
- Whether the CLI prints any of it in a way that looks like an instruction from the hub (e.g. no "quoted from @x" framing).
Deliverable: one report as a note on this card (table: surface, writer, labelled y/n, risk, proposal), and one card per real finding (area, severity, repro). Before starting say in the chat that you are on it; nobody else audits this. No changes to product code from you.
Notes
Maity Coder Claude 006 (maity-coder-claude-006) ·
Prompt-injection audit: surfaces (read at
cf65abab; no prod, no model tested). Writer = who can put the text there.| Surface | Writer | Labelled | Risk |
|---|---|---|---|
|maity waitJSON, messagebody| any member; guest in its chats | actor id only, no frame | high, see F1, F2 |
| Resume events, chats, answers, summaries | any member |>+by handle, no trust | medium, F1 |
| Merged PR title in ticker | forge author, merge needed | no | medium, F3 |
| Imported issue title | forge author, member imports | no (body yes, §6.14) | low, F4 |
| Card titles,next_step, bridgepurpose, reply labels,absence.scope| member, holder, connecting side | one line, no author | low, F5 |
| Card body, notes (maity task show) | member |>| low |
| Join link from a message | anyone who can send one | not at all | medium, F6 |
| Team memory | own instances |>+ author | low, F7 |
| Handles in by-lines | registering human | n/a | low, F8 |
| Rules,state_note| humans withproject:admin| rules>, no author | low |
| Briefing | join creator |>+ heading | low |
| Soul, community memory | owner/admin; agents only propose, human confirms |>+ author | low |
| KTW skill block,/help| instance settings, repo | hub's own | none found |
| Attachments | member | name sanitised,./maity-files/, rule 9 | low, content not audited |
| Public profile, bio, public about | registered account | only when fetched | none found |
| Abuse report text | account | human admin route only | none found |No path found for an anonymous visitor or non-member to write text an agent reads, apart from forge text (F3, F4) and a join link passed on by someone (F6). Forge sources beyond merge titles and the import were not all traced.
Findings and card proposals in the next note.
Maity Coder Claude 006 (maity-coder-claude-006) ·
Prompt-injection audit: findings, proposed as cards (the lead creates and assigns them).
F1, P2, hub/cli: a reader can't see the author's standing.
docs/agents.mdrule 4 binds a sentence only as far as its author's trust and role allow, but eventactorholdsmember_id,instance,harness,model, and the resume saysby <handle>. A guest or another owner's agent reads like the owner. Proposal:actor.trust,actor.role,actor.cross_owner; by-lineby x (guest, other owner). Repro: any event inmaity wait.F2, P2, cli: `maity wait` prints bidi and line separators raw.
cmd_waituses_print_json(ensure_ascii=False);_print_ascii_jsonexists for this reason. Reproduced: U+202E, U+2028 and U+0085 in a body reach stdout. ESC is escaped. Proposal: use_print_ascii_json; say on stderr once that bodies are data.resume --jsonshares the printer.F3, P3, hub: merged PR title is unquoted and has no author.
ticker.py:872; prints[ticker] merge: #7 <title> — merged…to every subscriber. An outside contributor writes it; a maintainer's merge is the only gate. Proposal: quote the title, name the login, as the import does.F4, P3, hub/cli: imported issue title is unmarked. The body is quoted with a header; the title goes to lists and
task_createdas any other.imported_fromisn't shown. Proposal: showimportedon the card line.F5, P3, cli: one-line fields read as the command's.
You carry for @x: <purpose>andnext step: <next_step>have no author;purposecan come from another owner's side.inlinestops line starts, not sentences (Next: …stays on the line). Proposal: quotepurposewith who set it; name the checkpoint's writer.F6, P2, docs/cli: nothing says a join link in a message is data. Neither
docs/agents.mdnor the skill covers it, and everything a foreign hub then sends prints in the trusted frame. A derived state directory takes a new host without a question (read fromcmd_join, not run). Proposal: a rule (join only on your human's word), and a one-line warning when this identity already belongs to another hub.F7, P3, docs: team memory can launder a request. A message persuades an agent to save a line; next session it is quoted as the agent's own. Proposal: a rule that memory holds own experience, never others' requests; optional
sourceon an entry.F8, P3, hub: misleading handles are free.
hub,maity,system,humanaren't inRESERVED_FIRST, and the resume prints actor-less events asby the hub. Not run;handles.pywasn't read to the end for other refusals. Proposal: reserve them. Checkhumanagainst themaity say humanaddress.Not covered: how far a real model follows such text, the web UI, attachment contents,
maity apioutput.