8000 feat(assistant): ask before sending data out once an answer read third-party text by adeelahmad · Pull Request #309 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(assistant): ask before sending data out once an answer read third-party text - #309

Merged
adeelahmad merged 3 commits into
mainfrom
claude/project-thread-mj7ggd
Oct 10, 2026
Merged

adeelahmad merged 3 commits into
mainfrom
claude/project-thread-mj7ggd

Conversation

@adeelahmad
Copy link
Copy Markdown
Owner

Requested by Adeel · project thread

Description

Before: after the assistant read a poisoned transcript, document, web page or chat-room message, it could call the person's own read web tools, another server's MCP tools or a Python tool with the network straight away, so an injection that said "look up https://evil.example/?d=" could carry data out. In a setup conversation, a change the admin's chat makes at once (moving llm.base_url, say) ran just as readily after reading such text.

After: once an answer has read text from outside Lens (a tool's result other than Lens's own, the page it was asked from, the namespace assistant's memories, a message in a chat room open to anyone, or an earlier answer in the conversation that read any of these), anything that could carry what it read out of Lens waits for the person's approval, even where it would otherwise run at once. The approval card says why: "Run lookup(q="every excerpt") (asked first: this answer read text from outside Lens: tool read_transcript)". Notes and memories are still written at once: they stay in Lens and are marked as data when read back (#302). Settings → AI assistant → Ask before sending data out after reading third-party text (ai.untrusted_asks, on) turns it off.

Second of the third-party prompt-injection series (#302 marked outside text as data). Next: a free injection-pattern flag, chat-room link defanging, MCP result labelling, and the threat-model update.

Motivation and Context

Adeel asked to handle third-party prompt injection. Marking text as data makes injection less likely; this bounds what a successful one can do: the person decides before anything leaves Lens or changes the server.

How: Toolbox keeps outside (what this answer read from outside Lens, plus read_before(history) for earlier answers whose steps read some); Toolbox.wary() gates extensions.sends_out(spec) tools (anything but prompt and graph tools, and Python tools without the network) and setup-mode _change in ops_tools. Chat rooms without bridge.users count as outside. Approvals that would happen anyway keep their old wording.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change; ! in the title)

Checklist:

  • The title is a Conventional Commit.
  • My code follows the code style of this project (make hooks checks it on commit).
  • I added or updated tests.
  • My change requires documentation updates, and I have updated docs/ accordingly.
  • Users need more than the title to understand it, so I wrote it under ## Unreleased in CHANGELOG.md. (n/a: the title and docs/assistant.md say it)

Done questions (AGENTS.md)

  • Goal: second layer of the injection defenses; merged on green CI.
  • Agents: the approval goes through the same approvals API and MCP as every other; the setting is in the settings API.
  • Zero setup: on by default, nothing to install.
  • Fewest clicks and keystrokes: one approval only where outside text could be steering an outbound call; reads stay instant.
  • Configurable: ai.untrusted_asks in Settings → AI assistant, default on.
  • Light: a list per answer; no model call.
  • Private and secure: closes the outbound-tool exfiltration path after reading untrusted text; the threat-model update comes in the last PR of this series.
  • Cost: no extra model calls.
  • User decides: this is the point: outbound and setup changes after reading outside text wait for the person.
  • Upgrades: no stored data changes (earlier answers' steps are read as stored).
  • Honest: docs say what asks first and what doesn't (notes, memories).
  • Tests (5 in tests/api/test_prompt_injection.py), docs (assistant.md, chat-rooms.md); no generated files touched.
  • Open standards; no new dependencies.
  • Result reported with evidence; refine later: notes and memories written in a turn that read flagged text could ask too.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BVez82cREgCJtFk6yHReed


Generated by Claude Code

…d-party text

Once an answer has read text from outside Lens (a tool's result other than
Lens's own, the page it was asked from, the namespace assistant's memories, a
message in a chat room open to anyone, or an earlier answer that read any of
these), tools that could carry it out of Lens wait for approval even where
they'd run at once: the person's own read web tools, other servers' MCP tools,
Python tools with the network, and the changes a setup conversation makes
itself. The approval card says why. ai.untrusted_asks (on by default) turns it
off.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVez82cREgCJtFk6yHReed
@adeelahmad adeelahmad self-assigned this Oct 10, 2026
The v0.7.0 release bumped the package version without regenerating
nextjs-frontend/openapi.json, so the generate-openapi-schema hook fails
on any pull request that touches the API. Regenerated with the
repository's own command; only the version changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVez82cREgCJtFk6yHReed
Copy link
Copy Markdown
Owner Author

pre-commit failed on generate OpenAPI schema: main's v0.7.0 release bumped the package version without regenerating nextjs-frontend/openapi.json (the diff is only "version": "0.6.0" → "0.7.0"), so the hook fails on any PR that touches app/api. Not this PR's change; I pushed the regenerated file (a6c7e17, from the repo's own command), which no-ops once main carries it.


Generated by Claude Code

…-mj7ggd

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVez82cREgCJtFk6yHReed
@adeelahmad
adeelahmad force-pushed the claude/project-thread-mj7ggd branch from 1b53354 to 22f8527 Compare October 10, 2026 21:54
@adeelahmad
adeelahmad merged commit f81139d into main Oct 10, 2026
8 checks passed
@adeelahmad
adeelahmad deleted the claude/project-thread-mj7ggd branch October 10, 2026 22:17
adeelahmad added a commit that referenced this pull request Oct 10, 2026
…s as built (#332)

#322, #330, #297 and #309 merged, so the README lists them as built and keeps
only open PRs under "In progress".


Claude-Session: https://claude.ai/code/session_01HDx7PPUauyjMn1LZs8JAsS

Co-authored-by: Adeel Ahmad <noreply@anthropic.com>
adeelahmad pushed a commit that referenced this pull request Oct 10, 2026
…tside text (#309)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdrxhwvmLjFsfzQZ5pRJGC
adeelahmad pushed a commit that referenced this pull request Oct 14, 2026
P-INJECTION-EGRESS: once an answer has read text from outside Lens,
outbound tools and setup changes ask first, and chat-room answers keep
only the links people, the archive, tools or Lens wrote. Correctness-only,
a bypass is VALID-HARDENING (Q26). The Assistant x-prompt-injection row
is now claimed; N-PROMPT says marking and flagging make injection less
likely and are not claimed; MCP results and the optional classifier are
described; Q26 lists #302, #309, #329, #338, #343, #344 and #347.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVez82cREgCJtFk6yHReed
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.

2 participants

0