Class: Vangrail::Rails::InjectedInstructions
- Inherits:
-
Vangrail::Rail
- Object
- Vangrail::Rail
- Vangrail::Rails::InjectedInstructions
- Defined in:
- lib/vangrail/rails/injected_instructions.rb
Overview
Reads a retrieved document for instructions aimed at the model.
This is the rail most stacks are missing. The input rail checks what the user typed and the output rail checks what the model wrote, and neither ever looks at the wiki page, search result, or file that the application pasted into the prompt in between. That page is the part an attacker can usually edit without touching the application at all.
Deterministic on purpose. The text is written by whoever wants it to be believed, so a model asked to judge it is reading an argument composed to persuade it. A pattern cannot be argued with, costs microseconds, and keeps working when the endpoint does not.
Patterns match shapes that have no honest reason to appear in documentation: a role header mid-page, an override of "the above", a claim about what the assistant must now do. Ordinary prose that happens to discuss instructions is not a hit, because a handbook says "follow the instructions above" constantly.
Constant Summary collapse
- PATTERNS =
{ # A chat role header inside a document: nothing in prose needs one. 'role_header' => /^\s{0,3}(?:###\s*)?(?:system|assistant|user)\s*:\s*\S/i, # An override aimed at whatever came before it in the prompt. 'override_above' => /\b(?:ignore|disregard|forget|override)\b[^.\n]{0,40}\b (?:above|previous|prior|earlier|preceding|system\s+prompt|instructions?)\b/xi, # A document telling the assistant what it is now. 'role_assignment' => /\byou\s+(?:are|must\s+now\s+act|will\s+now\s+act)\s+(?:now\s+)? (?:a|an|the)?\s*(?:assistant|ai|model|chatbot|dan)\b/xi, # An instruction to conceal something *from the reader*, which is what # separates an injection from ordinary advice. # # Three narrowings, each paid for by a false positive found in the # corpus. "In your response" and "when answering" alone flag a handbook # talking to a human ("in your response to the service desk, include the # job id"). A bare "never mention" flags security advice ("never mention # a password in a ticket"). So the concealment has to be tied either to # the user or to the act of answering, in whichever order it is written. 'answer_shaping' => / \b(?:do\s+not|don't|never)\s+(?:mention|reveal|disclose|tell|say|admit)\b[^.\n]{0,40}? \b(?:to\s+(?:the\s+|this\s+)?user|to\s+them|in\s+your\s+(?:answer|response|reply)| when\s+you\s+(?:answer|respond))\b | \b(?:in\s+your\s+(?:answer|response|reply)|when\s+(?:you\s+)?(?:answer|answering|respond))\b[^.\n]{0,40}? \b(?:do\s+not|don't|never)\s+(?:mention|reveal|disclose|tell|say|admit)\b | \bwithout\s+(?:mentioning|telling)\s+(?:the\s+|this\s+)?user\b /xi, # Exfiltration shapes: a document asking for the prompt or the key. 'exfiltration' => /\b(?:reveal|print|repeat|output|send|post)\b[^.\n]{0,40}\b (?:system\s+prompt|api[_\s-]?key|token|credentials?|conversation)\b/xi, # Hidden text: a marker for content meant for the model and not the # reader. HTML comments in a rendered page are the common carrier. 'hidden_directive' => /<!--[^>]*\b(?:ignore|instruction|assistant|system|prompt)\b[^>]*-->/im }.freeze
Constants inherited from Vangrail::Rail
Vangrail::Rail::DEFAULT_SIDES, Vangrail::Rail::SIDES
Instance Attribute Summary collapse
-
#patterns ⇒ Object
readonly
Returns the value of attribute patterns.
Attributes inherited from Vangrail::Rail
Instance Method Summary collapse
- #cache_key(text, _context) ⇒ Object
- #call(text, _context) ⇒ Object
-
#initialize(patterns: PATTERNS, name: 'injected_instructions', sides: [:context]) ⇒ InjectedInstructions
constructor
A new instance of InjectedInstructions.
- #offline? ⇒ Boolean
Methods inherited from Vangrail::Rail
#applies_to?, #placeholder?, #to_s
Constructor Details
#initialize(patterns: PATTERNS, name: 'injected_instructions', sides: [:context]) ⇒ InjectedInstructions
Returns a new instance of InjectedInstructions.
64 65 66 67 |
# File 'lib/vangrail/rails/injected_instructions.rb', line 64 def initialize(patterns: PATTERNS, name: 'injected_instructions', sides: [:context]) super(name: name, sides: sides) @patterns = patterns end |
Instance Attribute Details
#patterns ⇒ Object (readonly)
Returns the value of attribute patterns.
62 63 64 |
# File 'lib/vangrail/rails/injected_instructions.rb', line 62 def patterns @patterns end |
Instance Method Details
#cache_key(text, _context) ⇒ Object
73 74 75 |
# File 'lib/vangrail/rails/injected_instructions.rb', line 73 def cache_key(text, _context) text end |
#call(text, _context) ⇒ Object
77 78 79 80 81 82 83 |
# File 'lib/vangrail/rails/injected_instructions.rb', line 77 def call(text, _context) body = text.to_s hits = patterns.select { |_label, pattern| pattern.match?(body) }.keys return pass if hits.empty? block(categories: hits, reason: "instructions found in retrieved text: #{hits.join(', ')}") end |
#offline? ⇒ Boolean
69 70 71 |
# File 'lib/vangrail/rails/injected_instructions.rb', line 69 def offline? true end |