8000 feat(assistant): flag third-party text that reads like instructions to an AI by adeelahmad · Pull Request #329 · adeelahmad/lense · GitHub
[go: up one dir, main page]

Skip to content

feat(assistant): flag third-party text that reads like instructions to an AI - #329

Merged
adeelahmad merged 1 commit into
mainfrom
claude/project-thread-mj7ggd
Oct 10, 2026
Merged

adeelahmad merged 1 commit into
mainfrom
claude/project-thread-mj7ggd

Conversation

@adeelahmad
Copy link
Copy Markdown
Owner

Requested by Adeel · project thread

Description

Before: a poisoned transcript, email, web page or tool result reached the model marked as data (#302), but nothing told the model or the person that a piece of it was trying to give orders, and Unicode's invisible tag characters could spell out instructions nobody sees on screen.

After: a tool result that says "ignore previous instructions", claims to be new instructions or a new role, addresses an AI reading it, asks to hide something from the person, to reveal the system prompt, to call a tool or to put data into a link, or carries chat control tokens or hidden characters, gets a warning on its marker for the model (<untrusted-… warning="reads like instructions to an AI: ignore previous instructions">), and the answer's step says so: "Read 12 line(s) of Mallory's talk (reads like instructions to an AI: ignore previous instructions)". Invisible tag characters are taken out of marked text.

Settings → AI assistant → Flag text that reads like instructions to an AI (ai.injection_check): patterns (the default, free, kept narrow so honest text isn't flagged), model (the decision model also checks each tool result the patterns let through: one routine call each, through the activity ledger), or off.

Third of the prompt-injection series (#302 marked outside text as data, #309 asks before sending data out). Next: chat-room link defanging, MCP result labelling, audit log rows, an optional local classifier, and the threat-model update.

Motivation and Context

Adeel asked to handle third-party prompt injection with layered security. This is the detection layer: independent of marking and approvals, a warning and never the one that stops an action (the approvals in #309 do that).

How: untrusted.flags(text, cfg, model=False) runs narrow regexes (each with a reason), a control-token check and a hidden-character check over the text and over the strings its JSON carries; with model=True and ai.injection_check = "model" it asks decide.choose when nothing matched. untrusted.wrap(..., why=) adds the warning attribute. Toolbox.call flags each untrusted tool result and adds the reason to the step summary.

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: the detection layer of the injection defenses; merged on green CI.
  • Agents: the flag shows in the answer's steps over the API and MCP; the setting is in the settings API.
  • Zero setup: patterns are on by default, nothing to install.
  • Fewest clicks and keystrokes: nothing to click; a warning only.
  • Configurable: ai.injection_check in Settings → AI assistant, default patterns.
  • Light: regexes by default; the model check is opt-in.
  • Private and secure: flags injection attempts for the model and the person; the threat-model update is the last PR of this series.
  • Cost: free by default; model is one decision-model call per tool result, in the activity ledger and under budgets.
  • User decides: a warning, never an action; existing defaults unchanged.
  • Upgrades: no stored data changes.
  • Honest: docs say patterns are narrow and miss rephrased attacks, and that a flag is a warning, not a block.
  • Tests (tests/domain/test_untrusted.py, tests/api/test_prompt_injection.py), docs (assistant.md); no generated files touched.
  • Open standards; no new dependencies.
  • Result reported with evidence; refine later: mark pipeline, template, workflow and graph-ask prompts too.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BVez82cREgCJtFk6yHReed


Generated by Claude Code

…o an AI

A tool result that says "ignore previous instructions", claims new
instructions or a new role, addresses an AI reading it, asks to hide
something from the person, reveal the system prompt, call a tool or put
data into a link, or carries control tokens or invisible Unicode tag
characters, gets a warning on its marker for the model, and the answer's
step says so. Invisible tag characters are taken out of marked text.

ai.injection_check: patterns (free, the default), model (the decision
model also checks what the patterns let through, one routine call per
tool result, through the activity ledger), or 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
@adeelahmad
adeelahmad merged commit d51c82a into main Oct 10, 2026
8 checks passed
@adeelahmad
adeelahmad deleted the claude/project-thread-mj7ggd branch October 10, 2026 22:48
adeelahmad added a commit that referenced this pull request Oct 10, 2026
…PRs (#335)

The README and Get started say the web installer can install with the defaults in one click (#331). "In progress"
adds flagging instruction-like third-party text (#329), asking before the browser acts on what the assistant wrote
(#333) and cloud model presets (#334).


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

Co-authored-by: Adeel Ahmad <noreply@anthropic.com>
adeelahmad added a commit that referenced this pull request Oct 11, 2026
…349)

"Stay private and safe" now covers flagging instruction-like text (patterns or the optional local classifier, #329,
#347), asking before the browser acts on what the assistant wrote (#333), breaking up links in chat-room answers
(#338), the archive as data over MCP (#343) and the audit log of what was caught (#344). "See what it costs" adds the
overall budget asked for in setup (#281). "In progress" drops what merged and adds #337, #341 and #346.


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 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