spamtrap 0.3.2 → 0.3.4
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 +4 -4
- data/lib/spamtrap/helper.rb +56 -2
- data/lib/spamtrap/version.rb +1 -1
- data/test/form_builder_mutation_test.rb +142 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f1f9fa53d08c869e9952416e63376c31ed74f74584e9cf1e59f7439364c370d5
|
|
4
|
+
data.tar.gz: 4ecae5a00af343aa3c8856a3f2bcf04999bd37a0112d067d3d907e5b85b75e2a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 825936fb38cb3095ecf504c42aff353081a8352539a535beff67d85ef2423fc4e7bac328c107d48d5d0ed092ea1b508be6bb06e7490c33cceb2f230a0c89e45a
|
|
7
|
+
data.tar.gz: c35648761da54aab75b8b8d587f24d4ccfbfb03210d23ed930fbdbea2743876de9a795d19980acac9bac45b92e7bbe94621f8419440031d2e15412b6876e041a
|
data/lib/spamtrap/helper.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Spamtrap
|
|
2
4
|
module FormBuilderMutation
|
|
3
5
|
include Spamtrap::Crypto
|
|
@@ -11,8 +13,60 @@ module Spamtrap
|
|
|
11
13
|
|
|
12
14
|
MUTABLE_FIELDS.each do |m|
|
|
13
15
|
define_method(m) do |field, *args, &blk|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
if @spamtrap_salt
|
|
17
|
+
encrypted_field = spamtrap_encrypt_field(field.to_s, @spamtrap_salt)
|
|
18
|
+
|
|
19
|
+
opts =
|
|
20
|
+
if m == :check_box
|
|
21
|
+
args.first.is_a?(Hash) ? args.shift.dup : {}
|
|
22
|
+
else
|
|
23
|
+
args.last.is_a?(Hash) ? args.pop.dup : {}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
model_value = object.respond_to?(field) ? object.public_send(field) : nil
|
|
27
|
+
|
|
28
|
+
case m
|
|
29
|
+
when :check_box
|
|
30
|
+
# :checked controls the checked state; :value is the submitted value ("1" by default)
|
|
31
|
+
opts[:checked] = !!model_value unless opts.key?(:checked)
|
|
32
|
+
super(encrypted_field, opts, *args, &blk)
|
|
33
|
+
when :select, :collection_select, :grouped_collection_select
|
|
34
|
+
# :selected belongs in the inner options hash, not html_options (the last hash).
|
|
35
|
+
# After popping html_options into opts, args.last is the options hash (if present).
|
|
36
|
+
if args.last.is_a?(Hash)
|
|
37
|
+
sel_opts = args.pop.dup
|
|
38
|
+
sel_opts[:selected] = model_value unless sel_opts.key?(:selected)
|
|
39
|
+
args.push(sel_opts)
|
|
40
|
+
else
|
|
41
|
+
opts[:selected] = model_value unless opts.key?(:selected)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
super(encrypted_field, *args, opts, &blk)
|
|
45
|
+
when :label
|
|
46
|
+
# Preserve the human-readable label text using the real field name
|
|
47
|
+
# before substituting the encrypted form. Mirrors Rails' own
|
|
48
|
+
# resolution order:
|
|
49
|
+
# 1. explicit text argument — kept as-is
|
|
50
|
+
# 2. activerecord.attributes.Model.field (ActiveRecord models)
|
|
51
|
+
# 3. helpers.label.object_name.field (plain objects / form_with)
|
|
52
|
+
# 4. humanize fallback
|
|
53
|
+
unless args.first.is_a?(String)
|
|
54
|
+
human_text =
|
|
55
|
+
if object.respond_to?(:to_model)
|
|
56
|
+
object.class.human_attribute_name(field.to_s, default: field.to_s.humanize)
|
|
57
|
+
else
|
|
58
|
+
I18n.t(field, scope: [:helpers, :label, object_name], default: field.to_s.humanize)
|
|
59
|
+
end
|
|
60
|
+
args.unshift(human_text)
|
|
61
|
+
end
|
|
62
|
+
super(encrypted_field, *args, opts, &blk)
|
|
63
|
+
else
|
|
64
|
+
opts[:value] = model_value unless opts.key?(:value)
|
|
65
|
+
super(encrypted_field, *args, opts, &blk)
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
super(field, *args, &blk)
|
|
69
|
+
end
|
|
16
70
|
end
|
|
17
71
|
end
|
|
18
72
|
|
data/lib/spamtrap/version.rb
CHANGED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
|
4
|
+
require 'action_view'
|
|
5
|
+
require 'action_view/test_case'
|
|
6
|
+
|
|
7
|
+
# A simple model-like struct to back the form builder, mimicking an AR model.
|
|
8
|
+
Message = Struct.new(:name, :email, :body, :active, :country)
|
|
9
|
+
|
|
10
|
+
class FormBuilderMutationTest < ActionView::TestCase
|
|
11
|
+
include Spamtrap::Crypto
|
|
12
|
+
|
|
13
|
+
# Build a minimal form builder instance backed by a Message object.
|
|
14
|
+
def build_form_builder(object)
|
|
15
|
+
ActionView::Helpers::FormBuilder.new(:message, object, self, {})
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# --- text-like inputs ---
|
|
19
|
+
|
|
20
|
+
def test_text_field_does_not_raise_when_mutate_is_true
|
|
21
|
+
msg = Message.new('Alice', 'alice@example.com', 'Hello')
|
|
22
|
+
f = build_form_builder(msg)
|
|
23
|
+
f.spamtrap(:trap, mutate: true)
|
|
24
|
+
|
|
25
|
+
assert_nothing_raised { f.text_field(:name) }
|
|
26
|
+
assert_nothing_raised { f.email_field(:email) }
|
|
27
|
+
assert_nothing_raised { f.text_area(:body) }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_model_value_is_preserved_in_encrypted_field
|
|
31
|
+
msg = Message.new('Alice', 'alice@example.com', 'Hello')
|
|
32
|
+
f = build_form_builder(msg)
|
|
33
|
+
f.spamtrap(:trap, mutate: true)
|
|
34
|
+
|
|
35
|
+
assert_includes f.text_field(:name), 'Alice'
|
|
36
|
+
assert_includes f.email_field(:email), 'alice@example.com'
|
|
37
|
+
assert_includes f.text_area(:body), 'Hello'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_encrypted_field_name_is_used_in_html
|
|
41
|
+
msg = Message.new('Alice', 'alice@example.com', 'Hello')
|
|
42
|
+
f = build_form_builder(msg)
|
|
43
|
+
f.spamtrap(:trap, mutate: true)
|
|
44
|
+
|
|
45
|
+
html = f.text_field(:name)
|
|
46
|
+
|
|
47
|
+
# The original unencrypted name must NOT appear as the input name attribute
|
|
48
|
+
refute_match(/name="message\[name\]"/, html)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def test_nil_model_value_does_not_raise
|
|
52
|
+
msg = Message.new(nil, nil, nil)
|
|
53
|
+
f = build_form_builder(msg)
|
|
54
|
+
f.spamtrap(:trap, mutate: true)
|
|
55
|
+
|
|
56
|
+
assert_nothing_raised { f.text_field(:name) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_virtual_field_without_model_method_does_not_raise
|
|
60
|
+
msg = Message.new('Alice', 'alice@example.com', 'Hello')
|
|
61
|
+
f = build_form_builder(msg)
|
|
62
|
+
f.spamtrap(:trap, mutate: true)
|
|
63
|
+
|
|
64
|
+
# :nonexistent is not a method on Message; should render with empty value, not crash
|
|
65
|
+
assert_nothing_raised { f.text_field(:nonexistent) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def test_explicit_value_option_is_preserved
|
|
69
|
+
msg = Message.new('Alice', 'alice@example.com', 'Hello')
|
|
70
|
+
f = build_form_builder(msg)
|
|
71
|
+
f.spamtrap(:trap, mutate: true)
|
|
72
|
+
|
|
73
|
+
html = f.text_field(:name, value: 'Overridden')
|
|
74
|
+
|
|
75
|
+
assert_includes html, 'Overridden'
|
|
76
|
+
refute_includes html, 'Alice'
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# --- no mutation ---
|
|
80
|
+
|
|
81
|
+
def test_no_salt_leaves_field_names_unencrypted
|
|
82
|
+
msg = Message.new('Alice', 'alice@example.com', 'Hello')
|
|
83
|
+
f = build_form_builder(msg)
|
|
84
|
+
|
|
85
|
+
html = f.text_field(:name)
|
|
86
|
+
|
|
87
|
+
assert_match(/name="message\[name\]"/, html)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# --- check_box ---
|
|
91
|
+
|
|
92
|
+
def test_check_box_does_not_raise_when_mutate_is_true
|
|
93
|
+
msg = Message.new(nil, nil, nil, true)
|
|
94
|
+
f = build_form_builder(msg)
|
|
95
|
+
f.spamtrap(:trap, mutate: true)
|
|
96
|
+
|
|
97
|
+
assert_nothing_raised { f.check_box(:active) }
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def test_check_box_checked_when_model_value_is_true
|
|
101
|
+
msg = Message.new(nil, nil, nil, true)
|
|
102
|
+
f = build_form_builder(msg)
|
|
103
|
+
f.spamtrap(:trap, mutate: true)
|
|
104
|
+
|
|
105
|
+
assert_includes f.check_box(:active), 'checked'
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def test_check_box_unchecked_when_model_value_is_false
|
|
109
|
+
msg = Message.new(nil, nil, nil, false)
|
|
110
|
+
f = build_form_builder(msg)
|
|
111
|
+
f.spamtrap(:trap, mutate: true)
|
|
112
|
+
|
|
113
|
+
refute_includes f.check_box(:active), 'checked'
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# --- select ---
|
|
117
|
+
|
|
118
|
+
def test_select_does_not_raise_when_mutate_is_true
|
|
119
|
+
msg = Message.new(nil, nil, nil, nil, 'US')
|
|
120
|
+
f = build_form_builder(msg)
|
|
121
|
+
f.spamtrap(:trap, mutate: true)
|
|
122
|
+
|
|
123
|
+
assert_nothing_raised { f.select(:country, %w[US CA GB]) }
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def test_select_preselects_model_value
|
|
127
|
+
msg = Message.new(nil, nil, nil, nil, 'CA')
|
|
128
|
+
f = build_form_builder(msg)
|
|
129
|
+
f.spamtrap(:trap, mutate: true)
|
|
130
|
+
|
|
131
|
+
html = f.select(:country, %w[US CA GB])
|
|
132
|
+
|
|
133
|
+
assert_match(/selected.*CA|CA.*selected/, html)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
|
|
138
|
+
# ActionView::TestCase helpers expect a #request method; provide a stub.
|
|
139
|
+
def request
|
|
140
|
+
@request ||= ActionDispatch::TestRequest.create
|
|
141
|
+
end
|
|
142
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spamtrap
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Cedric Howe
|
|
@@ -36,6 +36,7 @@ files:
|
|
|
36
36
|
- lib/spamtrap/helper.rb
|
|
37
37
|
- lib/spamtrap/version.rb
|
|
38
38
|
- test/controller_test.rb
|
|
39
|
+
- test/form_builder_mutation_test.rb
|
|
39
40
|
- test/test_helper.rb
|
|
40
41
|
homepage: http://github.com/cedric/spamtrap/
|
|
41
42
|
licenses:
|
|
@@ -60,4 +61,5 @@ specification_version: 4
|
|
|
60
61
|
summary: Simple spamtrap for spambots.
|
|
61
62
|
test_files:
|
|
62
63
|
- test/controller_test.rb
|
|
64
|
+
- test/form_builder_mutation_test.rb
|
|
63
65
|
- test/test_helper.rb
|