Module: Spambust::FormHelpers

Defined in:
lib/spambust/form_helpers.rb

Overview

Form helpers for sinatra or similar DSLs/frameworks to block for spams

Examples:

app.rb

class TestApp < Sinatra::Base
  helpers Spambust::FormHelpers

  class << self
    def start_app
      run!
    end

    def direct_script_execution?
      app_file == $PROGRAM_NAME
    end
  end

  get '/' do
    erb :index, :locals => { :result => '...' }
  end

  post '/' do
    valid = valid?('user', params)
    result =  valid ? "Users is #{decrypt('user', params)}" : 'Faker!'
    erb :index, locals: { result: result }
  end

  start_app if direct_script_execution? && ENV['environment'] != 'test'
end

index.erb

<html>
<head>
  <title>Sample Sinatra application</title>
</head>
<body>
  <div id="result"><%= result %></div>
  <form method="post" action="/">
    <label for="user-first-name">First name</label>
    <%= input ["user", "first_name"], :id => "user-first-name" %>
    <label for="user-last-name">Last name</label>
    <%= input ["user", "last_name"], :id => "user-last-name" %>
    <label for="user-email">Email</label>
    <%= input ["user", "email"], :size => 40, :id => "user-email" %>
    <%= submit "Create account", :id => "user-submit" %>
  </form>
</body>
</html>

Constant Summary collapse

HIDING =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

'position:absolute;top:-10000px;left:-10000px;'
BLOCKED_OPTIONS =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

%i[id class style].freeze

Instance Method Summary collapse

Instance Method Details

#decrypt(lookup, global) ⇒ Object

Returns decrypted hash of user submitted POST parameters Use inside your application.

Examples:

decrypt('user', params)

Parameters:

  • lookup (String)
  • global (Hash)


146
147
148
149
150
151
152
153
154
# File 'lib/spambust/form_helpers.rb', line 146

def decrypt(lookup, global)
  fake = global[lookup] || {}
  hashed_lookup = digest(lookup)
  subset = global[hashed_lookup] || {}

  fake.each_with_object({}) do |(key, _value), real|
    real[key] = subset[digest(key)]
  end
end

#input(paths, options = {}) ⇒ String

Returns obfuscated input tags together with its fake input tags that are rendered off the screen

Use inside your templates to generate an obfuscated input field. This is the field that the server will use. If the server sees that fields with original names are filled, the server should assume it be be a spam. It also accepts options for input type and other CSS properties.

Examples:

input(['user', 'name'])
# => <input type="text" name="ee11cbb19052e40b07aac0ca060c23ee[b068931cc450442b63f5b3d276ea4297]" />\
#    <input type="text" name="user[name]" style="position:absolute;top:-10000px;left:-10000px;" />
input(['user', 'name'], type: 'password')
# => <input type="password" name="ee11cbb19052e40b07aac0ca060c23ee[b068931cc450442b63f5b3d276ea4297]" />\
#    <input type="text" name="user[name]" style="position:absolute;top:-10000px;left:-10000px;" />
input(['user', 'name'], id: 'name', class: 'name')
# => <input id="name" class="name" type="text" \
#    name="ee11cbb19052e40b07aac0ca060c23ee[b068931cc450442b63f5b3d276ea4297]" />\
#    <input type="text" name="user[name]" style="position:absolute;top:-10000px;left:-10000px;" />

Parameters:

  • paths (Array<String>)
  • options (Hash) (defaults to: {})

Returns:

  • (String)


92
93
94
95
96
97
98
99
100
101
# File 'lib/spambust/form_helpers.rb', line 92

def input(paths, options = {})
  type                = options.delete(:type) || 'text'
  sanitized_options   = options.reject { |key, _value| BLOCKED_OPTIONS.include?(key) }
  digested_paths      = paths.map { |path| digest(path) }
  visible_tag_options = options.merge(type: type, name: namify(digested_paths))
  hidden_tag_options  = sanitized_options.merge(type: 'text', name: namify(paths), style: HIDING)
  visible_tag = %(<input #{hash_to_options visible_tag_options} />)
  hidden_tag  = %(<input #{hash_to_options hidden_tag_options} />)
  "#{visible_tag}#{hidden_tag}"
end

#namify(paths) ⇒ String

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns a nested input name

Parameters:

  • paths (Array<String>)

Returns:

  • (String)


131
132
133
134
135
# File 'lib/spambust/form_helpers.rb', line 131

def namify(paths)
  first = paths[0]
  rest  = paths[1..-1].reduce([]) { |a, e| a << "[#{e}]" }.join('')
  "#{first}#{rest}"
end

#submit(text, options = {}) ⇒ String

Returns submit tags

Use inside your templates to generate a submit tag. It also accepts for CSS options.

Examples:

submit('Submit')
# => <input type="submit" value="Submit" />
submit('Submit', id: 'submit', class: 'submit')
# => <input id="submit" class="submit" type="submit" value="Submit" />

Parameters:

  • text (String)
  • options (Hash) (defaults to: {})

Returns:

  • (String)


120
121
122
123
# File 'lib/spambust/form_helpers.rb', line 120

def submit(text, options = {})
  visible_tag_options = options.merge(type: 'submit', value: text)
  %(<input #{hash_to_options visible_tag_options} />).gsub('  ', ' ')
end

#valid?(lookup, global) ⇒ Boolean

Returns if any POST data was present in the fake input fields

Use inside your application.

Examples:

valid?('user', params)

Parameters:

  • lookup (String)
  • global (Hash)

Returns:

  • (Boolean)


166
167
168
169
# File 'lib/spambust/form_helpers.rb', line 166

def valid?(lookup, global)
  fake = global[lookup] || {}
  fake.values.all?(&:empty?)
end