spamtrap 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/spamtrap/controller.rb +48 -1
- data/lib/spamtrap/crypto.rb +46 -0
- data/lib/spamtrap/helper.rb +64 -13
- data/lib/spamtrap/version.rb +1 -1
- data/lib/spamtrap.rb +11 -0
- data/test/controller_test.rb +238 -4
- data/test/test_helper.rb +25 -1
- metadata +7 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7c25ad9a77e76067aa77db7ac9f97aad1efde11b3f44a1ea6547a26da434bedd
|
|
4
|
+
data.tar.gz: 29211ceda85283fb468690b305e942246b9a00afb50e10450ca5e9b4a2839d7f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ed39e2e15276baee291686c81c295bb18b180bc2d58240d48ef019cb1db36bcd90a05b67aa4e68c069d69b934a7e1533a1f16b22e8aec063310a1755cbacea64
|
|
7
|
+
data.tar.gz: 127e9108db3e7b72c0ea22f1ca586152f94a6cd6c2ba0645aa5b25dbb4c7ad1cb5249c2b7dd2587339297cbeddb0452fa549b84b143c1549f8ea8b431be248aa
|
data/lib/spamtrap/controller.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
module Spamtrap::Controller
|
|
2
|
+
include Spamtrap::Crypto
|
|
2
3
|
|
|
3
4
|
def self.included(base)
|
|
4
5
|
base.extend ActsAsMethods
|
|
@@ -6,18 +7,64 @@ module Spamtrap::Controller
|
|
|
6
7
|
|
|
7
8
|
module ActsAsMethods
|
|
8
9
|
def spamtrap(honeypot = 'spamtrap', options = {}, &block)
|
|
9
|
-
|
|
10
|
+
nonce_enabled = options.delete(:nonce)
|
|
11
|
+
nonce_timeout = options.delete(:nonce_timeout) || Spamtrap.nonce_timeout
|
|
12
|
+
mutate_enabled = options.delete(:mutate)
|
|
13
|
+
|
|
14
|
+
before_action(options) do |controller|
|
|
10
15
|
controller.instance_eval(&block) if block_given?
|
|
11
16
|
controller.instance_eval do
|
|
17
|
+
spamtrap_remap_params if mutate_enabled
|
|
18
|
+
|
|
12
19
|
if params[honeypot].present?
|
|
13
20
|
Rails.logger.warn "Spamtrap triggered by #{request.remote_ip}."
|
|
14
21
|
head 200
|
|
22
|
+
elsif nonce_enabled && !spamtrap_valid_nonce?(nonce_timeout)
|
|
23
|
+
Rails.logger.warn "Spamtrap nonce invalid from #{request.remote_ip}."
|
|
24
|
+
head 200
|
|
15
25
|
end
|
|
16
26
|
end
|
|
17
27
|
end
|
|
18
28
|
end
|
|
19
29
|
end
|
|
20
30
|
|
|
31
|
+
def spamtrap_valid_nonce?(timeout)
|
|
32
|
+
timestamp = params[:spamtrap_timestamp].to_i
|
|
33
|
+
nonce = params[:spamtrap_nonce].to_s
|
|
34
|
+
return false if timestamp.zero? || nonce.empty?
|
|
35
|
+
return false if Time.now.to_i - timestamp > timeout.to_i
|
|
36
|
+
|
|
37
|
+
secret = Rails.application.secret_key_base
|
|
38
|
+
expected = OpenSSL::HMAC.hexdigest('SHA256', secret, "#{timestamp}:#{request.remote_ip}")
|
|
39
|
+
ActiveSupport::SecurityUtils.secure_compare(nonce, expected)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def spamtrap_remap_params
|
|
43
|
+
salt_hex = params[:spamtrap_mutation_salt].to_s
|
|
44
|
+
return if salt_hex.empty?
|
|
45
|
+
|
|
46
|
+
salt_bytes = [salt_hex].pack('H*')
|
|
47
|
+
return unless salt_bytes.bytesize == Spamtrap::Crypto::NONCE_LEN
|
|
48
|
+
|
|
49
|
+
spamtrap_remap_hash(params, salt_bytes)
|
|
50
|
+
rescue ArgumentError
|
|
51
|
+
nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def spamtrap_remap_hash(hash, salt)
|
|
55
|
+
hash.each_key.to_a.each do |key|
|
|
56
|
+
real = spamtrap_decrypt_field(key.to_s, salt)
|
|
57
|
+
if real
|
|
58
|
+
hash[real] = hash.delete(key)
|
|
59
|
+
key = real
|
|
60
|
+
end
|
|
61
|
+
child = hash[key]
|
|
62
|
+
spamtrap_remap_hash(child, salt) if child.is_a?(ActionController::Parameters)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private :spamtrap_valid_nonce?, :spamtrap_remap_params, :spamtrap_remap_hash
|
|
67
|
+
|
|
21
68
|
end
|
|
22
69
|
|
|
23
70
|
ActionController::Base.send :include, Spamtrap::Controller
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
|
|
3
|
+
module Spamtrap
|
|
4
|
+
module Crypto
|
|
5
|
+
CIPHER = 'aes-128-gcm'
|
|
6
|
+
KEY_LEN = 16
|
|
7
|
+
NONCE_LEN = 12
|
|
8
|
+
TAG_LEN = 16
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def spamtrap_crypto_key
|
|
13
|
+
@spamtrap_crypto_key ||= OpenSSL::KDF.hkdf(
|
|
14
|
+
Rails.application.secret_key_base,
|
|
15
|
+
salt: 'spamtrap-mutation',
|
|
16
|
+
info: '',
|
|
17
|
+
length: KEY_LEN,
|
|
18
|
+
hash: 'SHA256'
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def spamtrap_encrypt_field(field_name, salt_bytes)
|
|
23
|
+
cipher = OpenSSL::Cipher.new(CIPHER)
|
|
24
|
+
cipher.encrypt
|
|
25
|
+
cipher.key = spamtrap_crypto_key
|
|
26
|
+
cipher.iv = salt_bytes
|
|
27
|
+
ct = cipher.update(field_name.to_s) + cipher.final
|
|
28
|
+
Base64.urlsafe_encode64(ct + cipher.auth_tag(TAG_LEN), padding: false)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def spamtrap_decrypt_field(token, salt_bytes)
|
|
32
|
+
raw = Base64.urlsafe_decode64(token)
|
|
33
|
+
return nil if raw.bytesize <= TAG_LEN
|
|
34
|
+
ct = raw[0, raw.bytesize - TAG_LEN]
|
|
35
|
+
tag = raw[-TAG_LEN..]
|
|
36
|
+
cipher = OpenSSL::Cipher.new(CIPHER)
|
|
37
|
+
cipher.decrypt
|
|
38
|
+
cipher.key = spamtrap_crypto_key
|
|
39
|
+
cipher.iv = salt_bytes
|
|
40
|
+
cipher.auth_tag = tag
|
|
41
|
+
(cipher.update(ct) + cipher.final).to_sym
|
|
42
|
+
rescue OpenSSL::Cipher::CipherError, ArgumentError
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/spamtrap/helper.rb
CHANGED
|
@@ -1,17 +1,68 @@
|
|
|
1
|
+
module Spamtrap
|
|
2
|
+
module FormBuilderMutation
|
|
3
|
+
include Spamtrap::Crypto
|
|
4
|
+
|
|
5
|
+
MUTABLE_FIELDS = %i[
|
|
6
|
+
text_field email_field password_field number_field url_field telephone_field
|
|
7
|
+
text_area check_box hidden_field file_field date_field time_field
|
|
8
|
+
datetime_local_field month_field week_field search_field color_field
|
|
9
|
+
range_field select collection_select grouped_collection_select label
|
|
10
|
+
].freeze
|
|
11
|
+
|
|
12
|
+
MUTABLE_FIELDS.each do |m|
|
|
13
|
+
define_method(m) do |field, *args, &blk|
|
|
14
|
+
field = spamtrap_encrypt_field(field.to_s, @spamtrap_salt) if @spamtrap_salt
|
|
15
|
+
super(field, *args, &blk)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(object_name, object, template, options)
|
|
20
|
+
super
|
|
21
|
+
@spamtrap_salt = options[:spamtrap_salt] if options[:spamtrap_salt]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def fields_for(record_name, record_object = nil, fields_options = {}, &block)
|
|
25
|
+
if @spamtrap_salt
|
|
26
|
+
if record_object.is_a?(Hash) && record_object.extractable_options?
|
|
27
|
+
record_object = record_object.merge(spamtrap_salt: @spamtrap_salt)
|
|
28
|
+
else
|
|
29
|
+
fields_options = fields_options.merge(spamtrap_salt: @spamtrap_salt)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
super(record_name, record_object, fields_options, &block)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
1
37
|
class ActionView::Helpers::FormBuilder
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
38
|
+
prepend Spamtrap::FormBuilderMutation
|
|
39
|
+
|
|
40
|
+
def spamtrap(parameter = 'spamtrap', options = {})
|
|
41
|
+
mutate = options.delete(:mutate)
|
|
42
|
+
nonce = options.delete(:nonce)
|
|
43
|
+
options.reverse_merge!(class: 'spamtrap')
|
|
44
|
+
|
|
45
|
+
if mutate
|
|
46
|
+
@spamtrap_salt = SecureRandom.bytes(Spamtrap::Crypto::NONCE_LEN)
|
|
47
|
+
salt_field = @template.hidden_field_tag(
|
|
48
|
+
:spamtrap_mutation_salt, @spamtrap_salt.unpack1('H*')
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
@template.text_area_tag(parameter, nil, options) +
|
|
53
|
+
(nonce ? spamtrap_nonce_fields : ''.html_safe) +
|
|
54
|
+
(salt_field || ''.html_safe)
|
|
5
55
|
end
|
|
6
56
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def spamtrap_nonce_fields
|
|
60
|
+
timestamp = Time.now.to_i
|
|
61
|
+
ip = @template.request.remote_ip
|
|
62
|
+
secret = Rails.application.secret_key_base
|
|
63
|
+
nonce = OpenSSL::HMAC.hexdigest('SHA256', secret, "#{timestamp}:#{ip}")
|
|
64
|
+
|
|
65
|
+
@template.hidden_field_tag(:spamtrap_timestamp, timestamp) +
|
|
66
|
+
@template.hidden_field_tag(:spamtrap_nonce, nonce)
|
|
67
|
+
end
|
|
17
68
|
end
|
data/lib/spamtrap/version.rb
CHANGED
data/lib/spamtrap.rb
CHANGED
data/test/controller_test.rb
CHANGED
|
@@ -1,10 +1,244 @@
|
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# Test controllers — render a body so we can distinguish a real response
|
|
4
|
+
# from a spamtrap-blocked one (which uses head 200 and has an empty body).
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
class HoneypotController < ActionController::Base
|
|
7
|
+
spamtrap :trap_field, only: :create
|
|
8
|
+
|
|
9
|
+
def create
|
|
10
|
+
render plain: 'success'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class NonceController < ActionController::Base
|
|
15
|
+
spamtrap :trap_field, nonce: true, only: :create
|
|
16
|
+
|
|
17
|
+
def create
|
|
18
|
+
render plain: 'success'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class NonceTimeoutController < ActionController::Base
|
|
23
|
+
spamtrap :trap_field, nonce: true, nonce_timeout: 60, only: :create
|
|
24
|
+
|
|
25
|
+
def create
|
|
26
|
+
render plain: 'success'
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class MutationController < ActionController::Base
|
|
31
|
+
spamtrap :trap_field, mutate: true, only: :create
|
|
32
|
+
|
|
33
|
+
def create
|
|
34
|
+
# Render the remapped comment param keys so tests can assert on them
|
|
35
|
+
render plain: params[:comment].to_unsafe_h.keys.sort.join(',')
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class NestedMutationController < ActionController::Base
|
|
40
|
+
spamtrap :trap_field, mutate: true, only: :create
|
|
41
|
+
|
|
42
|
+
def create
|
|
43
|
+
# Render top-level and nested address keys to verify salt propagation
|
|
44
|
+
comment_keys = params[:comment].to_unsafe_h.except('address').keys.sort
|
|
45
|
+
address_keys = params.dig(:comment, :address).to_unsafe_h.keys.sort rescue []
|
|
46
|
+
render plain: "#{comment_keys.join(',')};#{address_keys.join(',')}"
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
module NonceTestHelper
|
|
51
|
+
REMOTE_IP = '0.0.0.0'
|
|
52
|
+
|
|
53
|
+
def generate_nonce(timestamp, ip = REMOTE_IP)
|
|
54
|
+
secret = Rails.application.secret_key_base
|
|
55
|
+
OpenSSL::HMAC.hexdigest('SHA256', secret, "#{timestamp}:#{ip}")
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
module MutationTestHelper
|
|
60
|
+
include Spamtrap::Crypto
|
|
61
|
+
|
|
62
|
+
MUTATION_SALT = SecureRandom.bytes(Spamtrap::Crypto::NONCE_LEN)
|
|
63
|
+
MUTATION_SALT_HEX = MUTATION_SALT.unpack1('H*')
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class HoneypotControllerTest < ActionController::TestCase
|
|
67
|
+
tests HoneypotController
|
|
68
|
+
|
|
69
|
+
def test_empty_honeypot_allows_request
|
|
70
|
+
post :create, params: { trap_field: '' }
|
|
71
|
+
assert_response :ok
|
|
72
|
+
assert_equal 'success', response.body
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_filled_honeypot_silently_discards
|
|
76
|
+
post :create, params: { trap_field: 'buy cheap meds' }
|
|
77
|
+
assert_response :ok
|
|
78
|
+
assert_empty response.body
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
class NonceControllerTest < ActionController::TestCase
|
|
83
|
+
include NonceTestHelper
|
|
84
|
+
tests NonceController
|
|
85
|
+
|
|
86
|
+
def test_valid_nonce_allows_request
|
|
87
|
+
timestamp = Time.now.to_i
|
|
88
|
+
post :create, params: {
|
|
89
|
+
trap_field: '',
|
|
90
|
+
spamtrap_timestamp: timestamp,
|
|
91
|
+
spamtrap_nonce: generate_nonce(timestamp)
|
|
92
|
+
}
|
|
93
|
+
assert_response :ok
|
|
94
|
+
assert_equal 'success', response.body
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def test_tampered_nonce_is_rejected
|
|
98
|
+
timestamp = Time.now.to_i
|
|
99
|
+
post :create, params: {
|
|
100
|
+
trap_field: '',
|
|
101
|
+
spamtrap_timestamp: timestamp,
|
|
102
|
+
spamtrap_nonce: 'deadbeef'
|
|
103
|
+
}
|
|
104
|
+
assert_response :ok
|
|
105
|
+
assert_empty response.body
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def test_expired_nonce_is_rejected
|
|
109
|
+
timestamp = Time.now.to_i - 7200
|
|
110
|
+
post :create, params: {
|
|
111
|
+
trap_field: '',
|
|
112
|
+
spamtrap_timestamp: timestamp,
|
|
113
|
+
spamtrap_nonce: generate_nonce(timestamp)
|
|
114
|
+
}
|
|
115
|
+
assert_response :ok
|
|
116
|
+
assert_empty response.body
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def test_missing_nonce_fields_are_rejected
|
|
120
|
+
post :create, params: { trap_field: '' }
|
|
121
|
+
assert_response :ok
|
|
122
|
+
assert_empty response.body
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def test_filled_honeypot_takes_priority_over_valid_nonce
|
|
126
|
+
timestamp = Time.now.to_i
|
|
127
|
+
post :create, params: {
|
|
128
|
+
trap_field: 'spam',
|
|
129
|
+
spamtrap_timestamp: timestamp,
|
|
130
|
+
spamtrap_nonce: generate_nonce(timestamp)
|
|
131
|
+
}
|
|
132
|
+
assert_response :ok
|
|
133
|
+
assert_empty response.body
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
class NonceTimeoutControllerTest < ActionController::TestCase
|
|
138
|
+
include NonceTestHelper
|
|
139
|
+
tests NonceTimeoutController
|
|
140
|
+
|
|
141
|
+
def test_nonce_within_custom_timeout_passes
|
|
142
|
+
timestamp = Time.now.to_i - 30
|
|
143
|
+
post :create, params: {
|
|
144
|
+
trap_field: '',
|
|
145
|
+
spamtrap_timestamp: timestamp,
|
|
146
|
+
spamtrap_nonce: generate_nonce(timestamp)
|
|
147
|
+
}
|
|
148
|
+
assert_response :ok
|
|
149
|
+
assert_equal 'success', response.body
|
|
8
150
|
end
|
|
9
151
|
|
|
152
|
+
def test_nonce_beyond_custom_timeout_is_rejected
|
|
153
|
+
timestamp = Time.now.to_i - 120
|
|
154
|
+
post :create, params: {
|
|
155
|
+
trap_field: '',
|
|
156
|
+
spamtrap_timestamp: timestamp,
|
|
157
|
+
spamtrap_nonce: generate_nonce(timestamp)
|
|
158
|
+
}
|
|
159
|
+
assert_response :ok
|
|
160
|
+
assert_empty response.body
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
class MutationControllerTest < ActionController::TestCase
|
|
165
|
+
include MutationTestHelper
|
|
166
|
+
tests MutationController
|
|
167
|
+
|
|
168
|
+
def test_encrypted_field_names_are_remapped_to_real_names
|
|
169
|
+
body_token = spamtrap_encrypt_field('body', MUTATION_SALT)
|
|
170
|
+
email_token = spamtrap_encrypt_field('email', MUTATION_SALT)
|
|
171
|
+
|
|
172
|
+
post :create, params: {
|
|
173
|
+
trap_field: '',
|
|
174
|
+
spamtrap_mutation_salt: MUTATION_SALT_HEX,
|
|
175
|
+
comment: { body_token => 'Hello world', email_token => 'test@example.com' }
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
assert_response :ok
|
|
179
|
+
assert_equal 'body,email', response.body
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def test_unencrypted_field_names_pass_through_unchanged
|
|
183
|
+
post :create, params: {
|
|
184
|
+
trap_field: '',
|
|
185
|
+
spamtrap_mutation_salt: MUTATION_SALT_HEX,
|
|
186
|
+
comment: { body: 'Hello', email: 'test@example.com' }
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
assert_response :ok
|
|
190
|
+
assert_equal 'body,email', response.body
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def test_no_salt_leaves_encrypted_params_unmapped
|
|
194
|
+
body_token = spamtrap_encrypt_field('body', MUTATION_SALT)
|
|
195
|
+
|
|
196
|
+
post :create, params: {
|
|
197
|
+
trap_field: '',
|
|
198
|
+
comment: { body_token => 'Hello' }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
assert_response :ok
|
|
202
|
+
refute_equal 'body', response.body
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def test_wrong_salt_fails_to_decrypt
|
|
206
|
+
other_salt = SecureRandom.bytes(Spamtrap::Crypto::NONCE_LEN)
|
|
207
|
+
body_token = spamtrap_encrypt_field('body', other_salt)
|
|
208
|
+
|
|
209
|
+
post :create, params: {
|
|
210
|
+
trap_field: '',
|
|
211
|
+
spamtrap_mutation_salt: MUTATION_SALT_HEX,
|
|
212
|
+
comment: { body_token => 'Hello' }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
assert_response :ok
|
|
216
|
+
refute_equal 'body', response.body
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
class NestedMutationControllerTest < ActionController::TestCase
|
|
221
|
+
include MutationTestHelper
|
|
222
|
+
tests NestedMutationController
|
|
223
|
+
|
|
224
|
+
def test_salt_propagates_to_fields_for_child_builder
|
|
225
|
+
body_token = spamtrap_encrypt_field('body', MUTATION_SALT)
|
|
226
|
+
street_token = spamtrap_encrypt_field('street', MUTATION_SALT)
|
|
227
|
+
city_token = spamtrap_encrypt_field('city', MUTATION_SALT)
|
|
228
|
+
|
|
229
|
+
post :create, params: {
|
|
230
|
+
trap_field: '',
|
|
231
|
+
spamtrap_mutation_salt: MUTATION_SALT_HEX,
|
|
232
|
+
comment: {
|
|
233
|
+
body_token => 'Hello',
|
|
234
|
+
address: {
|
|
235
|
+
street_token => '123 Main St',
|
|
236
|
+
city_token => 'Springfield'
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
assert_response :ok
|
|
242
|
+
assert_equal 'body;city,street', response.body
|
|
243
|
+
end
|
|
10
244
|
end
|
data/test/test_helper.rb
CHANGED
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
require 'rubygems'
|
|
2
|
+
require 'rails'
|
|
2
3
|
require 'action_controller'
|
|
3
|
-
require '
|
|
4
|
+
require 'action_controller/test_case'
|
|
5
|
+
require 'minitest/autorun'
|
|
4
6
|
require 'spamtrap'
|
|
7
|
+
|
|
8
|
+
class SpamtrapTestApp < Rails::Application
|
|
9
|
+
config.secret_key_base = 'a' * 64
|
|
10
|
+
config.eager_load = false
|
|
11
|
+
config.logger = Logger.new(nil)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
Rails.application.initialize!
|
|
15
|
+
|
|
16
|
+
Rails.application.routes.draw do
|
|
17
|
+
post 'honeypot/create', to: 'honeypot#create'
|
|
18
|
+
post 'nonce/create', to: 'nonce#create'
|
|
19
|
+
post 'nonce_timeout/create', to: 'nonce_timeout#create'
|
|
20
|
+
post 'mutation/create', to: 'mutation#create'
|
|
21
|
+
post 'nested_mutation/create', to: 'nested_mutation#create'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class ActionController::TestCase
|
|
25
|
+
setup do
|
|
26
|
+
@routes = Rails.application.routes
|
|
27
|
+
end
|
|
28
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spamtrap
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cedric Howe
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: rails
|
|
@@ -16,14 +15,14 @@ dependencies:
|
|
|
16
15
|
requirements:
|
|
17
16
|
- - ">="
|
|
18
17
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0'
|
|
18
|
+
version: '7.0'
|
|
20
19
|
type: :runtime
|
|
21
20
|
prerelease: false
|
|
22
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
22
|
requirements:
|
|
24
23
|
- - ">="
|
|
25
24
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0'
|
|
25
|
+
version: '7.0'
|
|
27
26
|
description: Create bogus form fields (honeypots) that will be filled-in by spambots.
|
|
28
27
|
When submitted, the form data will be discarded while still returning a 200 response.
|
|
29
28
|
email: cedric@freezerbox.com
|
|
@@ -33,6 +32,7 @@ extra_rdoc_files: []
|
|
|
33
32
|
files:
|
|
34
33
|
- lib/spamtrap.rb
|
|
35
34
|
- lib/spamtrap/controller.rb
|
|
35
|
+
- lib/spamtrap/crypto.rb
|
|
36
36
|
- lib/spamtrap/helper.rb
|
|
37
37
|
- lib/spamtrap/version.rb
|
|
38
38
|
- test/controller_test.rb
|
|
@@ -41,7 +41,6 @@ homepage: http://github.com/cedric/spamtrap/
|
|
|
41
41
|
licenses:
|
|
42
42
|
- MIT
|
|
43
43
|
metadata: {}
|
|
44
|
-
post_install_message:
|
|
45
44
|
rdoc_options: []
|
|
46
45
|
require_paths:
|
|
47
46
|
- lib
|
|
@@ -49,16 +48,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
49
48
|
requirements:
|
|
50
49
|
- - ">="
|
|
51
50
|
- !ruby/object:Gem::Version
|
|
52
|
-
version:
|
|
51
|
+
version: 3.0.0
|
|
53
52
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
53
|
requirements:
|
|
55
54
|
- - ">="
|
|
56
55
|
- !ruby/object:Gem::Version
|
|
57
56
|
version: 1.3.6
|
|
58
57
|
requirements: []
|
|
59
|
-
|
|
60
|
-
rubygems_version: 2.4.5.3
|
|
61
|
-
signing_key:
|
|
58
|
+
rubygems_version: 4.0.6
|
|
62
59
|
specification_version: 4
|
|
63
60
|
summary: Simple spamtrap for spambots.
|
|
64
61
|
test_files:
|