Class: Vangrail::Rails::Markup

Inherits:
Vangrail::Rail show all
Defined in:
lib/vangrail/rails/markup.rb

Overview

Removes markup that does something when the answer is rendered.

An answer is text until a client renders it, and most clients render markdown by pulling in a library that passes raw HTML straight through. At that point a script tag in the answer is a script tag in the page, and the model wrote it because a retrieved document told it to. That is a cross-site scripting bug with a language model in the middle of it, and the fact that the model is the delivery mechanism does not make it a different class of bug.

The application's own sanitiser is the real defence and this does not replace it. What this covers is the case where there is no sanitiser, which is most of them, and where the answer is passed to a renderer that was chosen for how its tables look.

Removes rather than blocks. An answer with a script tag in it is an answer with one bad span, the same as an answer with a credential in it.

Not on by default. A desk whose client renders markdown as text, or which escapes before rendering, does not need it, and a rail that strips markup nobody was going to execute is noise in the result.

Constant Summary collapse

PATTERNS =
{
  # Executes on load.
  'script' => /<script\b[^>]*>.*?<\/script>|<script\b[^>]*\/?>/mi,
  # Loads and executes something else.
  'frame' => /<(?:iframe|frame|embed|object|applet)\b[^>]*>(?:.*?<\/(?:iframe|frame|embed|object|applet)>)?/mi,
  # Runs on an event, which is how a lone img tag becomes an exploit.
  'event_handler' => /\son[a-z]{3,20}\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)/i,
  # A scheme that executes rather than fetches.
  'active_scheme' => /(?:javascript|vbscript|data)\s*:\s*[^\s"'<>)]+/i,
  # Rewrites where a form or a link goes, or what the page loads next.
  'meta_refresh' => /<meta\b[^>]*http-equiv\s*=\s*["']?refresh["']?[^>]*>/i,
  'base_tag' => /<base\b[^>]*>/i,
  'form' => /<form\b[^>]*>.*?<\/form>|<form\b[^>]*>/mi,
  # Styling can position an invisible overlay over the page.
  'style_block' => /<style\b[^>]*>.*?<\/style>/mi
}.freeze

Constants inherited from Vangrail::Rail

Vangrail::Rail::DEFAULT_SIDES, Vangrail::Rail::SIDES

Instance Attribute Summary collapse

Attributes inherited from Vangrail::Rail

#name, #sides

Instance Method Summary collapse

Methods inherited from Vangrail::Rail

#applies_to?, #placeholder?, #to_s

Constructor Details

#initialize(patterns: PATTERNS, name: 'markup', sides: [:output]) ⇒ Markup

Returns a new instance of Markup.



48
49
50
51
# File 'lib/vangrail/rails/markup.rb', line 48

def initialize(patterns: PATTERNS, name: 'markup', sides: [:output])
  super(name: name, sides: sides)
  @patterns = patterns
end

Instance Attribute Details

#patternsObject (readonly)

Returns the value of attribute patterns.



46
47
48
# File 'lib/vangrail/rails/markup.rb', line 46

def patterns
  @patterns
end

Instance Method Details

#cache_key(text, _context) ⇒ Object



57
58
59
# File 'lib/vangrail/rails/markup.rb', line 57

def cache_key(text, _context)
  text
end

#call(text, _context) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/vangrail/rails/markup.rb', line 61

def call(text, _context)
  body = text.to_s
  found = []
  cleaned = patterns.reduce(body) do |acc, (label, pattern)|
    acc.gsub(pattern) do
      found << label
      ''
    end
  end
  return pass if found.empty?

  modify(cleaned, categories: found.uniq,
                  reason: "removed #{found.uniq.join(', ')} from the answer")
end

#offline?Boolean

Returns:

  • (Boolean)


53
54
55
# File 'lib/vangrail/rails/markup.rb', line 53

def offline?
  true
end