Skip to content

Harden shared agent instruction review - #4220

Merged
shellz-n-stuff merged 8 commits into
block:mainfrom
shellz-n-stuff:codex/harden-shared-agent-instructions
Aug 13, 2026
Merged

Harden shared agent instruction review#4220
shellz-n-stuff merged 8 commits into
block:mainfrom
shellz-n-stuff:codex/harden-shared-agent-instructions

Conversation

@shellz-n-stuff

@shellz-n-stuff shellz-n-stuff commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • render shared-agent instructions as literal text so Markdown cannot conceal spoiler contents, link destinations, or image sources
  • reject non-reviewable Unicode controls at every agent-definition boundary while preserving legitimate rendered emoji sequences
  • verify shared catalog event IDs and signatures before trusting authorship, coordinates, pagination, or executable content
  • preserve the exact system-prompt bytes between review and execution instead of silently stripping or normalizing content

Security rationale

Shared system prompts are executable configuration. Previously, catalog prompts were projected through the chat Markdown renderer, which could hide text, replace link destinations with benign labels, and turn image syntax into remote loads. Zero-width and bidirectional controls could also make reviewed text differ from what the agent executes.

This change establishes a review invariant: the prompt a user sees is the prompt the agent executes. Definitions that cannot be reviewed faithfully are rejected rather than rewritten. Catalog events must also pass Nostr ID/signature verification before they can claim a publisher, coordinate, or cursor.

What changed

  • catalog instructions render as exact literal text rather than rich Markdown
  • catalog relay events are verified on a fresh wire-shaped object before paging, coordinate selection, attribution, or projection
  • forged content, pubkeys, signatures, and invalid newer heads are ignored and cannot shadow a valid signed definition
  • TypeScript catalog parsing rejects unsafe remote definitions before they reach the UI
  • shared Rust validation covers persona create/update/import, inbound relay sync, definition-less managed-agent sync, and catalog publication paths
  • definition-less managed agents now fail closed on local create, local update, and publication before persistence or relay retention
  • linked managed agents validate their local name while treating the persona definition as authoritative; their inert record-level prompt is not executed or published
  • names reject layout controls; prompts retain ordinary newlines and tabs
  • legitimate emoji composition is supported, including contextual VS16, ZWJ, skin-tone, family, flag, and keycap sequences
  • detached selectors/joiners, bidirectional controls, tag characters, zero-width concealment, and other default-ignorables remain rejected
  • names are bounded to 128 characters and prompts to 64 KiB
  • contributor guidance documents the byte-for-byte review requirement for future sharing paths

Validation reports the offending code point and never silently removes it.

E2E recording

buzz-shared-agent-security-e2e.webm

The recording demonstrates:

  • a safe definition remains visible
  • a prompt containing zero-width U+200B is rejected
  • a name containing bidi override U+202E is rejected
  • the prompt is preserved exactly
  • spoiler, link, and image syntax remains literal and does not render or load

Verification

Passed locally:

  • just test: all 10 unit and Docker-backed integration stages
  • desktop frontend unit suite: 4,295 tests
  • persona catalog relay unit suite: 32 tests, including forged-event and cursor-shadowing cases
  • focused Rust definition-validation coverage: 3 local create/update tests and 6 publication-filtered tests
  • complete desktop Tauri library suite after rebase: 2,263 passed, 14 ignored, 0 failed
  • desktop Tauri clippy with warnings denied and Rust formatting
  • complete agent Playwright spec: 34 tests
  • the exact formerly failing inbox-edit immediate-attachment smoke test after rebase: 1 test
  • focused shared-agent publish, literal-review, hidden-control, signature, and cross-member import Playwright coverage
  • desktop E2E production build and TypeScript typecheck
  • changed-file formatting/lint and file-size ratchet
  • pre-commit secret scan and DCO signoff

The branch was rebased onto current main, which includes the upstream attachment-button label fix. Fresh post-rebase GitHub CI is green for every required and selected check: Desktop Core, all four Desktop Smoke E2E shards, both Desktop E2E Integration shards and their aggregate, Desktop E2E Relay, Desktop Build (macOS), Windows Rust, Rust Lint, DCO, security scanners, and Desktop Release Candidate. The previously failing Desktop Smoke E2E (3) shard now passes.

The repository-wide desktop check also reports existing CSS formatting/!important findings in components.css and terminal.css; neither file is changed by this PR. GitHub's Desktop Core lint and format stage passes on the rebased branch.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f9536a5673

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread desktop/src-tauri/src/commands/personas/inbound.rs

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes on Wes's behalf after independent security, regression, and integration reviews of d56523fcbab119e78cc2a208c0b78cae55dfe4b3.

Two blockers remain:

  1. The catalog accepts unverified relay events as executable definitions and attributes them to attacker-selected pubkeys. A compromised relay can forge a member's shared agent and prompt a user to import arbitrary instructions.
  2. The validator rejects legitimate VS16/ZWJ emoji sequences, breaking edits to existing local agents and hiding valid shared definitions from the catalog.

The literal <pre> review surface, Rust ingress validation, snapshot/team-snapshot coverage, pre-retention validation, and TypeScript/Rust range parity otherwise look sound. Existing CI at this exact head is green; reviewers did not duplicate CI-equivalent suites locally.

Comment thread desktop/src-tauri/src/commands/personas/create.rs
Comment thread desktop/src/features/agents/lib/personaCatalogRelay.ts

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes on Wes's behalf after coordinated review of fc53800ef0d849a127d850598f35b9d2d867c099 with Princess Donut and Mongo.

The two prior blockers are fixed: catalog events are now verified on fresh wire-shaped objects before they can claim coordinates or influence paging, and the TS/Rust validators permit contextual VS16/ZWJ emoji while rejecting detached formatting. The literal review surface and catalog add path preserve exact prompt bytes. Mongo independently found no remaining issue in those paths.

One blocker remains: the Rust safety invariant is incomplete for other executable, relay-synced definition text.

  • Definition-less ManagedAgentRecord.system_prompt is executable, but create_managed_agent and update_managed_agent accept it without validate_agent_definition_text; those records are persisted and queued for publication. Inbound definition-less records do get validated, leaving an asymmetric local bypass.
  • Team instructions are executable, but team create/update and inbound kind 30176 apply them without the validator. Team snapshot validation validates member definitions but not the executable team.instructions (or the team name), then import persists those fields.

As a result, invisible/bidi text such as Review\u{200B}code can still be persisted, executed, and published through local managed-agent/team paths even though equivalent persona and inbound managed-agent definitions are rejected. Please centralize validation for executable name/instruction fields across create, update, import, sync, and publication boundaries, with direct regressions for definition-less agents and teams—or explicitly narrow the promised security contract and demonstrate why these executable fields are outside it.

Existing CI is green at this exact head. Focused local test attempts in my clean review worktree were environment-blocked (missing JS dependencies; missing Tauri sidecar), so I did not count them as validation. Mongo separately ran the focused TS suites: 44/44 passed.

@wesbillman

Copy link
Copy Markdown
Collaborator

Scope clarification on my changes-requested review, on Wes's behalf:

I agree the team-instructions work can be split into a follow-up. It is executable shared text and the gap is real, but this PR's concrete review surface is agent/persona definitions; extending the invariant to teams broadens the product/UI contract enough to justify separate work. Please track that follow-up before merge.

I do not think the definition-less managed-agent gap is separable. This PR explicitly added inbound validation for definition-less kind 30177 records and its description claims “definition-less managed-agent sync” plus validation at every agent-definition boundary. Leaving local create/update able to persist, execute, and publish the same unsafe prompt makes that new boundary asymmetric and directly contradicts the PR's stated invariant.

So the narrowed blocker is: validate definition-less managed-agent name/prompt on local create/update (and at publication as defense in depth), with direct regressions. Team validation may follow separately with an issue/PR.

Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewing e6aa35849204fc33dd9bab846e2987b637e32e7d on Wes's behalf.

The remaining in-scope blocker from my prior changes-requested review is fixed:

  • local definition-less agent create validates the trimmed name and executable prompt before persistence;
  • update applies the prospective prompt/name, validates the complete record, and returns before save on failure;
  • kind 30177 inbound sync validates before retention;
  • publication validates again as defense in depth;
  • linked records continue to treat their record-level prompt as inert and persona-authoritative.

I also re-traced catalog signature verification, coordinate selection, cursor derivation, literal prompt rendering, persona create/update/import, and agent snapshot decode/import at this head. I found no new actionable defect in the PR's narrowed agent/persona scope. Existing required CI is green for this exact head; I did not duplicate the broad suites locally.

Two merge-state notes remain:

  1. GitHub currently reports this branch as conflicting with main (baseRefOid is 96ae141763e5459beb68a847e0082e931af72f4c while current origin/main is 538e5e113fc33571f939c87b925567fd4e277109), so the rebased result still needs review/CI.
  2. The agreed follow-up for executable team names/instructions is not present in this PR, and I found no matching open or closed issue in block/buzz using searches for team-instruction Unicode/zero-width/bidi validation. Please track that before merge as previously agreed. Team create/update, inbound kind 30176, publication, and team snapshot validation remain outside this patch.

I am leaving this as a comment rather than approval: Wes requested review, not approval.

@shellz-n-stuff
shellz-n-stuff force-pushed the codex/harden-shared-agent-instructions branch from e6aa358 to e19bf1e Compare August 10, 2026 23:21
@shellz-n-stuff
shellz-n-stuff dismissed wesbillman’s stale review August 13, 2026 03:11

Approved by will and feedback addressed

@shellz-n-stuff
shellz-n-stuff merged commit a96af89 into block:main Aug 13, 2026
26 checks passed
setar pushed a commit to setar/buzz that referenced this pull request Aug 13, 2026
Upstream changes merged:
- Refine channel settings and profile panels (block#5574)
- Harden shared agent instruction review (block#4220)
- feat(deletion): add durable whole-community deletion (block#4425)
- Various agent, relay, and UI improvements

Conflict resolutions: same as feat/i18n-desktop-ru merge —
i18n t() calls preserved with upstream structural changes adopted.

Locale sync: en.json + ru.json brought up to date with feat/i18n-desktop-ru
(25 new channel.* keys added).

Signed-off-by: Sergey Taranenko <s.taranenko@vdi-service.ru>
wpfleger96 pushed a commit that referenced this pull request Aug 13, 2026
…-projection

* origin/main:
  Harden shared agent instruction review (#4220)
  chore(release): release Buzz Desktop version 0.5.11 (#5714)
  feat(acp): report standard adapter usage (#4950)
  fix(mobile): settle hydrated threads on latest reply (#4702)
  perf(desktop): persist channel snapshot hash (#5684)
  fix(agent): raise output limit and allow 3 recoveries (#5475)
  fix(desktop): defer foreground resume work (#5696)

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>

# Conflicts:
#	desktop/src-tauri/src/commands/personas/update.rs
#	desktop/src-tauri/src/managed_agents/mod.rs
wpfleger96 pushed a commit that referenced this pull request Aug 13, 2026
…n-surface

* origin/main:
  Harden shared agent instruction review (#4220)

Signed-off-by: Hayt <41ea58f1e64c243627e8acde7c89be667052ee6e17d8f021c1195be4324ebf04@buzz.block.builderlab.xyz>
morgmart added a commit that referenced this pull request Aug 13, 2026
…graphy-staging

* origin/main:
  Harden shared agent instruction review (#4220)
  chore(release): release Buzz Desktop version 0.5.11 (#5714)
  feat(acp): report standard adapter usage (#4950)
  fix(mobile): settle hydrated threads on latest reply (#4702)
  perf(desktop): persist channel snapshot hash (#5684)
  fix(agent): raise output limit and allow 3 recoveries (#5475)
  fix(desktop): defer foreground resume work (#5696)
  perf(desktop): coalesce thread-activity localStorage writes (#5693)
  Batch observer-store publications per relay envelope (#5680)
  feat(buzz-acp): idle re-sleep for woken lazy pools (#5682)
  fix(desktop): preserve agent mention separator after send (#5623)
  fix(link-previews): proxy sent preview media (#5627)
  feat(deletion): add durable whole-community deletion (#4425)
  fix(desktop): preserve live channel timelines (#5662)
  Refine channel settings and profile panels (#5574)
  fix(deps): bump webbrowser to 1.2.4 for RUSTSEC-2026-0257 (#5659)
  feat(acp): deliver channel description in prompt [Context] (#4552)
  fix(desktop): launch Databricks OAuth from passive model discovery (#5607)

Signed-off-by: morgmart <98432065+morgmart@users.noreply.github.com>
tlongwell-block pushed a commit that referenced this pull request Aug 13, 2026
* origin/main:
  Harden shared agent instruction review (#4220)

Signed-off-by: tlongwell-block <tlongwell@block.xyz>
tlongwell-block pushed a commit that referenced this pull request Aug 13, 2026
…ent-config

* origin/main: (31 commits)
  Harden shared agent instruction review (#4220)
  chore(release): release Buzz Desktop version 0.5.11 (#5714)
  feat(acp): report standard adapter usage (#4950)
  fix(mobile): settle hydrated threads on latest reply (#4702)
  perf(desktop): persist channel snapshot hash (#5684)
  fix(agent): raise output limit and allow 3 recoveries (#5475)
  fix(desktop): defer foreground resume work (#5696)
  perf(desktop): coalesce thread-activity localStorage writes (#5693)
  Batch observer-store publications per relay envelope (#5680)
  feat(buzz-acp): idle re-sleep for woken lazy pools (#5682)
  fix(desktop): preserve agent mention separator after send (#5623)
  fix(link-previews): proxy sent preview media (#5627)
  feat(deletion): add durable whole-community deletion (#4425)
  fix(desktop): preserve live channel timelines (#5662)
  Refine channel settings and profile panels (#5574)
  fix(deps): bump webbrowser to 1.2.4 for RUSTSEC-2026-0257 (#5659)
  feat(acp): deliver channel description in prompt [Context] (#4552)
  fix(desktop): launch Databricks OAuth from passive model discovery (#5607)
  chore(release): release Buzz Desktop version 0.5.10 (#5613)
  fix(desktop): remove 0.5.9+ perf regressions, speed up get_channels (#5599)
  ...

Co-authored-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>
Signed-off-by: Eva <011987e296fd5006292d2f930b574be47c7801048d1983c46c425d3c95f0cffd@buzz.block.builderlab.xyz>

# Conflicts:
#	desktop/src-tauri/src/commands/personas/inbound.rs
#	desktop/src-tauri/src/commands/personas/inbound/inbound_tests.rs
TechPrieto added a commit to TechPrieto/buzz that referenced this pull request Aug 13, 2026
Resolved conflicts in crates/buzz-acp/src/acp.rs and config.rs (additive
struct fields/tests from both sides, kept), and
mobile/lib/features/channels/thread_detail_page.dart (took upstream's
tail-follow scroll rework from block#4702, kept our shouldShowNestedThreadSummary
gating from feat/named-conversation-threads).

Notable upstream change: block#4220 hardens shared agent instruction review
(literal-text rendering, Unicode control rejection, signature verification
before trusting catalog content).
wpfleger96 added a commit that referenced this pull request Aug 13, 2026
…ties-manifest

* origin/main:
  Harden shared agent instruction review (#4220)
  chore(release): release Buzz Desktop version 0.5.11 (#5714)
  feat(acp): report standard adapter usage (#4950)
  fix(mobile): settle hydrated threads on latest reply (#4702)
  perf(desktop): persist channel snapshot hash (#5684)
  fix(agent): raise output limit and allow 3 recoveries (#5475)
  fix(desktop): defer foreground resume work (#5696)
  perf(desktop): coalesce thread-activity localStorage writes (#5693)
  Batch observer-store publications per relay envelope (#5680)
  feat(buzz-acp): idle re-sleep for woken lazy pools (#5682)

Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
wpfleger96 added a commit that referenced this pull request Aug 13, 2026
* origin/main:
  Harden shared agent instruction review (#4220)
  chore(release): release Buzz Desktop version 0.5.11 (#5714)
  feat(acp): report standard adapter usage (#4950)
  fix(mobile): settle hydrated threads on latest reply (#4702)
  perf(desktop): persist channel snapshot hash (#5684)
  fix(agent): raise output limit and allow 3 recoveries (#5475)
  fix(desktop): defer foreground resume work (#5696)
  perf(desktop): coalesce thread-activity localStorage writes (#5693)
  Batch observer-store publications per relay envelope (#5680)
  feat(buzz-acp): idle re-sleep for woken lazy pools (#5682)
  fix(desktop): preserve agent mention separator after send (#5623)
  fix(link-previews): proxy sent preview media (#5627)
  feat(deletion): add durable whole-community deletion (#4425)
  fix(desktop): preserve live channel timelines (#5662)

Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
yjc801 added a commit to yjc801/buzz that referenced this pull request Aug 13, 2026
## Summary

- render shared-agent instructions as literal text so Markdown cannot
conceal spoiler contents, link destinations, or image sources
- reject non-reviewable Unicode controls at every agent-definition
boundary while preserving legitimate rendered emoji sequences
- verify shared catalog event IDs and signatures before trusting
authorship, coordinates, pagination, or executable content
- preserve the exact system-prompt bytes between review and execution
instead of silently stripping or normalizing content

## Security rationale

Shared system prompts are executable configuration. Previously, catalog
prompts were projected through the chat Markdown renderer, which could
hide text, replace link destinations with benign labels, and turn image
syntax into remote loads. Zero-width and bidirectional controls could
also make reviewed text differ from what the agent executes.

This change establishes a review invariant: the prompt a user sees is
the prompt the agent executes. Definitions that cannot be reviewed
faithfully are rejected rather than rewritten. Catalog events must also
pass Nostr ID/signature verification before they can claim a publisher,
coordinate, or cursor.

## What changed

- catalog instructions render as exact literal text rather than rich
Markdown
- catalog relay events are verified on a fresh wire-shaped object before
paging, coordinate selection, attribution, or projection
- forged content, pubkeys, signatures, and invalid newer heads are
ignored and cannot shadow a valid signed definition
- TypeScript catalog parsing rejects unsafe remote definitions before
they reach the UI
- shared Rust validation covers persona create/update/import, inbound
relay sync, definition-less managed-agent sync, and catalog publication
paths
- definition-less managed agents now fail closed on local create, local
update, and publication before persistence or relay retention
- linked managed agents validate their local name while treating the
persona definition as authoritative; their inert record-level prompt is
not executed or published
- names reject layout controls; prompts retain ordinary newlines and
tabs
- legitimate emoji composition is supported, including contextual VS16,
ZWJ, skin-tone, family, flag, and keycap sequences
- detached selectors/joiners, bidirectional controls, tag characters,
zero-width concealment, and other default-ignorables remain rejected
- names are bounded to 128 characters and prompts to 64 KiB
- contributor guidance documents the byte-for-byte review requirement
for future sharing paths

Validation reports the offending code point and never silently removes
it.

## E2E recording


[buzz-shared-agent-security-e2e.webm](https://github.com/user-attachments/assets/44d6b75f-0877-490f-bda4-a716fae3f700)

The recording demonstrates:

- a safe definition remains visible
- a prompt containing zero-width `U+200B` is rejected
- a name containing bidi override `U+202E` is rejected
- the prompt is preserved exactly
- spoiler, link, and image syntax remains literal and does not render or
load

## Verification

Passed locally:

- `just test`: all 10 unit and Docker-backed integration stages
- desktop frontend unit suite: 4,295 tests
- persona catalog relay unit suite: 32 tests, including forged-event and
cursor-shadowing cases
- focused Rust definition-validation coverage: 3 local create/update
tests and 6 publication-filtered tests
- complete desktop Tauri library suite after rebase: 2,263 passed, 14
ignored, 0 failed
- desktop Tauri clippy with warnings denied and Rust formatting
- complete agent Playwright spec: 34 tests
- the exact formerly failing `inbox-edit` immediate-attachment smoke
test after rebase: 1 test
- focused shared-agent publish, literal-review, hidden-control,
signature, and cross-member import Playwright coverage
- desktop E2E production build and TypeScript typecheck
- changed-file formatting/lint and file-size ratchet
- pre-commit secret scan and DCO signoff

The branch was rebased onto current `main`, which includes the upstream
attachment-button label fix. Fresh post-rebase GitHub CI is green for
every required and selected check: Desktop Core, all four Desktop Smoke
E2E shards, both Desktop E2E Integration shards and their aggregate,
Desktop E2E Relay, Desktop Build (macOS), Windows Rust, Rust Lint, DCO,
security scanners, and Desktop Release Candidate. The previously failing
`Desktop Smoke E2E (3)` shard now passes.

The repository-wide desktop check also reports existing CSS
formatting/`!important` findings in `components.css` and `terminal.css`;
neither file is changed by this PR. GitHub's Desktop Core lint and
format stage passes on the rebased branch.

---------

Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Co-authored-by: Alex Rosenzweig <64241648+shellz-n-stuff@users.noreply.github.com>
cameronhotchkies pushed a commit that referenced this pull request Aug 13, 2026
…read-context

* origin/main: (38 commits)
  Add mobile community invites (#5641)
  Harden shared agent instruction review (#4220)
  chore(release): release Buzz Desktop version 0.5.11 (#5714)
  feat(acp): report standard adapter usage (#4950)
  fix(mobile): settle hydrated threads on latest reply (#4702)
  perf(desktop): persist channel snapshot hash (#5684)
  fix(agent): raise output limit and allow 3 recoveries (#5475)
  fix(desktop): defer foreground resume work (#5696)
  perf(desktop): coalesce thread-activity localStorage writes (#5693)
  Batch observer-store publications per relay envelope (#5680)
  feat(buzz-acp): idle re-sleep for woken lazy pools (#5682)
  fix(desktop): preserve agent mention separator after send (#5623)
  fix(link-previews): proxy sent preview media (#5627)
  feat(deletion): add durable whole-community deletion (#4425)
  fix(desktop): preserve live channel timelines (#5662)
  Refine channel settings and profile panels (#5574)
  fix(deps): bump webbrowser to 1.2.4 for RUSTSEC-2026-0257 (#5659)
  feat(acp): deliver channel description in prompt [Context] (#4552)
  fix(desktop): launch Databricks OAuth from passive model discovery (#5607)
  chore(release): release Buzz Desktop version 0.5.10 (#5613)
  ...

Signed-off-by: Lazy Joe <dbd8c9941ba6dafebcef0abc015b65e75d52e7452f2ce483c9c3fd4d180f2504@buzz.block.builderlab.xyz>
tellaho added a commit that referenced this pull request Aug 13, 2026
…icit-settling

* origin/main:
  fix(desktop): more compact "compact" link previews (#5629)
  Fix mobile composer input regressions (#5594)
  Add mobile community invites (#5641)
  Harden shared agent instruction review (#4220)
  chore(release): release Buzz Desktop version 0.5.11 (#5714)
  feat(acp): report standard adapter usage (#4950)
  fix(mobile): settle hydrated threads on latest reply (#4702)
  perf(desktop): persist channel snapshot hash (#5684)
  fix(agent): raise output limit and allow 3 recoveries (#5475)
  fix(desktop): defer foreground resume work (#5696)

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
shelman09 added a commit to Namleh-Studios/buzz that referenced this pull request Aug 13, 2026
* fix(buzz-agent): harden Databricks OAuth token cache and callback (block#5534)

Hardens the Databricks PKCE OAuth code in
`crates/buzz-agent/src/auth.rs`. Two fixes.

## Token cache is owner-only across its whole lifecycle, and race-safe

The PKCE cache holds both the access and refresh tokens, but `save()`
wrote it with a bare `fs::write` + `fs::rename`. Under a `022` umask the
file landed world-readable, and the fixed `*.json.tmp` temp name races
across concurrent savers sharing `$HOME` — one writer's `rename` can
fail on another's half-written temp.

**On write**, `write_private_cache()` creates a temp file with
owner-only permissions from the moment it exists — mode `0o600` on Unix
via `OpenOptions::mode` — writes and fsyncs it, then renames over the
destination. The rename swaps the inode wholesale, so a pre-existing
cache file with loose permissions is *replaced* by the new private inode
rather than inheriting its mode. `unique_suffix()` (getrandom, timestamp
fallback) gives each write a distinct temp name, and a drop guard
removes the temp on any failure path.

**On load**, owner-only is enforced as a cache lifecycle invariant, not
just a write-path property. A world-readable cache left by an older
buzz-agent was previously read straight into memory and returned on the
fresh cache-hit path without ever invoking `save()`, so a token file
with no advertised expiry could stay exposed indefinitely.
`read_cache()` now funnels every load — initial and cross-process
re-reads — through `read_private_cache()`, which on Unix opens with
`O_NOFOLLOW` (kernel-level symlink refusal, no stat/open TOCTOU),
requires a regular file, and `fchmod`s the pinned handle to `0o600` when
any group/other bit is set. A cache that cannot be secured is treated as
absent, so callers fail closed to a fresh flow rather than trusting an
exposed file.

## OAuth callback no longer reflects untrusted input

The localhost callback embedded the untrusted `error` query param
straight into the HTML response — an XSS sink on the redirect page — and
routed that same raw value into the error string that reaches the logs.

`callback_outcome()` is now a pure function returning `(result,
static_page)`: the browser always sees a fixed literal page that embeds
no request parameter, and failure detail travels only through the result
channel. `sanitize_callback_detail()` strips control characters (CR/LF
log-line injection) and caps length before that detail enters the error
string bound for the logs.

## Deferred: Windows owner-only ACLs

Windows owner-only protection is out of scope for this change. The
goose-parity route (`CreateFileW` with an owner-only SDDL
`D:P(A;;FA;;;OW)`) requires `unsafe` FFI, which this crate's
`#![forbid(unsafe_code)]` prohibits; reconciling that conflict is a
separate decision. Both platform seams — `create_private_temp_file`
(write) and `read_private_cache` (load) — have a `#[cfg(not(unix))]`
branch that relies on the default per-user ACLs and is the drop-in point
if Windows protection is added later. No new dependency and no `unsafe`
are introduced here.

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Hayt <41ea58f1e64c243627e8acde7c89be667052ee6e17d8f021c1195be4324ebf04@buzz.block.builderlab.xyz>
(cherry picked from commit 5e4d0fe)

* fix(desktop): launch Databricks OAuth from passive model discovery (block#5607)

When a user's agent runtime is `buzz-agent` with no cached Databricks
OAuth token, the desktop app's passive model-discovery surfaces were
forbidden from launching interactive auth. Discovery failed silently, so
the model dropdown showed only built-in fallback models behind a vague
"Could not load live models for `databricks_v2`" note (reported
internally by Nick and Jose).

## What changed

Both discovery surfaces — the passive draft-form discovery and the
explicit saved-model picker — now launch the browser OAuth flow,
matching goose's behavior. The only behavioral difference between them
is cooldown handling:

- **Passive draft discovery** fires on every form-state change, so a
failed, cancelled, or timed-out sign-in records a per-host cooldown (5
min) that suppresses re-popping the browser on the next keystroke. While
the cooldown is active it returns the "sign-in required" guidance
instead of relaunching.
- **The explicit model picker** is a deliberate user action, so it
always launches and clears any stale cooldown first.

Safety rails:

- A 150s hard timeout (`AUTH_FLOW_TIMEOUT`) bounds the whole interactive
flow so an abandoned SSO tab fails discovery cleanly rather than wedging
the dropdown. Success clears the cooldown; failure and timeout both
record it.
- `AuthCooldown` recovers from a poisoned lock rather than wedging every
future sign-in on one panic.

The frontend maps the terminal Databricks sign-in states to typed,
actionable copy in `formatModelDiscoveryErrorStatus`: "sign-in required"
is a muted note pointing at the picker and `buzz-agent auth databricks`;
a failed or timed-out sign-in is a warning pointing at the explicit
retry. Other Databricks failures fall through to the existing generic
notice.

## Scope

Changes are confined to Databricks discovery and its frontend status
formatter — no `agent_models.rs` call sites are touched. The
interactive-auth helper takes an injected timeout so the
timeout/cooldown policy is unit-testable without a live browser.

## Deferred

Cooldown keys use the raw trimmed `DATABRICKS_HOST`, while the catalog
and OAuth cache normalize trailing slashes
(`crates/buzz-agent/src/catalog.rs:96`,
`crates/buzz-agent/src/llm.rs:2046`). So `https://workspace/` and
`https://workspace` share credentials but get separate cooldown entries
— an equivalent-spelling change to the host field mid-cooldown can
re-pop passive OAuth once within the 5-minute window. Self-limiting (one
extra browser launch, never auth corruption). Follow-up: a
`trim_end_matches('/')` on the cooldown key plus an equivalent-host
test, picked up with the coordinator migration if
[block#5545](block#5545) ever merges.

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
(cherry picked from commit 1ff98fa)

* fix(relay): stop panicking the ingest worker on reactions to project events (block#5294)

A NIP-25 reaction whose target is a project root or project comment
(kind
1621 issue, 1618 PR, or a kind-1 comment on one) carries no h tag, so
channel_id is None on the reaction write path. The conformance-trace
emission asserted a channel was always present:

channel: channel_label(channel_id.expect("reaction path has channel")),

so the worker panicked at ingest.rs:2824. The row was inserted before
the
panic, so the client saw a failed request for a persisted event and
retried,
and the duplicate branch carried the same expect, head-of-line blocking
a
durable publish queue forever.

Mirror the message write's three-way split at the same seam:
(Some, true) -> WriteInsert, (Some, false) -> WriteDuplicate, (None, _)
-> WriteInsertGlobal. The conformance vocabulary already models
channel-less
writes; only the reaction path was missing it.

Closes block#4936

Signed-off-by: Taksh <takshkothari09@gmail.com>
Signed-off-by: Ravneet Arora <rarora@squareup.com>
(cherry picked from commit 16b7ae7)

* Harden shared agent instruction review (block#4220)

## Summary

- render shared-agent instructions as literal text so Markdown cannot
conceal spoiler contents, link destinations, or image sources
- reject non-reviewable Unicode controls at every agent-definition
boundary while preserving legitimate rendered emoji sequences
- verify shared catalog event IDs and signatures before trusting
authorship, coordinates, pagination, or executable content
- preserve the exact system-prompt bytes between review and execution
instead of silently stripping or normalizing content

## Security rationale

Shared system prompts are executable configuration. Previously, catalog
prompts were projected through the chat Markdown renderer, which could
hide text, replace link destinations with benign labels, and turn image
syntax into remote loads. Zero-width and bidirectional controls could
also make reviewed text differ from what the agent executes.

This change establishes a review invariant: the prompt a user sees is
the prompt the agent executes. Definitions that cannot be reviewed
faithfully are rejected rather than rewritten. Catalog events must also
pass Nostr ID/signature verification before they can claim a publisher,
coordinate, or cursor.

## What changed

- catalog instructions render as exact literal text rather than rich
Markdown
- catalog relay events are verified on a fresh wire-shaped object before
paging, coordinate selection, attribution, or projection
- forged content, pubkeys, signatures, and invalid newer heads are
ignored and cannot shadow a valid signed definition
- TypeScript catalog parsing rejects unsafe remote definitions before
they reach the UI
- shared Rust validation covers persona create/update/import, inbound
relay sync, definition-less managed-agent sync, and catalog publication
paths
- definition-less managed agents now fail closed on local create, local
update, and publication before persistence or relay retention
- linked managed agents validate their local name while treating the
persona definition as authoritative; their inert record-level prompt is
not executed or published
- names reject layout controls; prompts retain ordinary newlines and
tabs
- legitimate emoji composition is supported, including contextual VS16,
ZWJ, skin-tone, family, flag, and keycap sequences
- detached selectors/joiners, bidirectional controls, tag characters,
zero-width concealment, and other default-ignorables remain rejected
- names are bounded to 128 characters and prompts to 64 KiB
- contributor guidance documents the byte-for-byte review requirement
for future sharing paths

Validation reports the offending code point and never silently removes
it.

## E2E recording

[buzz-shared-agent-security-e2e.webm](https://github.com/user-attachments/assets/44d6b75f-0877-490f-bda4-a716fae3f700)

The recording demonstrates:

- a safe definition remains visible
- a prompt containing zero-width `U+200B` is rejected
- a name containing bidi override `U+202E` is rejected
- the prompt is preserved exactly
- spoiler, link, and image syntax remains literal and does not render or
load

## Verification

Passed locally:

- `just test`: all 10 unit and Docker-backed integration stages
- desktop frontend unit suite: 4,295 tests
- persona catalog relay unit suite: 32 tests, including forged-event and
cursor-shadowing cases
- focused Rust definition-validation coverage: 3 local create/update
tests and 6 publication-filtered tests
- complete desktop Tauri library suite after rebase: 2,263 passed, 14
ignored, 0 failed
- desktop Tauri clippy with warnings denied and Rust formatting
- complete agent Playwright spec: 34 tests
- the exact formerly failing `inbox-edit` immediate-attachment smoke
test after rebase: 1 test
- focused shared-agent publish, literal-review, hidden-control,
signature, and cross-member import Playwright coverage
- desktop E2E production build and TypeScript typecheck
- changed-file formatting/lint and file-size ratchet
- pre-commit secret scan and DCO signoff

The branch was rebased onto current `main`, which includes the upstream
attachment-button label fix. Fresh post-rebase GitHub CI is green for
every required and selected check: Desktop Core, all four Desktop Smoke
E2E shards, both Desktop E2E Integration shards and their aggregate,
Desktop E2E Relay, Desktop Build (macOS), Windows Rust, Rust Lint, DCO,
security scanners, and Desktop Release Candidate. The previously failing
`Desktop Smoke E2E (3)` shard now passes.

The repository-wide desktop check also reports existing CSS
formatting/`!important` findings in `components.css` and `terminal.css`;
neither file is changed by this PR. GitHub's Desktop Core lint and
format stage passes on the rebased branch.

---------

Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
(cherry picked from commit a96af89)

* fix: reject non-reviewable Unicode formatting

* fix: close upstream security review gaps

* fix: authorize inbound agent sync events

---------

Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Taksh <takshkothari09@gmail.com>
Signed-off-by: Ravneet Arora <rarora@squareup.com>
Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Hayt <41ea58f1e64c243627e8acde7c89be667052ee6e17d8f021c1195be4324ebf04@buzz.block.builderlab.xyz>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: Taksh Kothari <takshkothari09@gmail.com>
Co-authored-by: Alex Rosenzweig <64241648+shellz-n-stuff@users.noreply.github.com>
tellaho added a commit that referenced this pull request Aug 13, 2026
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>

* origin/main:
  Polish glass Huddle tray behavior (#5590)
  test: add deterministic desktop release smoke (#5699)
  fix(channels): return complete member rosters (#5765)
  feat(desktop): add Inbox message delete action (#5779)
  fix(desktop): enforce agent mention authorization at send boundaries (#5681)
  fix(desktop): route compact preview geometry fixture through media proxy (#5799)
  Make workflow run history authoritative in Desktop (#5780)
  fix(desktop): more compact "compact" link previews (#5629)
  Fix mobile composer input regressions (#5594)
  Add mobile community invites (#5641)
  Harden shared agent instruction review (#4220)
  chore(release): release Buzz Desktop version 0.5.11 (#5714)
  feat(acp): report standard adapter usage (#4950)
  fix(mobile): settle hydrated threads on latest reply (#4702)
  perf(desktop): persist channel snapshot hash (#5684)

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
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.

3 participants