fast_submission_protection 0.1.0 → 0.1.1
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.
- data/CHANGELOG +5 -0
- data/README.md +29 -13
- data/app/views/fast_submission_protection/{error.html → error.html.erb} +1 -1
- data/lib/fast_submission_protection/controller.rb +16 -33
- data/lib/fast_submission_protection/submission_timer.rb +19 -6
- data/lib/fast_submission_protection/version.rb +1 -1
- data/spec/controllers/{controller_spec.rb → integration_spec.rb} +12 -16
- data/spec/fast_submission_protection/filters_spec.rb +56 -0
- data/spec/fast_submission_protection/submission_timer_spec.rb +108 -0
- data/spec/spec_helper.rb +21 -1
- metadata +27 -12
data/CHANGELOG
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
= 0.1.1
|
2
|
+
* Supply an error template which includes the delay
|
3
|
+
* Adds config.action_controller.allow_fast_submission_protection (off by default in test mode)
|
4
|
+
* Stabilise the API
|
5
|
+
|
1
6
|
= 0.1.0
|
2
7
|
* Renamed to fast_submission_protection
|
3
8
|
* Raise an error to handle fast submission
|
data/README.md
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
# FastSubmissionProtection
|
2
2
|
|
3
|
-
*This is experimental and the API is currently subject to sudden and massive change!*
|
4
|
-
|
5
3
|
[](http://travis-ci.org/i2w/timed_spam_rejection)
|
6
4
|
|
7
|
-
ActionController
|
5
|
+
ActionController engine that facilitates rejecting spam based on how long the form submission took.
|
8
6
|
|
9
|
-
This
|
7
|
+
This was developed by [Ian White](http://github.com/ianwhite) and [Nicholas Rutherford](http://github.com/nruth) while working at [Distinctive Doors](http://distinctivedoors.co.uk) who have kindly agreed to release this under the MIT-LICENSE.
|
10
8
|
|
11
9
|
## Installation
|
12
10
|
|
@@ -16,15 +14,20 @@ In your Gemfile:
|
|
16
14
|
|
17
15
|
## Example Usage
|
18
16
|
|
17
|
+
Specify `protect_from_fast_submission` to protect a create action from being submitted in less than 5 seconds. The default protection is
|
18
|
+
to render a HTTP 420 page (see Rescue below).
|
19
|
+
|
19
20
|
class FeedbackController < ApplicationController
|
20
|
-
protect_from_fast_submission
|
21
|
+
protect_from_fast_submission
|
21
22
|
end
|
22
23
|
|
24
|
+
You can change the delay, start and finish actions, and the rescue behaviour
|
25
|
+
|
23
26
|
class CommentsController < ApplicationController
|
24
|
-
# protects a Comment#update from happening too quickly, and rescues with custom behaviour
|
25
27
|
protect_from_fast_submission :delay => 10, :start => [:edit, :update], :finish => [:update], :rescue => false
|
26
28
|
|
27
|
-
rescue_from FastSubmissionProtection::SubmissionTooFastError,
|
29
|
+
rescue_from FastSubmissionProtection::SubmissionTooFastError,
|
30
|
+
:with => lambda {|c| c.redirect_to :edit, :alert => 'Don't comment in anger!' }
|
28
31
|
end
|
29
32
|
|
30
33
|
See `FastSubmissionProtection::Controller#protect_from_fast_submission` for more details.
|
@@ -43,14 +46,27 @@ You can start and finish the timed submission in different controllers, just set
|
|
43
46
|
|
44
47
|
## Instance methods
|
45
48
|
|
46
|
-
|
49
|
+
Or, for the most fine-grained control, you can start and finish at any point
|
50
|
+
|
51
|
+
# within a controller instance
|
52
|
+
self.submission_timer('abused_form').start
|
53
|
+
|
54
|
+
# some point later, where controller is any controller instance
|
55
|
+
# will raise FastSubmissionProtection::SubmissionTooFastError if the above call was < 20 seconds ago
|
56
|
+
controller.submission_timer('abused_form', 20).finish
|
57
|
+
|
58
|
+
|
59
|
+
## Configuration
|
47
60
|
|
48
|
-
|
49
|
-
|
61
|
+
By default `fast_submission_protection` is off in `test` mode. You can configure it in environment files as follows:
|
62
|
+
|
63
|
+
config.action_controller.allow_fast_submission_protection = false
|
50
64
|
|
51
|
-
|
65
|
+
You can also configure it on a per controller basis:
|
52
66
|
|
53
|
-
|
67
|
+
class MyController < ApplicationController
|
68
|
+
self.allow_fast_submission_protection = true
|
69
|
+
end
|
54
70
|
|
55
71
|
## Rescue
|
56
72
|
|
@@ -59,7 +75,7 @@ This is included by default when you specify `protect_from_fast_submission`.
|
|
59
75
|
|
60
76
|
The default error page resides in 'views/fast_submission_protection/error'. Simply add this page to your views directory to use a custom page.
|
61
77
|
|
62
|
-
Another option is to do something like put a message in the flash, and re-render the new page. Simply rescue_from FastSubmissionProtection::
|
78
|
+
Another option is to do something like put a message in the flash, and re-render the new page. Simply rescue_from FastSubmissionProtection::SubmissionTooFastError.
|
63
79
|
|
64
80
|
## Development
|
65
81
|
|
@@ -19,7 +19,7 @@
|
|
19
19
|
<body>
|
20
20
|
<div class="dialog">
|
21
21
|
<h1>The request you made was too fast.</h1>
|
22
|
-
<p>Click your browser's back button to return to the previous page, wait
|
22
|
+
<p>Click your browser's back button to return to the previous page, wait <%= exception.delay %> seconds, and try the request again.</p>
|
23
23
|
<p>This is an anti-spam measure.</p>
|
24
24
|
</div>
|
25
25
|
</body>
|
@@ -26,10 +26,10 @@ module FastSubmissionProtection
|
|
26
26
|
# before_filter FastSubmissionProtection::StartFilter.new('abused_form_post'), :only => [:new]
|
27
27
|
#
|
28
28
|
# # At the instance level, wherever you want, perhaps in an action
|
29
|
-
#
|
29
|
+
# submission_timer('often_abused_form_post').start # to start
|
30
30
|
#
|
31
31
|
# # later, somewhere else
|
32
|
-
#
|
32
|
+
# submission_timer('often_abused_form_post').finish # to finsih, raises SubmissionTooFastError if too fast
|
33
33
|
def protect_from_fast_submission options = {}
|
34
34
|
delay = options[:delay]
|
35
35
|
start = options[:start] || [:new, :create]
|
@@ -44,35 +44,24 @@ module FastSubmissionProtection
|
|
44
44
|
end
|
45
45
|
|
46
46
|
included do
|
47
|
-
hide_action :
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
def finish_timed_submission name, delay = nil
|
55
|
-
if protect_from_fast_submission?
|
56
|
-
timer = submission_timer(name, delay)
|
57
|
-
if timer.too_fast?
|
58
|
-
logger.warn "WARNING: timed submission too fast" if logger
|
59
|
-
timer.restart
|
60
|
-
raise SubmissionTooFastError.new(name, delay)
|
61
|
-
else
|
62
|
-
timer.clear
|
63
|
-
end
|
47
|
+
hide_action :submission_timer, :protect_from_fast_submission?
|
48
|
+
|
49
|
+
# Controls whether fast submission protection is turned on or not. Turned off by default only in test mode.
|
50
|
+
config_accessor :allow_fast_submission_protection
|
51
|
+
if allow_fast_submission_protection.nil?
|
52
|
+
self.allow_fast_submission_protection = (Rails.env != 'test')
|
64
53
|
end
|
65
54
|
end
|
66
55
|
|
67
|
-
protected
|
68
56
|
def submission_timer name, delay = nil
|
69
57
|
SubmissionTimer.new timed_submission_storage, name, delay
|
70
58
|
end
|
71
59
|
|
72
60
|
def protect_from_fast_submission?
|
73
|
-
request.post? || request.put?
|
61
|
+
allow_fast_submission_protection && (request.post? || request.put?)
|
74
62
|
end
|
75
63
|
|
64
|
+
protected
|
76
65
|
def timed_submission_storage
|
77
66
|
session[:_fsp] ||= {}
|
78
67
|
end
|
@@ -80,22 +69,16 @@ module FastSubmissionProtection
|
|
80
69
|
|
81
70
|
class StartFilter < Struct.new(:name)
|
82
71
|
def filter controller
|
83
|
-
controller.
|
72
|
+
controller.submission_timer(name).start
|
84
73
|
end
|
85
74
|
end
|
86
75
|
|
87
76
|
class FinishFilter < Struct.new(:name, :delay)
|
88
77
|
def filter controller
|
89
|
-
controller.
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
class FastSubmissionProtection::SubmissionTooFastError < RuntimeError
|
94
|
-
def initialize name, delay
|
95
|
-
@name, @delay = name, delay
|
78
|
+
if controller.protect_from_fast_submission?
|
79
|
+
controller.submission_timer(name, delay).finish
|
80
|
+
end
|
96
81
|
end
|
97
|
-
|
98
|
-
attr_reader :name, :delay
|
99
82
|
end
|
100
83
|
|
101
84
|
module Rescue
|
@@ -106,8 +89,8 @@ module FastSubmissionProtection
|
|
106
89
|
end
|
107
90
|
|
108
91
|
protected
|
109
|
-
def render_fast_submission_protection_error
|
110
|
-
render :template => 'fast_submission_protection/error', :layout => false, :status => 420
|
92
|
+
def render_fast_submission_protection_error exception
|
93
|
+
render :template => 'fast_submission_protection/error', :locals => {:exception => exception}, :layout => false, :status => 420
|
111
94
|
end
|
112
95
|
end
|
113
96
|
end
|
@@ -1,4 +1,11 @@
|
|
1
1
|
module FastSubmissionProtection
|
2
|
+
class SubmissionTooFastError < RuntimeError
|
3
|
+
attr_reader :name, :delay
|
4
|
+
def initialize name, delay
|
5
|
+
@name, @delay = name, delay
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
2
9
|
class SubmissionTimer
|
3
10
|
class_attribute :delay, :clock
|
4
11
|
self.delay = 5
|
@@ -9,22 +16,28 @@ module FastSubmissionProtection
|
|
9
16
|
@delay = delay || self.class.delay
|
10
17
|
@clock = clock || self.class.clock
|
11
18
|
end
|
12
|
-
|
13
|
-
def too_fast?
|
14
|
-
started = @storage[@key]
|
15
|
-
!started || (started + @delay > @clock.now)
|
16
|
-
end
|
17
19
|
|
18
20
|
def start
|
19
21
|
@storage[@key] ||= @clock.now
|
20
22
|
end
|
21
23
|
|
22
24
|
def restart
|
23
|
-
|
25
|
+
clear
|
26
|
+
start
|
24
27
|
end
|
25
28
|
|
26
29
|
def clear
|
27
30
|
@storage.delete @key
|
28
31
|
end
|
32
|
+
|
33
|
+
def finish
|
34
|
+
started = @storage[@key]
|
35
|
+
if (started && (started + @delay <= @clock.now))
|
36
|
+
clear
|
37
|
+
else
|
38
|
+
restart
|
39
|
+
raise SubmissionTooFastError.new(@key, @delay)
|
40
|
+
end
|
41
|
+
end
|
29
42
|
end
|
30
43
|
end
|
@@ -1,18 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
module FastSubmissionProtection
|
4
|
-
class Application < Rails::Application
|
5
|
-
config.i18n.default_locale = :en
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
class ApplicationController < ActionController::Base
|
10
|
-
include Rails.application.routes.url_helpers
|
11
|
-
end
|
12
|
-
|
13
3
|
describe 'A controller with protect_from_fast_submission' do
|
14
4
|
controller do
|
15
|
-
|
5
|
+
self.allow_fast_submission_protection = true # test mode turns this off be default
|
6
|
+
|
7
|
+
protect_from_fast_submission :name => 'a_submission'
|
16
8
|
|
17
9
|
def new
|
18
10
|
render_new
|
@@ -60,16 +52,20 @@ describe 'A controller with protect_from_fast_submission' do
|
|
60
52
|
end
|
61
53
|
|
62
54
|
shared_examples_for 'a spammy form posting' do
|
63
|
-
it
|
55
|
+
it 'should render the fast_submission_protection error page' do
|
56
|
+
subject
|
57
|
+
controller.should render_template('fast_submission_protection/error')
|
58
|
+
end
|
64
59
|
|
65
|
-
it 'should not
|
66
|
-
controller.should_not_receive
|
60
|
+
it 'should not have created' do
|
61
|
+
controller.should_not_receive :created
|
62
|
+
subject
|
67
63
|
end
|
68
64
|
end
|
69
65
|
|
70
66
|
shared_examples_for 'a non spammy form posting' do
|
71
|
-
it 'should
|
72
|
-
controller.should_receive
|
67
|
+
it 'should have created successfully' do
|
68
|
+
controller.should_receive :created
|
73
69
|
subject
|
74
70
|
end
|
75
71
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FastSubmissionProtection::StartFilter do
|
4
|
+
describe '.new <submission_name>' do
|
5
|
+
subject { described_class.new('name') }
|
6
|
+
|
7
|
+
it "is equal to another StartFilter with the same name (for filter chain manipulation purposes)" do
|
8
|
+
subject.should_not == described_class.new('foo')
|
9
|
+
subject.should == described_class.new('name')
|
10
|
+
end
|
11
|
+
|
12
|
+
it '#filter <controller> starts a submission_timer for <name>' do
|
13
|
+
controller, timer = double, double
|
14
|
+
|
15
|
+
controller.should_receive(:submission_timer).with('name').and_return(timer)
|
16
|
+
timer.should_receive(:start)
|
17
|
+
subject.filter controller
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe FastSubmissionProtection::FinishFilter do
|
23
|
+
describe '.new <submission_name>, <delay>' do
|
24
|
+
subject { filter }
|
25
|
+
|
26
|
+
let(:filter) { described_class.new('name', 20) }
|
27
|
+
|
28
|
+
it "is equal to another FinishFilter with the same name and delay (for filter chain manipulation purposes)" do
|
29
|
+
subject.should_not == described_class.new('foo', 20)
|
30
|
+
subject.should_not == described_class.new('name', 15)
|
31
|
+
subject.should == described_class.new('name', 20)
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#filter <controller>' do
|
35
|
+
subject { filter.filter controller }
|
36
|
+
|
37
|
+
context 'when controller doesn\'t protect_from_fast_submission' do
|
38
|
+
let(:controller) { double :protect_from_fast_submission? => false }
|
39
|
+
|
40
|
+
it 'doesn\'t do anything' do
|
41
|
+
subject
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when controller protect_from_fast_submission?' do
|
46
|
+
let(:controller) { double :protect_from_fast_submission? => true }
|
47
|
+
|
48
|
+
it 'finishes a submission_timer for <name>, <delay>' do
|
49
|
+
controller.should_receive(:submission_timer).with('name', 20).and_return(timer = double)
|
50
|
+
timer.should_receive(:finish)
|
51
|
+
subject
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe FastSubmissionProtection::SubmissionTimer do
|
4
|
+
describe '.new <storage>, <key>, <delay>, <clock>' do
|
5
|
+
subject { timer }
|
6
|
+
|
7
|
+
let(:timer) { FastSubmissionProtection::SubmissionTimer.new storage, key, delay, clock }
|
8
|
+
let(:storage) { Hash.new }
|
9
|
+
let(:key) { 'the_key' }
|
10
|
+
let(:delay) { 10 }
|
11
|
+
let(:clock) { double :now => now }
|
12
|
+
let(:now) { Time.now }
|
13
|
+
|
14
|
+
describe '#start' do
|
15
|
+
subject { timer.start }
|
16
|
+
|
17
|
+
it 'starts the timer (stores the clock\'s current time (#now) for the key)' do
|
18
|
+
subject
|
19
|
+
storage[key].should == now
|
20
|
+
end
|
21
|
+
|
22
|
+
context 'when the timer is started' do
|
23
|
+
before { timer.start }
|
24
|
+
|
25
|
+
it "doesn\'t do anything (it doesn't store a later time)" do
|
26
|
+
earlier = now
|
27
|
+
clock.stub(:now).and_return now + 1.hour
|
28
|
+
subject
|
29
|
+
storage[key].should == earlier
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#clear' do
|
35
|
+
it 'clears the timer (clears any time stored for the key)' do
|
36
|
+
timer.start
|
37
|
+
timer.clear
|
38
|
+
storage.should_not have_key(key)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#restart' do
|
43
|
+
it 'clears and starts the timer (clears any storage and stores the clock\'s current time for the key)' do
|
44
|
+
timer.start
|
45
|
+
clock.stub(:now).and_return(later = now + 1.hour)
|
46
|
+
timer.restart
|
47
|
+
storage[key].should == later
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#finish' do
|
52
|
+
subject { timer.finish }
|
53
|
+
|
54
|
+
context 'when the timer isn\'t started' do
|
55
|
+
it { expect{ subject }.to raise_error(FastSubmissionProtection::SubmissionTooFastError) }
|
56
|
+
|
57
|
+
it 'should start the timer' do
|
58
|
+
subject rescue nil
|
59
|
+
storage[key].should == now
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when the timer is started' do
|
64
|
+
before { timer.start }
|
65
|
+
|
66
|
+
context 'and not enough time has passed' do
|
67
|
+
before { clock.stub(:now).and_return now + delay - 1}
|
68
|
+
|
69
|
+
it { expect{ subject }.to raise_error(FastSubmissionProtection::SubmissionTooFastError) }
|
70
|
+
|
71
|
+
it 'should restart the timer' do
|
72
|
+
subject rescue nil
|
73
|
+
storage[key].should == now + delay - 1
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'and enough time has passed' do
|
78
|
+
before { clock.stub(:now).and_return now + delay }
|
79
|
+
|
80
|
+
it { expect{ subject }.to_not raise_error }
|
81
|
+
|
82
|
+
it 'should clear the storage' do
|
83
|
+
subject
|
84
|
+
storage.should_not have_key(key)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'when <delay> is nil (default)' do
|
91
|
+
let(:delay) { nil }
|
92
|
+
|
93
|
+
it 'uses the class self.delay' do
|
94
|
+
FastSubmissionProtection::SubmissionTimer.should_receive(:delay)
|
95
|
+
subject
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'when <clock> is nil (default)' do
|
100
|
+
let(:clock) { nil }
|
101
|
+
|
102
|
+
it 'uses the class self.clock' do
|
103
|
+
FastSubmissionProtection::SubmissionTimer.should_receive(:clock)
|
104
|
+
subject
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -5,6 +5,26 @@ if ENV['SIMPLECOV']
|
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
8
|
+
ENV['RAILS_ENV'] = 'test'
|
9
|
+
|
8
10
|
require 'rails/all'
|
11
|
+
require 'rspec'
|
9
12
|
require 'rspec/rails'
|
10
|
-
|
13
|
+
require_relative '../lib/fast_submission_protection'
|
14
|
+
|
15
|
+
class Rails::Application::Configuration
|
16
|
+
def database_configuration
|
17
|
+
{'test' => {'adapter' => 'sqlite3', 'database' => ":memory:"}}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module FastSubmissionProtection
|
22
|
+
class Application < Rails::Application
|
23
|
+
config.active_support.deprecation = :stderr
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class ApplicationController < ActionController::Base
|
28
|
+
end
|
29
|
+
|
30
|
+
FastSubmissionProtection::Application.initialize!
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fast_submission_protection
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-04-
|
13
|
+
date: 2012-04-04 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
17
|
-
requirement: &
|
17
|
+
requirement: &70119054331820 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '3'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70119054331820
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: rspec-rails
|
28
|
-
requirement: &
|
28
|
+
requirement: &70119054331160 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,16 +33,27 @@ dependencies:
|
|
33
33
|
version: '2'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70119054331160
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: sqlite3
|
39
|
+
requirement: &70119054330560 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *70119054330560
|
37
48
|
description: Reject form submissions based on the time taken to submit them. Version
|
38
|
-
0.1.
|
49
|
+
0.1.1
|
39
50
|
email:
|
40
51
|
- ian@i2wdev.com
|
41
52
|
executables: []
|
42
53
|
extensions: []
|
43
54
|
extra_rdoc_files: []
|
44
55
|
files:
|
45
|
-
- app/views/fast_submission_protection/error.html
|
56
|
+
- app/views/fast_submission_protection/error.html.erb
|
46
57
|
- lib/fast_submission_protection/controller.rb
|
47
58
|
- lib/fast_submission_protection/engine.rb
|
48
59
|
- lib/fast_submission_protection/submission_timer.rb
|
@@ -52,7 +63,9 @@ files:
|
|
52
63
|
- Rakefile
|
53
64
|
- README.md
|
54
65
|
- CHANGELOG
|
55
|
-
- spec/controllers/
|
66
|
+
- spec/controllers/integration_spec.rb
|
67
|
+
- spec/fast_submission_protection/filters_spec.rb
|
68
|
+
- spec/fast_submission_protection/submission_timer_spec.rb
|
56
69
|
- spec/spec_helper.rb
|
57
70
|
homepage: http://github.com/i2w/timed_spam_rejection
|
58
71
|
licenses: []
|
@@ -68,7 +81,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
81
|
version: '0'
|
69
82
|
segments:
|
70
83
|
- 0
|
71
|
-
hash:
|
84
|
+
hash: -2459182796522294369
|
72
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
86
|
none: false
|
74
87
|
requirements:
|
@@ -77,7 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
77
90
|
version: '0'
|
78
91
|
segments:
|
79
92
|
- 0
|
80
|
-
hash:
|
93
|
+
hash: -2459182796522294369
|
81
94
|
requirements: []
|
82
95
|
rubyforge_project:
|
83
96
|
rubygems_version: 1.8.10
|
@@ -85,5 +98,7 @@ signing_key:
|
|
85
98
|
specification_version: 3
|
86
99
|
summary: Reject form submissions based on the time taken to submit them
|
87
100
|
test_files:
|
88
|
-
- spec/controllers/
|
101
|
+
- spec/controllers/integration_spec.rb
|
102
|
+
- spec/fast_submission_protection/filters_spec.rb
|
103
|
+
- spec/fast_submission_protection/submission_timer_spec.rb
|
89
104
|
- spec/spec_helper.rb
|