Skip to content

fix(matrix-bridge): 3 audit-found bugs — R2 secret leak, E2EE/voice mention gate, reverse-map backfill#31

Merged
replicas-connector[bot] merged 1 commit into
mainfrom
fix/audit-found-bugs
May 30, 2026
Merged

fix(matrix-bridge): 3 audit-found bugs — R2 secret leak, E2EE/voice mention gate, reverse-map backfill#31
replicas-connector[bot] merged 1 commit into
mainfrom
fix/audit-found-bugs

Conversation

@replicas-connector

Copy link
Copy Markdown
Contributor

Summary

Round-4 audit of the new code from tonight's 12 PRs (#21-#30). Three real bugs found and fixed.

#1 (HIGH) — R2 archive basename leaks inline env-var values

archiveBasename("Bash", { command: "OPENAI_API_KEY=sk-xxx node srv.js" }) used the first whitespace-delimited token (OPENAI_API_KEY=sk-xxx) as the R2 object's basename, which lands in the public r2.dev URL the bridge emits as 📎 View full output. Any inline env-var assignment in front of a Bash command would publish the value to anyone who reads the chat.

Fix: walk tokens skipping KEY=value env vars + nohup/time/sudo/exec/env prefixes; defensively strip any trailing =…; cap final basename at 40 chars. New test exercises the exact leak vector.

#2 (MED) — Mention gate skipped E2EE + voice content

shouldHandleMessage(roomId, ev) was reading body/m.mentions from the OUTER ev.content. For E2EE rooms that's only the megolm ciphertext (no body/mentions). For voice messages it's "Voice message.ogg" not the transcript. Result: in E2EE groups the bot stayed silent on explicit mentions; in any group with voice messages the wake-word check missed.

Fix: introduce mentionContent alongside body/msgtype. For plain events it's ev.content; for E2EE it's the decrypted inner; for voice it's a synthetic { msgtype: "m.text", body: transcript }. Then call shouldHandleMessage with the synthetic.

#3 (MED) — Cross-room guard backward-compat hole

PR #29's guard only checks replica:{id} reverse mapping; the 47 existing room mappings that pre-date the guard have no reverse, so the check is a no-op for them until each room respawns.

Fix: opportunistic backfill on every successful follow-up — if reverse is absent, write it. Race-safe: a colliding peer's NEXT follow-up sees mismatch and respawns.

Validation

  • 173/173 tests pass (+8 new — archive basename security suite)
  • Typecheck clean
  • Deployed 43e13059

Test plan

  • Typecheck clean
  • All tests pass
  • Deploy succeeds
  • Smoke: send a long Bash output prompt with an inline env var, verify the 📎 View full output link's path component contains NO secret
  • Smoke: send Jada hi in an E2EE group (Element/Beeper), confirm bot now responds
  • Smoke: send a voice "Jada do X" in a 3+ person room, confirm bot transcribes + responds

Created by Jaden (jadengarza@pm.me) with Replicas
Workspace  ·  Slack Thread

…ention gate, reverse-map backfill

Round-4 audit of the new code from tonight's 12 PRs (#21-#30). Three
real bugs found and fixed in this PR.

**#1 (HIGH) — R2 archive basename can leak inline env-var values**

`archiveBasename("Bash", { command: "OPENAI_API_KEY=sk-xxx node srv.js" })`
used the first whitespace-delimited token (`OPENAI_API_KEY=sk-xxx`) as
the R2 object's basename, which lands in the *public* r2.dev URL the
bridge emits as `📎 View full output`. Any inline env-var assignment
in front of a Bash command would publish the value to anyone who reads
the chat.

Fix: walk tokens skipping `KEY=value` env vars + `nohup`/`time`/`sudo`/
`exec`/`env` prefixes; defensively strip any trailing `=…` even if the
regex missed something; cap the final basename at 40 chars. New test
exercises the exact leak vector.

**#2 (MED) — mention gate skipped over E2EE and voice content**

`shouldHandleMessage(roomId, ev)` was reading body/m.mentions from the
OUTER ev.content. For E2EE rooms that's only the megolm ciphertext
(no body/mentions). For voice messages it's `"Voice message.ogg"`
(not the transcript). Result: in E2EE groups the bot stayed silent on
explicit mentions; in any group with voice messages the bot couldn't
trigger on the "Jada" wake-word even when the transcript started with
it.

Fix: introduce `mentionContent` alongside `body`/`msgtype`. For plain
events it's `ev.content`; for E2EE it's `tryDecrypt`'s inner content;
for voice it's a synthetic `{ msgtype: "m.text", body: transcript }`.
Then call `shouldHandleMessage(roomId, { content: mentionContent })`.

**#3 (MED) — cross-room guard backward-compat hole**

PR #29's guard only checks the `replica:{id}` reverse mapping; the 47
existing room mappings that pre-date the guard have no reverse, so the
check is a no-op for them. Until each room respawns its replica
(could be days), the contamination guard does nothing.

Fix: opportunistic backfill on every successful follow-up — if the
reverse mapping is absent, write it (this room owns this replica).
Race-safe: a colliding peer's NEXT follow-up sees mismatch and
respawns. Same TTL as the forward mapping.

**Validation**
- 173/173 tests pass (+8 new — archive basename security suite)
- Typecheck clean
- Deployed `43e13059`

Co-Authored-By: itsablabla <itsablabla@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@replicas-connector replicas-connector Bot merged commit b96d765 into main May 30, 2026
1 of 3 checks passed
@capy-ai

capy-ai Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants